smplkit 3.0.134 → 3.0.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/smplkit/client.rb +27 -27
- data/lib/smplkit/config/client.rb +44 -41
- data/lib/smplkit/event_stream.rb +453 -0
- data/lib/smplkit/flags/client.rb +45 -42
- data/lib/smplkit/jobs/client.rb +2 -2
- data/lib/smplkit/jobs/models.rb +2 -2
- data/lib/smplkit/logging/client.rb +69 -59
- data/lib/smplkit/transport.rb +1 -1
- data/lib/smplkit/version.rb +2 -2
- data/lib/smplkit.rb +1 -1
- metadata +2 -22
- data/lib/smplkit/ws.rb +0 -268
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c1cba1222e1ec1109993202959fa417b65752d969da90230f6f4ed913de9753
|
|
4
|
+
data.tar.gz: d1656093905daa0e565c6fb20c482b922251d2a4eb0dfbc7ca8684bc323abac5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c18d2ec862ee1ad4b58539f326aaa55e6868de62205fe037998a00f90cb6d3c60676d80ab3f94771d8923d9c08543133d861358dc9c18dd1ff35a57ae15e783
|
|
7
|
+
data.tar.gz: d9225dd299dbbeac794f58a7fbd48713e10166e4f7e266a4eedd4ead1649c7315542de018a5d1bfe1aa1796cb7f82d4c04bfac4fdab30ab1ecd21ac4ba48ad2c
|
data/lib/smplkit/client.rb
CHANGED
|
@@ -102,7 +102,7 @@ module Smplkit
|
|
|
102
102
|
MetricsReporter.new(http_client: @app_http, environment: cfg.environment, service: cfg.service)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
@
|
|
105
|
+
@event_stream = nil
|
|
106
106
|
# Platform's cross-cutting CRUD on one client; wired into this parent so
|
|
107
107
|
# it borrows the shared app transport, and owns the context-registration
|
|
108
108
|
# buffer. Built BEFORE flags so the contexts seam below is available.
|
|
@@ -111,17 +111,17 @@ module Smplkit
|
|
|
111
111
|
# (the settings sub-client uses Faraday directly).
|
|
112
112
|
@account = Account::AccountClient.new(api_key: cfg.api_key, base_url: app_url, extra_headers: extra_headers)
|
|
113
113
|
# Config's full surface on one client; wired into this parent so it
|
|
114
|
-
# borrows the shared config transport and
|
|
114
|
+
# borrows the shared config transport and event stream.
|
|
115
115
|
@config = Config::ConfigClient.new(parent: self, transport: @transports.config_http, metrics: @metrics)
|
|
116
116
|
# Flags' full surface on one client; wired into this parent so it borrows
|
|
117
|
-
# the shared flags transport and
|
|
118
|
-
# seam for evaluation-context registration, wired to
|
|
117
|
+
# the shared flags transport and event stream. ``contexts`` is the
|
|
118
|
+
# injection seam for evaluation-context registration, wired to
|
|
119
119
|
# ``client.platform.contexts``.
|
|
120
120
|
@flags = Flags::FlagsClient.new(
|
|
121
121
|
parent: self, transport: @transports.flags_http, contexts: @platform.contexts, metrics: @metrics
|
|
122
122
|
)
|
|
123
123
|
# Logging's full surface on one client; wired into this parent so it
|
|
124
|
-
# borrows the shared logging transport and
|
|
124
|
+
# borrows the shared logging transport and event stream. The two management
|
|
125
125
|
# sub-clients live at client.logging.loggers / client.logging.log_groups.
|
|
126
126
|
@logging = Logging::LoggingClient.new(parent: self, transport: @transports.logging_http, metrics: @metrics)
|
|
127
127
|
# Audit's full surface on one client; this runtime instance scopes audit
|
|
@@ -148,35 +148,35 @@ module Smplkit
|
|
|
148
148
|
@init_thread = nil
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
# Optionally pre-warm the SDK and block until the live
|
|
151
|
+
# Optionally pre-warm the SDK and block until the live stream is up.
|
|
152
152
|
#
|
|
153
153
|
# Eagerly connects config and flags — flushing discovery, pre-fetching all
|
|
154
|
-
# flags and configs into the local cache, opening the live-updates
|
|
155
|
-
# — and waits for the
|
|
156
|
-
# / +client.config.subscribe+ hit cache (no first-request
|
|
157
|
-
# any +on_change+ listeners receive every server event
|
|
158
|
-
# forward.
|
|
154
|
+
# flags and configs into the local cache, opening the live-updates event
|
|
155
|
+
# stream — and waits for the connect to complete. After this returns,
|
|
156
|
+
# +flag.get+ / +client.config.subscribe+ hit cache (no first-request
|
|
157
|
+
# connect tax) and any +on_change+ listeners receive every server event
|
|
158
|
+
# from this point forward.
|
|
159
159
|
#
|
|
160
160
|
# Optional: config and flags connect lazily on first live use, so this is
|
|
161
|
-
# purely a pre-warm /
|
|
161
|
+
# purely a pre-warm / stream-ready barrier. Logging integration is *not*
|
|
162
162
|
# connected here — call +client.logging.install+ separately if you want it
|
|
163
163
|
# (it installs adapters and hooks into your application's logger, which
|
|
164
164
|
# should be opt-in).
|
|
165
165
|
#
|
|
166
166
|
# @param timeout [Float] Maximum seconds to wait for the live-updates
|
|
167
|
-
#
|
|
167
|
+
# event stream to connect before giving up. Defaults to +10.0+.
|
|
168
168
|
# @return [void]
|
|
169
|
-
# @raise [Smplkit::TimeoutError] If the
|
|
170
|
-
# +timeout+ seconds.
|
|
169
|
+
# @raise [Smplkit::TimeoutError] If the event stream fails to connect
|
|
170
|
+
# within +timeout+ seconds.
|
|
171
171
|
def wait_until_ready(timeout: 10.0)
|
|
172
172
|
@flags._ensure_connected
|
|
173
173
|
@config._ensure_connected
|
|
174
|
-
|
|
174
|
+
stream = _ensure_event_stream
|
|
175
175
|
deadline = monotonic_now + timeout
|
|
176
|
-
while
|
|
176
|
+
while stream.connection_status != "connected"
|
|
177
177
|
if monotonic_now >= deadline
|
|
178
|
-
raise TimeoutError, "Live-updates
|
|
179
|
-
"(status: #{
|
|
178
|
+
raise TimeoutError, "Live-updates event stream did not connect within #{timeout}s " \
|
|
179
|
+
"(status: #{stream.connection_status.inspect})"
|
|
180
180
|
end
|
|
181
181
|
|
|
182
182
|
sleep(0.05)
|
|
@@ -235,8 +235,8 @@ module Smplkit
|
|
|
235
235
|
@logging._close
|
|
236
236
|
@flags._close
|
|
237
237
|
@audit._close
|
|
238
|
-
@
|
|
239
|
-
@
|
|
238
|
+
@event_stream&.stop
|
|
239
|
+
@event_stream = nil
|
|
240
240
|
# Close the shared per-service HTTP transports (app/config/flags/logging/
|
|
241
241
|
# jobs). client.platform/account borrow the app transport and close
|
|
242
242
|
# nothing; client.audit owns and closed its own transport above.
|
|
@@ -256,7 +256,7 @@ module Smplkit
|
|
|
256
256
|
#
|
|
257
257
|
# Idempotent and thread-safe (lock + flag); a no-op after +close+. Triggered
|
|
258
258
|
# by the first config/flags/logging operation, +set_context+,
|
|
259
|
-
# +wait_until_ready+, or
|
|
259
|
+
# +wait_until_ready+, or event stream open — never at construction.
|
|
260
260
|
def _ensure_started
|
|
261
261
|
@start_lock.synchronize do
|
|
262
262
|
return if @started || @closed
|
|
@@ -267,13 +267,13 @@ module Smplkit
|
|
|
267
267
|
@init_thread = Thread.new { register_service_context }
|
|
268
268
|
end
|
|
269
269
|
|
|
270
|
-
def
|
|
270
|
+
def _ensure_event_stream
|
|
271
271
|
_ensure_started
|
|
272
|
-
if @
|
|
273
|
-
@
|
|
274
|
-
@
|
|
272
|
+
if @event_stream.nil?
|
|
273
|
+
@event_stream = EventStream.new(app_base_url: @app_base_url, api_key: @api_key, metrics: @metrics)
|
|
274
|
+
@event_stream.start
|
|
275
275
|
end
|
|
276
|
-
@
|
|
276
|
+
@event_stream
|
|
277
277
|
end
|
|
278
278
|
|
|
279
279
|
private
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
# resolved read), +bind+ (a live Struct/Hash binding), +on_change+, and
|
|
15
15
|
# +refresh+. The first live call transparently flushes discovery, fetches and
|
|
16
16
|
# resolves every config into the local cache, and opens the live-updates
|
|
17
|
-
#
|
|
17
|
+
# event stream — no explicit install step.
|
|
18
18
|
#
|
|
19
19
|
# The client supports two construction shapes:
|
|
20
20
|
#
|
|
21
21
|
# * *Wired* into +Smplkit::Client+ — borrows the parent's config transport for
|
|
22
|
-
# both runtime fetch and CRUD and the parent's shared
|
|
23
|
-
# channel. This is the common path.
|
|
22
|
+
# both runtime fetch and CRUD and the parent's shared event stream for the
|
|
23
|
+
# live channel. This is the common path.
|
|
24
24
|
# * *Standalone* — +ConfigClient.new(api_key: ..., base_url: ..., ...)+ builds
|
|
25
25
|
# and owns its own config transport, and on first live use opens and owns its
|
|
26
|
-
# own
|
|
27
|
-
#
|
|
26
|
+
# own event stream. +close+ tears down only the owned transport and owned
|
|
27
|
+
# event stream.
|
|
28
28
|
module Smplkit
|
|
29
29
|
module Config
|
|
30
30
|
# Module-level helpers for the config client. Extracted so they can be
|
|
@@ -339,7 +339,7 @@ module Smplkit
|
|
|
339
339
|
# resolver fills in whatever is missing (+~/.smplkit+ / env vars /
|
|
340
340
|
# defaults). +environment+/+service+ resolve the same way (constructor
|
|
341
341
|
# argument wins). The app base URL is returned alongside so a standalone
|
|
342
|
-
# client can open its own
|
|
342
|
+
# client can open its own event stream against the app service.
|
|
343
343
|
#
|
|
344
344
|
# @api private
|
|
345
345
|
# @param api_key [String, nil] API key, or +nil+ to resolve it.
|
|
@@ -392,7 +392,7 @@ module Smplkit
|
|
|
392
392
|
# is pure CRUD. The live surface (+subscribe+ / +get_value+ / +bind+ /
|
|
393
393
|
# +on_change+ / +refresh+) connects lazily on first use — the first call
|
|
394
394
|
# flushes discovery, fetches and resolves all configs into the local cache,
|
|
395
|
-
# and opens the live-updates
|
|
395
|
+
# and opens the live-updates event stream. No explicit install step is
|
|
396
396
|
# required.
|
|
397
397
|
class ConfigClient
|
|
398
398
|
# Sentinel distinguishing "no default supplied" from an explicit +nil+
|
|
@@ -418,8 +418,8 @@ module Smplkit
|
|
|
418
418
|
# @param debug [Boolean, nil] Enable SDK debug logging.
|
|
419
419
|
# @param extra_headers [Hash{String => String}, nil] Extra headers
|
|
420
420
|
# attached to every request.
|
|
421
|
-
# @param streaming [Boolean] Live updates over
|
|
422
|
-
# +true+): the first live call opens a shared
|
|
421
|
+
# @param streaming [Boolean] Live updates over the event stream (default
|
|
422
|
+
# +true+): the first live call opens a shared stream and config changes
|
|
423
423
|
# stream in. Set +false+ for the stateless read-through surface: the
|
|
424
424
|
# first live call still fetches and resolves every config once
|
|
425
425
|
# (blocking), reads stay local, +refresh+ re-fetches on demand, and NO
|
|
@@ -478,8 +478,8 @@ module Smplkit
|
|
|
478
478
|
@connected = false
|
|
479
479
|
@lock = Mutex.new
|
|
480
480
|
@listeners = [] # [callback, config_id_or_nil, item_key_or_nil]
|
|
481
|
-
@
|
|
482
|
-
@
|
|
481
|
+
@event_stream = nil
|
|
482
|
+
@owns_stream = false
|
|
483
483
|
end
|
|
484
484
|
|
|
485
485
|
# ----------------------------------------------------------------
|
|
@@ -648,7 +648,7 @@ module Smplkit
|
|
|
648
648
|
# authoritative and synced onto the bound object; if it is brand-new, the
|
|
649
649
|
# cache entry is seeded in-memory from the bound object's values resolved
|
|
650
650
|
# through its bound parent chain (no network round-trip). On every
|
|
651
|
-
#
|
|
651
|
+
# stream-delivered change thereafter the bound object is mutated in
|
|
652
652
|
# place. Readers always see the current resolved value with no proxy
|
|
653
653
|
# indirection.
|
|
654
654
|
#
|
|
@@ -677,7 +677,7 @@ module Smplkit
|
|
|
677
677
|
|
|
678
678
|
parent_id = register_binding_declaration(id, config, parent)
|
|
679
679
|
|
|
680
|
-
# Register the binding BEFORE syncing so
|
|
680
|
+
# Register the binding BEFORE syncing so event dispatch finds it.
|
|
681
681
|
@bindings[id] = config
|
|
682
682
|
@bound_parents[id] = parent_id
|
|
683
683
|
seed_or_sync_binding(id, config)
|
|
@@ -803,17 +803,17 @@ module Smplkit
|
|
|
803
803
|
|
|
804
804
|
# Release resources — only those this client owns.
|
|
805
805
|
#
|
|
806
|
-
# Tears down the owned
|
|
807
|
-
# live use) and the owned HTTP transport (standalone construction).
|
|
808
|
-
# wired client borrows the parent's transport and
|
|
809
|
-
# neither.
|
|
806
|
+
# Tears down the owned event stream (opened by a standalone client on
|
|
807
|
+
# first live use) and the owned HTTP transport (standalone construction).
|
|
808
|
+
# A wired client borrows the parent's transport and event stream and
|
|
809
|
+
# closes neither.
|
|
810
810
|
#
|
|
811
811
|
# @return [void]
|
|
812
812
|
def close
|
|
813
|
-
if @
|
|
814
|
-
@
|
|
815
|
-
@
|
|
816
|
-
@
|
|
813
|
+
if @owns_stream && @event_stream
|
|
814
|
+
@event_stream.stop
|
|
815
|
+
@event_stream = nil
|
|
816
|
+
@owns_stream = false
|
|
817
817
|
end
|
|
818
818
|
nil
|
|
819
819
|
end
|
|
@@ -848,29 +848,31 @@ module Smplkit
|
|
|
848
848
|
private
|
|
849
849
|
|
|
850
850
|
# ----------------------------------------------------------------
|
|
851
|
-
# Live surface: lazy connect + transport /
|
|
851
|
+
# Live surface: lazy connect + transport / event stream helpers
|
|
852
852
|
# ----------------------------------------------------------------
|
|
853
853
|
|
|
854
|
-
def
|
|
855
|
-
return @parent.
|
|
854
|
+
def ensure_event_stream
|
|
855
|
+
return @parent._ensure_event_stream unless @parent.nil?
|
|
856
856
|
|
|
857
|
-
if @
|
|
858
|
-
@
|
|
857
|
+
if @event_stream.nil?
|
|
858
|
+
@event_stream = EventStream.new(
|
|
859
859
|
app_base_url: @app_base_url, api_key: @standalone_api_key, metrics: @metrics
|
|
860
860
|
)
|
|
861
|
-
@
|
|
862
|
-
@
|
|
861
|
+
@event_stream.start
|
|
862
|
+
@owns_stream = true
|
|
863
863
|
end
|
|
864
|
-
@
|
|
864
|
+
@event_stream
|
|
865
865
|
end
|
|
866
866
|
|
|
867
867
|
# Open the live connection to the running Smpl Config service.
|
|
868
868
|
#
|
|
869
869
|
# Flushes any buffered discovery declarations, fetches and resolves every
|
|
870
870
|
# config for the configured environment into the local cache, opens the
|
|
871
|
-
# shared
|
|
872
|
-
# / +configs_changed+ events.
|
|
873
|
-
#
|
|
871
|
+
# shared event stream, and subscribes to +config_changed+ /
|
|
872
|
+
# +config_deleted+ / +configs_changed+ events. Also registers the
|
|
873
|
+
# bulk-refresh path as the stream's reconnect refetch, so a stream outage
|
|
874
|
+
# ends with a full re-sync. In stateless mode (+streaming: false+) no
|
|
875
|
+
# stream is ever created; +refresh+ re-fetches on demand.
|
|
874
876
|
#
|
|
875
877
|
# Idempotent and internal — every live method calls it on first use, so
|
|
876
878
|
# the live surface auto-connects with no explicit step.
|
|
@@ -892,10 +894,11 @@ module Smplkit
|
|
|
892
894
|
@connected = true
|
|
893
895
|
return unless @streaming
|
|
894
896
|
|
|
895
|
-
@
|
|
896
|
-
@
|
|
897
|
-
@
|
|
898
|
-
@
|
|
897
|
+
@event_stream = ensure_event_stream
|
|
898
|
+
@event_stream.on("config_changed") { |data| handle_config_changed(data) }
|
|
899
|
+
@event_stream.on("config_deleted") { |data| handle_config_deleted(data) }
|
|
900
|
+
@event_stream.on("configs_changed") { |data| handle_configs_changed(data) }
|
|
901
|
+
@event_stream.on_reconnect { handle_configs_changed({}) }
|
|
899
902
|
end
|
|
900
903
|
|
|
901
904
|
# List configs directly from the API for the runtime cache.
|
|
@@ -1047,7 +1050,7 @@ module Smplkit
|
|
|
1047
1050
|
#
|
|
1048
1051
|
# A freshly-bound config lives only as a seed until it is flushed and
|
|
1049
1052
|
# fetched; without this, any cache rebuild (a manual refresh, or a
|
|
1050
|
-
#
|
|
1053
|
+
# push event for another config) would drop it. Server-present
|
|
1051
1054
|
# configs are already in +new_cache+ and are authoritative — only bound
|
|
1052
1055
|
# ids missing from it are re-seeded.
|
|
1053
1056
|
def merge_pending_seeds(new_cache)
|
|
@@ -1124,7 +1127,7 @@ module Smplkit
|
|
|
1124
1127
|
end
|
|
1125
1128
|
|
|
1126
1129
|
# ----------------------------------------------------------------
|
|
1127
|
-
# Internal: event handlers (called by
|
|
1130
|
+
# Internal: event handlers (called by EventStream)
|
|
1128
1131
|
# ----------------------------------------------------------------
|
|
1129
1132
|
|
|
1130
1133
|
# Re-resolve every config in +store+ and fire change listeners.
|
|
@@ -1189,7 +1192,7 @@ module Smplkit
|
|
|
1189
1192
|
|
|
1190
1193
|
new_store[key] = cfg
|
|
1191
1194
|
ensure_ancestors_cached(new_store)
|
|
1192
|
-
rebuild_from_store(new_store, source: "
|
|
1195
|
+
rebuild_from_store(new_store, source: "push")
|
|
1193
1196
|
rescue StandardError => e
|
|
1194
1197
|
Smplkit.debug("config", "config_changed handler failed for #{key.inspect}: #{e.class}: #{e.message}")
|
|
1195
1198
|
end
|
|
@@ -1202,11 +1205,11 @@ module Smplkit
|
|
|
1202
1205
|
new_store = @lock.synchronize { @raw_config_store.dup }
|
|
1203
1206
|
return if new_store.delete(key).nil?
|
|
1204
1207
|
|
|
1205
|
-
rebuild_from_store(new_store, source: "
|
|
1208
|
+
rebuild_from_store(new_store, source: "push")
|
|
1206
1209
|
end
|
|
1207
1210
|
|
|
1208
1211
|
def handle_configs_changed(_data)
|
|
1209
|
-
do_refresh("
|
|
1212
|
+
do_refresh("push")
|
|
1210
1213
|
rescue StandardError => e
|
|
1211
1214
|
Smplkit.debug("config", "configs_changed refresh failed: #{e.class}: #{e.message}")
|
|
1212
1215
|
end
|