Class: Bulldogger::Probe::Bucket

Inherits:
Object
  • Object
show all
Defined in:
lib/bulldogger/probe/bucket.rb

Overview

One params/returns aggregate: a running tally of observed classes and nil-ness (updated on every call), plus a bounded, fully-serialized sample of the first max_samples values.

Tally and sample are deliberately split: contract-verbs.md measured full serialization at ~24us/event, which would blow the probe overhead budget on a call-dense target if it ran on every call. Class name and nil? never touch #inspect, so the tally stays cheap regardless of how many calls a session sees.

Instance Method Summary collapse

Constructor Details

#initialize(formatter:, max_samples:, redacted_name: false) ⇒ Bucket

Returns a new instance of Bucket.



15
16
17
18
19
20
21
22
23
# File 'lib/bulldogger/probe/bucket.rb', line 15

def initialize(formatter:, max_samples:, redacted_name: false)
  @formatter = formatter
  @max_samples = max_samples
  @redacted_name = redacted_name
  @classes = Hash.new(0)
  @nil_count = 0
  @samples = []
  @count = 0
end

Instance Method Details

#merge!(other) ⇒ Object

Folds another Bucket's tally into this one. This is the finish-time merge step for thread-local aggregation: each thread records into its own Bucket with no lock, and totals are combined once, in one place, instead of every call taking a Mutex to update a single shared Bucket.

Samples are re-capped at @max_samples here too: each thread-local Bucket already capped its own samples independently, so without this cap a target hit by N threads could publish up to N * max_samples samples, silently widening the documented limit just because more than one thread happened to record some of the calls.



54
55
56
57
58
59
60
61
62
63
# File 'lib/bulldogger/probe/bucket.rb', line 54

def merge!(other)
  @count += other.count
  other.classes.each { |klass, n| @classes[klass] += n }
  @nil_count += other.nil_count
  other.samples.each do |sample|
    break if @samples.size >= @max_samples

    @samples << sample
  end
end

#record(value) ⇒ Object



25
26
27
28
29
30
# File 'lib/bulldogger/probe/bucket.rb', line 25

def record(value)
  @count += 1
  @classes[safe_class_name(value)] += 1
  @nil_count += 1 if value.nil?
  @samples << sample_for(value) if @samples.size < @max_samples
end

#to_hObject



32
33
34
35
36
37
38
39
40
# File 'lib/bulldogger/probe/bucket.rb', line 32

def to_h
  h = { "classes" => @classes, "nil_count" => @nil_count, "samples" => @samples }
  omitted = @count - @samples.size
  # Present only when something was actually cut, so a reader
  # can trust its absence -- the same rule contract.md applies
  # to frames_omitted/locals_omitted.
  h["samples_omitted"] = omitted if omitted.positive?
  h
end