Class: BSV::Network::Services
- Inherits:
-
Object
- Object
- BSV::Network::Services
- Defined in:
- lib/bsv/network/services.rb
Overview
Porcelain routing layer above SDK providers/protocols.
Same call(command, *args, **kwargs) interface as Provider — drop-in replacement. Adds capability-based routing, fallback on retryable errors, per-provider rate limiting, response normalization, and opportunistic sibling data caching.
Defined Under Namespace
Classes: TokenBucket
Instance Attribute Summary collapse
-
#providers ⇒ Array<BSV::Network::Provider>
readonly
Returns the registered providers (frozen at construction time).
Instance Method Summary collapse
-
#call(command, *args, **kwargs) ⇒ BSV::Network::ProtocolResponse
Dispatch a command with provider routing and fallback.
-
#commands ⇒ Set<Symbol>
Union of all commands available across all registered providers.
-
#fetch!(entity) ⇒ BSV::Network::ProtocolResponse
Fetch state from the network into an entity.
-
#initialize(providers:) ⇒ Services
constructor
A new instance of Services.
-
#push!(entity) ⇒ BSV::Network::ProtocolResponse
Push an entity to the network.
Constructor Details
#initialize(providers:) ⇒ Services
Returns a new instance of Services.
22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/bsv/network/services.rb', line 22 def initialize(providers:) raise ArgumentError, 'at least one provider is required' if providers.nil? || providers.empty? @providers = providers.dup.freeze @buckets = providers.each_with_object({}) do |p, h| h[p] = TokenBucket.new(p.rate_limit) if p.rate_limit end @sibling_memo = {} @broadcast_affinity = {} @mutex = Mutex.new end |
Instance Attribute Details
#providers ⇒ Array<BSV::Network::Provider> (readonly)
Returns the registered providers (frozen at construction time).
125 126 127 |
# File 'lib/bsv/network/services.rb', line 125 def providers @providers end |
Instance Method Details
#call(command, *args, **kwargs) ⇒ BSV::Network::ProtocolResponse
Dispatch a command with provider routing and fallback.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/bsv/network/services.rb', line 40 def call(command, *args, **kwargs) sym = command.to_sym # Serve from sibling memo if available memo_result = check_sibling_memo(sym, args, kwargs) return memo_result if memo_result candidates = candidates_for(sym, args, kwargs) return no_provider_response(sym) if candidates.empty? last_error = nil candidates.each do |provider| acquire_rate_limit!(provider) result = provider.call(sym, *args, **kwargs) if result.http_success? stash_siblings(sym, result, args, kwargs) normalized = normalize(sym, result) record_affinity(sym, provider, normalized) return normalized end return result if result.http_not_found? last_error = result break unless result.retryable? end last_error end |
#commands ⇒ Set<Symbol>
Union of all commands available across all registered providers.
76 77 78 |
# File 'lib/bsv/network/services.rb', line 76 def commands @providers.reduce(Set.new) { |acc, p| acc | p.commands } end |
#fetch!(entity) ⇒ BSV::Network::ProtocolResponse
Fetch state from the network into an entity.
Calls entity.fetch_command and entity.fetch_args, dispatches through the routing layer, and writes the response back on success.
108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/bsv/network/services.rb', line 108 def fetch!(entity) command = entity.fetch_command args = entity.fetch_args response = call(command, **args) if response.http_success? entity.write!(response) else BSV.logger&.warn { "[Services] fetch! failed: #{response.}" } end response end |
#push!(entity) ⇒ BSV::Network::ProtocolResponse
Push an entity to the network.
Calls entity.push_command and entity.push_payload, dispatches through the routing layer, and writes the response back on success.
87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/bsv/network/services.rb', line 87 def push!(entity) command = entity.push_command payload = entity.push_payload response = call(command, payload) if response.http_success? entity.write!(response) else BSV.logger&.warn { "[Services] push! failed: #{response.}" } end response end |