Class: Flare::UploadUrlPool

Inherits:
Object
  • Object
show all
Defined in:
lib/flare/upload_url_pool.rb

Overview

Thread-safe pool of presigned R2 PUT URLs the RuleManager fills from the /api/rules response. TraceExporter checks one out before each upload; if the pool is empty (no active rules, no fresh URLs) it returns nil and the exporter gives up on that batch – caller decides what to do.

Each entry is a Hash: { upload_id:, key:, put_url:, expires_at: }. expires_at is a Time; entries past their expiry are skipped on checkout.

Fork-safe: after_fork clears the pool so child processes don’t reuse parent URLs (each child polls its own copy from /api/rules anyway).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeUploadUrlPool

Returns a new instance of UploadUrlPool.



20
21
22
23
24
25
# File 'lib/flare/upload_url_pool.rb', line 20

def initialize
  @entries_ref   = Concurrent::AtomicReference.new([].freeze)
  @checkouts     = Concurrent::AtomicFixnum.new(0)
  @empty_count   = Concurrent::AtomicFixnum.new(0)
  @expired_count = Concurrent::AtomicFixnum.new(0)
end

Instance Attribute Details

#checkoutsObject (readonly)

Returns the value of attribute checkouts.



18
19
20
# File 'lib/flare/upload_url_pool.rb', line 18

def checkouts
  @checkouts
end

#empty_countObject (readonly)

Returns the value of attribute empty_count.



18
19
20
# File 'lib/flare/upload_url_pool.rb', line 18

def empty_count
  @empty_count
end

#expired_countObject (readonly)

Returns the value of attribute expired_count.



18
19
20
# File 'lib/flare/upload_url_pool.rb', line 18

def expired_count
  @expired_count
end

Instance Method Details

#after_forkObject

Call from Flare.after_fork. Parent’s URLs aren’t usable from the child’s point of view (each child should get its own from a fresh /api/rules poll), so just drop them.



82
83
84
# File 'lib/flare/upload_url_pool.rb', line 82

def after_fork
  clear
end

#checkoutObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/flare/upload_url_pool.rb', line 32

def checkout
  now = Time.now
  loop do
    current = @entries_ref.get
    if current.empty?
      @empty_count.increment
      return nil
    end

    candidate, *rest = current
    next_state = rest.freeze
    next unless @entries_ref.compare_and_set(current, next_state)

    if expired?(candidate, now)
      @expired_count.increment
      next # try the next one
    end

    @checkouts.increment
    return candidate
  end
end

#clearObject



63
64
65
# File 'lib/flare/upload_url_pool.rb', line 63

def clear
  @entries_ref.set([].freeze)
end

#empty?Boolean

Returns:

  • (Boolean)


59
60
61
# File 'lib/flare/upload_url_pool.rb', line 59

def empty?
  size.zero?
end

#replace(entries) ⇒ Object



27
28
29
30
# File 'lib/flare/upload_url_pool.rb', line 27

def replace(entries)
  normalized = (entries || []).filter_map { |raw| normalize(raw) }
  @entries_ref.set(normalized.freeze)
end

#sizeObject



55
56
57
# File 'lib/flare/upload_url_pool.rb', line 55

def size
  @entries_ref.get.length
end

#sweepObject

Drop URLs that have already passed their expires_at. Cheap; safe to call from RuleManager’s scheduler in between polls.



69
70
71
72
73
74
75
76
77
# File 'lib/flare/upload_url_pool.rb', line 69

def sweep
  now = Time.now
  current = @entries_ref.get
  live    = current.reject { |e| expired?(e, now) }
  return 0 if live.length == current.length

  @entries_ref.set(live.freeze)
  current.length - live.length
end