Class: OpenReceive::Rates::CachedPriceFeed
- Inherits:
-
Object
- Object
- OpenReceive::Rates::CachedPriceFeed
- Defined in:
- lib/openreceive/rates.rb
Overview
Serves BTC fiat rates from a disposable process-local cache, refreshing from the primary feed first and the fallback second. Port of the JS CachedPriceFeed state machine: fresh entries are served for cache_seconds; a refresh failure fails closed for cache_seconds; a concurrent in-flight refresh serves the stale entry only while it is younger than the invoice quote TTL. Satisfies the openreceive-server price_provider contract via btc_fiat_price(currency).
Instance Attribute Summary collapse
-
#source ⇒ Object
readonly
Representative source for the plain source reader; the true origin is reported per-call by btc_fiat_rates_with_source.
Instance Method Summary collapse
-
#btc_fiat_price(currency) ⇒ Object
The openreceive-server price_provider contract: one decimal price string for one uppercase ISO 4217 currency.
- #btc_fiat_rates(currencies) ⇒ Object
- #btc_fiat_rates_with_source(currencies) ⇒ Object
-
#health_check(currencies = nil) ⇒ Object
Forces a live refresh, ignoring the cache, for explicit operational probes.
-
#initialize(currencies:, primary:, fallback:, cache_seconds: nil, clock: nil) ⇒ CachedPriceFeed
constructor
A new instance of CachedPriceFeed.
Constructor Details
#initialize(currencies:, primary:, fallback:, cache_seconds: nil, clock: nil) ⇒ CachedPriceFeed
Returns a new instance of CachedPriceFeed.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/openreceive/rates.rb', line 303 def initialize(currencies:, primary:, fallback:, cache_seconds: nil, clock: nil) raise ArgumentError, "CachedPriceFeed requires at least one currency" if currencies.empty? cache_seconds ||= PRICE_FEED_CACHE_SECONDS unless cache_seconds.is_a?(Integer) && cache_seconds.positive? raise ArgumentError, "CachedPriceFeed cache_seconds must be a positive integer" end # A cache window wider than the quote TTL would let a read be reported # as fresh that is already too old to price an invoice. if cache_seconds > INVOICE_QUOTE_TTL_SECONDS raise ArgumentError, "CachedPriceFeed cache_seconds must not exceed the #{INVOICE_QUOTE_TTL_SECONDS}s invoice quote TTL" end @currencies = currencies.map(&:to_s).freeze @primary = primary @fallback = fallback @cache_seconds = cache_seconds @clock = clock || -> { Time.now.to_i } @source = "primary" @mutex = Mutex.new @refresh_done = ConditionVariable.new @state = nil @in_flight = nil end |
Instance Attribute Details
#source ⇒ Object (readonly)
Representative source for the plain source reader; the true origin is reported per-call by btc_fiat_rates_with_source.
301 302 303 |
# File 'lib/openreceive/rates.rb', line 301 def source @source end |
Instance Method Details
#btc_fiat_price(currency) ⇒ Object
The openreceive-server price_provider contract: one decimal price string for one uppercase ISO 4217 currency.
348 349 350 |
# File 'lib/openreceive/rates.rb', line 348 def btc_fiat_price(currency) btc_fiat_rates([currency]).fetch("bitcoin").fetch(Rates.normalize_fiat_currency(currency)) end |
#btc_fiat_rates(currencies) ⇒ Object
327 328 329 |
# File 'lib/openreceive/rates.rb', line 327 def btc_fiat_rates(currencies) btc_fiat_rates_with_source(currencies).fetch("rates") end |
#btc_fiat_rates_with_source(currencies) ⇒ Object
331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/openreceive/rates.rb', line 331 def btc_fiat_rates_with_source(currencies) now = @clock.call claim = read_or_claim_refresh(now) entry = case claim.fetch(:status) when :served then claim.fetch(:entry) when :pending then await_refresh(claim.fetch(:pending)) else tracked_refresh(now, claim.fetch(:previous_entry), claim.fetch(:pending)) end { "source" => entry.fetch("source"), "rates" => Rates.parse_simple_price_response(entry.fetch("rates"), currencies) } end |
#health_check(currencies = nil) ⇒ Object
Forces a live refresh, ignoring the cache, for explicit operational probes. Raises if both feeds fail. Tolerant of an upstream that drops an individual currency (pass no currencies to get everything cached).
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/openreceive/rates.rb', line 355 def health_check(currencies = nil) now = @clock.call pending = { "owner" => Thread.current, "done" => false } previous_entry = @mutex.synchronize do @in_flight = pending @state && @state["entry"] end entry = tracked_refresh(now, previous_entry, pending) rates = if currencies.nil? || currencies.empty? entry.fetch("rates") else Rates.parse_simple_price_response(entry.fetch("rates"), currencies) end { "source" => entry.fetch("source"), "rates" => rates } end |