Class: Kameleoon::Managers::Tracking::ConcurrentVisitorTrackingRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/kameleoon/managers/tracking/visitor_tracking_registry.rb

Overview

Visitor codes waiting for a tracking request, in a Hash used as an insertion-ordered set.

Lock-free on the hot path (MRI): add is a single Hash#[]=, which the GVL makes atomic (String keys hash in C, so no Ruby code runs inside it), and request threads never block on the registry. The Hash is never swapped out; extract removes with Hash#shift (O(1), oldest first) under a lock that only serializes extractors, so a concurrently added code is either returned now or kept for the next extraction, never lost. Nothing here iterates the live Hash: an add during iteration would raise in the request thread. Other engines run threads in parallel and take the lock on add as well: JRuby's Hash raises ConcurrencyError on unsynchronized concurrent writes; TruffleRuby's shared collections are thread-safe, but the lock-free path has not been measured there.

Constant Summary collapse

LIMITED_EXTRACTION_THRESHOLD_COEFFICIENT =
2
REMOVAL_FACTOR =
0.8
UNLIMITED_EXTRACTION =
2**62
LOCK_FREE_ADD =
(RUBY_ENGINE == 'ruby')

Instance Method Summary collapse

Constructor Details

#initialize(visitor_manager, storage_limit = 1_000_000, extraction_limit = 20_000) ⇒ ConcurrentVisitorTrackingRegistry

Returns a new instance of ConcurrentVisitorTrackingRegistry.



22
23
24
25
26
27
28
# File 'lib/kameleoon/managers/tracking/visitor_tracking_registry.rb', line 22

def initialize(visitor_manager, storage_limit = 1_000_000, extraction_limit = 20_000)
  @visitor_manager = visitor_manager
  @storage_limit = storage_limit
  @extraction_limit = extraction_limit
  @visitors = {}
  @extract_mutex = Mutex.new
end

Instance Method Details

#add(visitor_code) ⇒ Object



31
32
33
# File 'lib/kameleoon/managers/tracking/visitor_tracking_registry.rb', line 31

def add(visitor_code)
  @visitors[visitor_code] = true
end

#add_all(visitor_codes) ⇒ Object



35
36
37
38
# File 'lib/kameleoon/managers/tracking/visitor_tracking_registry.rb', line 35

def add_all(visitor_codes)
  visitor_codes.each { |vc| @visitors[vc] = true }
  erase_to_storage_limit if @visitors.size > @storage_limit
end

#extract(limit = UNLIMITED_EXTRACTION) ⇒ Object

Removes and returns visitor codes, oldest first: all of them while the registry holds no more than limit codes and fewer than twice the extraction limit, otherwise min(limit, extraction_limit).



52
53
54
# File 'lib/kameleoon/managers/tracking/visitor_tracking_registry.rb', line 52

def extract(limit = UNLIMITED_EXTRACTION)
  @extract_mutex.synchronize { shift_visitors(extraction_count(@visitors.size, limit)) }
end

#visitorsObject

Test purpose only



57
58
59
# File 'lib/kameleoon/managers/tracking/visitor_tracking_registry.rb', line 57

def visitors
  @visitors.keys
end