Class: TIMEx::Composers::Adaptive::InMemoryStore

Inherits:
Object
  • Object
show all
Defined in:
lib/timex/composers/adaptive.rb

Overview

Note:

#estimate_ms is intentionally lock-free; it may briefly return a stale value while #record runs on another thread.

O(1) streaming quantile estimator (P² algorithm, Jain & Chlamtac 1985).

#record updates markers; #estimate_ms reads the last published estimate without locking. Markers reset every window samples.

Constant Summary collapse

P_DEFAULT =
0.99

Instance Method Summary collapse

Constructor Details

#initialize(window: 200, alpha: 0.2, p: P_DEFAULT) ⇒ InMemoryStore

Returns a new instance of InMemoryStore.

Parameters:

  • window (Integer) (defaults to: 200)

    samples before marker reset

  • alpha (Float) (defaults to: 0.2)

    EWMA smoothing factor for the safety margin

  • p (Float) (defaults to: P_DEFAULT)

    target quantile (default ~p99)



26
27
28
29
30
31
32
33
34
35
# File 'lib/timex/composers/adaptive.rb', line 26

def initialize(window: 200, alpha: 0.2, p: P_DEFAULT)
  @window = window
  @alpha = alpha
  @p = p
  @ewma = nil
  @count = 0
  @mutex = Mutex.new
  @last_estimate_ms = nil
  reset_markers
end

Instance Method Details

#estimate_msFloat?

Lock-free read: #record publishes a fresh estimate at the end of every call under the mutex. Readers don't need to synchronize.

Returns:

  • (Float, nil)

    last estimated budget in ms, or nil when empty



65
66
67
# File 'lib/timex/composers/adaptive.rb', line 65

def estimate_ms
  @last_estimate_ms
end

#record(ms) ⇒ self

Records a latency sample in milliseconds and refreshes the published estimate.

Parameters:

  • ms (Numeric)

    observed latency in ms

Returns:

  • (self)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/timex/composers/adaptive.rb', line 41

def record(ms)
  @mutex.synchronize do
    reset_markers if @count >= @window
    @count += 1
    @ewma  = @ewma.nil? ? ms : (@alpha * ms) + ((1 - @alpha) * @ewma)

    if @q.nil?
      @initial << ms
      promote_to_psquare if @initial.size == 5
    else
      psquare_step(ms)
    end
    # Publish the post-record estimate for lock-free reads. Single ivar
    # write is atomic in MRI; readers see either the previous or new
    # value, both of which are valid.
    @last_estimate_ms = compute_estimate
  end
  self
end