Class: Kameleoon::ClientReadiness

Inherits:
Object
  • Object
show all
Defined in:
lib/kameleoon/client_readiness.rb

Overview

ClientReadiness tracks whether the SDK has successfully loaded its configuration and exposes that state to KameleoonClient#wait_init and KameleoonClient#is_ready?:

  • a successful fetch settles the state successfully;
  • a failed fetch settles it with an Exception::Initialization error.

Readiness is monotonic: once the SDK is ready it stays ready, and a later failed fetch can never revert it. A failure reported before the first success does not prevent a subsequent successful retry from marking the SDK ready.

It is created on the thread that builds the client but settled from the background thread(s) performing configuration fetches, so it relies on Concurrent::Event (which allows cross-thread release).

Instance Method Summary collapse

Constructor Details

#initialize(site_code, environment) ⇒ ClientReadiness

Returns a new instance of ClientReadiness.



47
48
49
50
51
52
53
# File 'lib/kameleoon/client_readiness.rb', line 47

def initialize(site_code, environment)
  @mutex = Mutex.new
  @state = State.new
  @ready = false
  @site_code = site_code
  @environment = environment
end

Instance Method Details

#mark_not_ready(cause) ⇒ Object

Reports a failed fetch with an Exception::Initialization error, but only while the SDK is not yet ready. A failure never reverts an already-ready client and is reported at most once, so the first reported cause is the one waiters observe.

Parameters:

  • cause (StandardError, nil)

    the failure which prevented the SDK from loading its configuration.



79
80
81
82
83
84
85
86
# File 'lib/kameleoon/client_readiness.rb', line 79

def mark_not_ready(cause)
  @mutex.synchronize do
    unless @state.settled?
      @state.error = Exception::Initialization.new(@site_code, @environment, cause)
      @state.settle
    end # else: already ready, or the failure was already reported
  end
end

#mark_readyObject

Marks the SDK ready and releases all waiters.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/kameleoon/client_readiness.rb', line 56

def mark_ready
  @mutex.synchronize do
    @ready = true
    if @state.settled?
      unless @state.error.nil?
        # Recovery after a failure: the failed state is left settled for its waiters,
        # and a fresh, ready state is published for the new ones.
        state = State.new
        state.settle
        @state = state
      end
    else
      @state.settle
    end
  end
end

#ready?Boolean

This is the per-request hot path, so it is lock-free: @ready is only ever flipped false -> true (readiness is monotonic), making a plain ivar read safe. At worst a reader briefly observes false while mark_ready is completing, which is indistinguishable from calling a moment earlier.

Returns:

  • (Boolean)

    true if the SDK has been successfully initialized, false otherwise (including while the initialization is still pending or has failed). It never blocks.



96
97
98
# File 'lib/kameleoon/client_readiness.rb', line 96

def ready?
  @ready
end

#wait_with_timeout(timeout_second) ⇒ Kameleoon::Exception::Initialization?

Blocks until the readiness state is settled, but no longer than timeout_second. Returns the result of the configuration fetch: nil once the SDK is ready, or the Exception::Initialization error the fetch failure was reported with (fail-fast - it does not wait for background retries). If no fetch result is available within the timeout, returns an Exception::Initialization error caused by the expired timeout. A non-positive timeout expires immediately unless a result is already available. An expired timeout does not settle the readiness state: once the SDK becomes ready, a subsequent call returns nil.

Parameters:

  • timeout_second (Numeric)

    maximum number of seconds to wait.

Returns:



111
112
113
114
115
116
117
118
119
# File 'lib/kameleoon/client_readiness.rb', line 111

def wait_with_timeout(timeout_second)
  state = current_state
  return state.error if state.settled?
  return timeout_failure(timeout_second) if timeout_second <= 0

  return state.error if state.wait(timeout_second)

  timeout_failure(timeout_second)
end