magick-feature-flags 1.3.1 → 1.3.2
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/magick/adapters/registry.rb +54 -1
- data/lib/magick/rails/railtie.rb +11 -0
- data/lib/magick/version.rb +1 -1
- data/lib/magick.rb +19 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58eabf5855bcd4578d09090c65cc5b8d1b33ba2caa38ae7dd85bf7ecebb76047
|
|
4
|
+
data.tar.gz: e57e675ff58e913e60ef883b4dd8078087c7575ef5ced4355c8811bb15bf2dd3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3fdaf5955fcf00d83047ff4ed785476b91b89e9d53ad80d890459e77134cf974cfe53a204060e471c491f133cd4d88f88e0f91813e042e5bf311821e09d804ea
|
|
7
|
+
data.tar.gz: d09483c77bb6af18a7f24565366d08e28a725c95d741c8cdcbf0ae3abcd17d8c3e0626942668a41f9041120a786f0463d35567c4ccfaaca3f56826f2888aa682
|
|
@@ -21,11 +21,34 @@ module Magick
|
|
|
21
21
|
@last_reload_times = {} # Track last reload time per feature for debouncing
|
|
22
22
|
@local_writes = {} # Track recent local writes to skip self-invalidation
|
|
23
23
|
@reload_mutex = Mutex.new
|
|
24
|
+
@stopping = false
|
|
25
|
+
@shutdown_mutex = Mutex.new
|
|
24
26
|
# Only start Pub/Sub subscriber if Redis is available
|
|
25
27
|
# In memory-only mode, each process has isolated cache (no cross-process invalidation)
|
|
26
28
|
start_cache_invalidation_subscriber if redis_adapter
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
# Gracefully terminate the Pub/Sub subscriber thread and its Redis connection.
|
|
32
|
+
# Without this, Ruby/Puma shutdown waits on the blocking `subscribe` call.
|
|
33
|
+
def shutdown(timeout: 5)
|
|
34
|
+
@shutdown_mutex.synchronize do
|
|
35
|
+
return if @stopping
|
|
36
|
+
|
|
37
|
+
@stopping = true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
close_subscriber_connection(@subscriber)
|
|
41
|
+
terminate_subscriber_thread(@subscriber_thread, timeout)
|
|
42
|
+
|
|
43
|
+
@subscriber = nil
|
|
44
|
+
@subscriber_thread = nil
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def stopping?
|
|
49
|
+
@stopping == true
|
|
50
|
+
end
|
|
51
|
+
|
|
29
52
|
def get(feature_name, key)
|
|
30
53
|
# Try memory first (fastest) - no Redis calls needed thanks to Pub/Sub invalidation
|
|
31
54
|
value = memory_adapter.get(feature_name, key) if memory_adapter
|
|
@@ -288,6 +311,32 @@ module Magick
|
|
|
288
311
|
|
|
289
312
|
attr_reader :memory_adapter, :redis_adapter, :active_record_adapter, :circuit_breaker
|
|
290
313
|
|
|
314
|
+
# Signal the subscribe loop to return, then close the connection so any
|
|
315
|
+
# retry/reconnect attempt fails fast instead of sleeping for 5s.
|
|
316
|
+
def close_subscriber_connection(subscriber)
|
|
317
|
+
return unless subscriber
|
|
318
|
+
|
|
319
|
+
begin
|
|
320
|
+
subscriber.unsubscribe(CACHE_INVALIDATION_CHANNEL)
|
|
321
|
+
rescue StandardError
|
|
322
|
+
# connection may already be dead; fall through to close/kill
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
begin
|
|
326
|
+
subscriber.close
|
|
327
|
+
rescue StandardError
|
|
328
|
+
# ignore: best-effort close
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def terminate_subscriber_thread(thread, timeout)
|
|
333
|
+
return unless thread
|
|
334
|
+
return if thread.join(timeout)
|
|
335
|
+
|
|
336
|
+
thread.kill
|
|
337
|
+
thread.join(1) # give it a moment to actually unwind
|
|
338
|
+
end
|
|
339
|
+
|
|
291
340
|
# Record that this process just wrote a feature, so the subscriber
|
|
292
341
|
# ignores its own Pub/Sub messages and doesn't revert the correct in-memory state.
|
|
293
342
|
def record_local_write(feature_name)
|
|
@@ -421,9 +470,13 @@ module Magick
|
|
|
421
470
|
(defined?(Rails) && Rails.env.test?)
|
|
422
471
|
return if is_rspec_error
|
|
423
472
|
|
|
473
|
+
# Stop cleanly during app shutdown instead of sleeping + retrying,
|
|
474
|
+
# which would keep the process alive and delay termination.
|
|
475
|
+
return if @stopping
|
|
476
|
+
|
|
424
477
|
warn "Cache invalidation subscriber error: #{e.message}" if defined?(Rails) && Rails.env.development?
|
|
425
478
|
sleep 5
|
|
426
|
-
retry
|
|
479
|
+
retry unless @stopping
|
|
427
480
|
end
|
|
428
481
|
@subscriber_thread.abort_on_exception = false
|
|
429
482
|
end
|
data/lib/magick/rails/railtie.rb
CHANGED
|
@@ -125,6 +125,17 @@ if defined?(Rails)
|
|
|
125
125
|
Magick.performance_metrics.enable_redis_tracking(enable: true)
|
|
126
126
|
end
|
|
127
127
|
end
|
|
128
|
+
|
|
129
|
+
# Terminate the Pub/Sub subscriber + async metrics thread on process exit.
|
|
130
|
+
# Without this, Ruby waits on the blocking `Redis#subscribe` call inside
|
|
131
|
+
# the subscriber thread and Puma/Rails shutdown stalls.
|
|
132
|
+
initializer 'magick.shutdown_hook' do
|
|
133
|
+
at_exit do
|
|
134
|
+
Magick.shutdown!
|
|
135
|
+
rescue StandardError
|
|
136
|
+
# Best-effort: never raise from an at_exit handler.
|
|
137
|
+
end
|
|
138
|
+
end
|
|
128
139
|
end
|
|
129
140
|
end
|
|
130
141
|
|
data/lib/magick/version.rb
CHANGED
data/lib/magick.rb
CHANGED
|
@@ -265,6 +265,15 @@ module Magick
|
|
|
265
265
|
@performance_metrics&.clear!
|
|
266
266
|
end
|
|
267
267
|
|
|
268
|
+
# Gracefully terminate background threads (Redis Pub/Sub subscriber,
|
|
269
|
+
# async metrics processor) so the host process can exit promptly.
|
|
270
|
+
# Intended for use in Rails shutdown hooks, `at_exit`, or tests.
|
|
271
|
+
def shutdown!(timeout: 5)
|
|
272
|
+
safely_shutdown(@adapter_registry) { |r| r.shutdown(timeout: timeout) }
|
|
273
|
+
safely_shutdown(@performance_metrics, &:stop_async_processor)
|
|
274
|
+
true
|
|
275
|
+
end
|
|
276
|
+
|
|
268
277
|
# Get default adapter registry (public method for use by other classes)
|
|
269
278
|
def default_adapter_registry
|
|
270
279
|
@default_adapter_registry ||= begin
|
|
@@ -279,5 +288,15 @@ module Magick
|
|
|
279
288
|
end
|
|
280
289
|
|
|
281
290
|
private
|
|
291
|
+
|
|
292
|
+
# Run a cleanup action on a collaborator, swallowing errors so that
|
|
293
|
+
# shutdown hooks (at_exit, Rails) never raise.
|
|
294
|
+
def safely_shutdown(collaborator)
|
|
295
|
+
return unless collaborator
|
|
296
|
+
|
|
297
|
+
yield(collaborator)
|
|
298
|
+
rescue StandardError
|
|
299
|
+
# Best-effort: termination paths must not raise.
|
|
300
|
+
end
|
|
282
301
|
end
|
|
283
302
|
end
|