magick-feature-flags 0.9.23 → 0.9.24

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cae8aed5b88784ad2c7b2635b377cd17270044eea05e4f47a18bd3a209c9e3f
4
- data.tar.gz: f2c69d41bd7a26010f25eee8e0088d660e90fdeb976de961192dcbc52c781c3d
3
+ metadata.gz: 9aaecf68655a77ae48f4f7b2d7f760528a4aa14aa5f37b5f8a603897e3040d40
4
+ data.tar.gz: 8b9694162f06fded4597aaadd14334c515f996979161915004ef113a979aba38
5
5
  SHA512:
6
- metadata.gz: 7a184080f97332ecac03e71f3ae72791ed3a01389a05f84dfdab7d6588dca2a0e8da7c5c5f78b9dc7fee05c09ddbdb302bd8cc97e8a6af0b276e621a01331640
7
- data.tar.gz: c51c09f333e04b46e980c8c6ff67d434611208d716757b53850d625630a48c73c6f4c961f091909ade1fd33d0170f0128111fefb1d1a181f9a012e77be2be1ab
6
+ metadata.gz: 4f47eaddee984442954a6a9f556c434469f8f9c923c93daa4971e232ff0b8ee248c96a510c505c3caecd99ed38687ec2a27544d8624837030a767b13b6253d0f
7
+ data.tar.gz: e2523e3c4051c6922638bdf6739eb3dc89e92822a1a93b0f542d6827bb5706a59d8de3bf2d56d1125584d5e20c016f92b3314b9f1f6ac047cab0ce828b00ad7d
data/README.md CHANGED
@@ -10,7 +10,7 @@ A performant and memory-efficient feature toggle gem for Ruby and Rails applicat
10
10
  - **Rails Integration**: Seamless integration with Rails, including request store caching
11
11
  - **DSL Support**: Define features in a Ruby DSL file (`config/features.rb`)
12
12
  - **Thread-Safe**: All operations are thread-safe for concurrent access
13
- - **Performance**: Optimized for speed with memory-first caching strategy
13
+ - **Performance**: Lightning-fast feature checks with async metrics recording and memory-first caching strategy
14
14
  - **Advanced Features**: Circuit breaker, audit logging, performance metrics, versioning, and more
15
15
 
16
16
  ## Installation
@@ -52,7 +52,8 @@ The generator creates `config/initializers/magick.rb` with sensible defaults. Yo
52
52
  ```ruby
53
53
  Magick.configure do
54
54
  # Configure Redis (optional)
55
- redis url: ENV['REDIS_URL']
55
+ # Use database 1 by default to avoid conflicts with Rails cache (which uses DB 0)
56
+ redis url: ENV['REDIS_URL'], db: 1
56
57
 
57
58
  # Enable features
58
59
  performance_metrics enabled: true
@@ -73,7 +74,10 @@ Magick.configure do
73
74
  memory_ttl 7200 # 2 hours
74
75
 
75
76
  # Redis configuration
76
- redis url: ENV['REDIS_URL'], namespace: 'magick:features'
77
+ # Use separate database (DB 1) to avoid conflicts with Rails cache (DB 0)
78
+ # This ensures feature toggles persist even when Rails cache is cleared
79
+ redis url: ENV['REDIS_URL'], namespace: 'magick:features', db: 1
80
+ # Or include database in URL: redis url: 'redis://localhost:6379/1'
77
81
 
78
82
  # Circuit breaker settings
79
83
  circuit_breaker threshold: 5, timeout: 60
@@ -383,7 +387,9 @@ Magick.configure do
383
387
  end
384
388
  ```
385
389
 
386
- **Note:** When `redis_tracking: true` is set, usage counts are persisted to Redis and aggregated across all processes, giving you total usage statistics.
390
+ **Performance:** Metrics are recorded asynchronously in a background thread, ensuring zero overhead on feature checks. The `enabled?` method remains lightning-fast even with metrics enabled.
391
+
392
+ **Note:** When `redis_tracking: true` is set, usage counts are persisted to Redis and aggregated across all processes, giving you total usage statistics. Metrics are automatically flushed in batches to minimize Redis overhead.
387
393
 
388
394
  #### Audit Logging
389
395
 
@@ -424,6 +430,9 @@ With Redis configured:
424
430
  - ✅ Persistent storage across restarts
425
431
  - ✅ Zero Redis calls on feature checks (only memory lookups)
426
432
  - ✅ Automatic cache invalidation when features change in any process
433
+ - ✅ **Isolated from Rails cache** - Use `db: 1` to store feature toggles in a separate Redis database, ensuring they persist even when Rails cache is cleared
434
+
435
+ **Important:** By default, Magick uses Redis database 1 to avoid conflicts with Rails cache (which typically uses database 0). This ensures that clearing Rails cache (`Rails.cache.clear`) won't affect your feature toggle states.
427
436
 
428
437
  ### Feature Types
429
438
 
data/lib/magick/config.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Magick
4
4
  class Config
5
5
  attr_accessor :adapter_registry, :performance_metrics, :audit_log, :versioning, :warn_on_deprecated,
6
- :async_updates, :memory_ttl, :circuit_breaker_threshold, :circuit_breaker_timeout, :redis_url, :redis_namespace, :environment
6
+ :async_updates, :memory_ttl, :circuit_breaker_threshold, :circuit_breaker_timeout, :redis_url, :redis_namespace, :redis_db, :environment
7
7
 
8
8
  def initialize
9
9
  @warn_on_deprecated = false
@@ -12,6 +12,7 @@ module Magick
12
12
  @circuit_breaker_threshold = 5
13
13
  @circuit_breaker_timeout = 60
14
14
  @redis_namespace = 'magick:features'
15
+ @redis_db = nil # Use default database (0) unless specified
15
16
  @environment = defined?(Rails) ? Rails.env.to_s : 'development'
16
17
  end
17
18
 
@@ -38,10 +39,11 @@ module Magick
38
39
  configure_memory_adapter(**options)
39
40
  end
40
41
 
41
- def redis(url: nil, namespace: nil, **options)
42
+ def redis(url: nil, namespace: nil, db: nil, **options)
42
43
  @redis_url = url if url
43
44
  @redis_namespace = namespace if namespace
44
- redis_adapter = configure_redis_adapter(url: url, namespace: namespace, **options)
45
+ @redis_db = db if db
46
+ redis_adapter = configure_redis_adapter(url: url, namespace: namespace, db: db, **options)
45
47
 
46
48
  # Automatically create Registry adapter if it doesn't exist
47
49
  # This allows users to just call `redis url: ...` without needing to call `adapter :registry`
@@ -161,17 +163,33 @@ module Magick
161
163
  adapter
162
164
  end
163
165
 
164
- def configure_redis_adapter(url: nil, namespace: nil, client: nil)
166
+ def configure_redis_adapter(url: nil, namespace: nil, db: nil, client: nil)
165
167
  return nil unless defined?(Redis)
166
168
 
167
169
  url ||= @redis_url
168
170
  namespace ||= @redis_namespace
171
+ db ||= @redis_db
169
172
 
170
173
  redis_client = client || begin
174
+ redis_options = {}
175
+
171
176
  if url
172
- ::Redis.new(url: url)
177
+ # Parse URL to extract database number if present
178
+ parsed_url = URI.parse(url) rescue nil
179
+ db_from_url = nil
180
+ if parsed_url && parsed_url.path && parsed_url.path.length > 1
181
+ # Redis URL format: redis://host:port/db_number
182
+ db_from_url = parsed_url.path[1..-1].to_i
183
+ end
184
+
185
+ # Use db parameter if provided, otherwise use db from URL, otherwise nil (default DB 0)
186
+ final_db = db || db_from_url
187
+ redis_options[:db] = final_db if final_db
188
+ redis_options[:url] = url
189
+ ::Redis.new(redis_options)
173
190
  else
174
- ::Redis.new
191
+ redis_options[:db] = db if db
192
+ ::Redis.new(redis_options)
175
193
  end
176
194
  rescue StandardError
177
195
  nil
@@ -179,6 +197,20 @@ module Magick
179
197
 
180
198
  return nil unless redis_client
181
199
 
200
+ # If db was specified but not in URL, select it explicitly
201
+ # This handles cases where URL doesn't include db number
202
+ if db && url
203
+ parsed_url = URI.parse(url) rescue nil
204
+ url_has_db = parsed_url && parsed_url.path && parsed_url.path.length > 1
205
+ unless url_has_db
206
+ begin
207
+ redis_client.select(db)
208
+ rescue StandardError
209
+ # Ignore if SELECT fails (some Redis setups don't support SELECT, e.g., Redis Cluster)
210
+ end
211
+ end
212
+ end
213
+
182
214
  adapter = Adapters::Redis.new(redis_client)
183
215
  adapter.instance_variable_set(:@namespace, namespace) if namespace
184
216
  adapter
@@ -25,7 +25,13 @@ if defined?(Rails)
25
25
  if defined?(Redis)
26
26
  begin
27
27
  redis_url = app.config.respond_to?(:redis_url) ? app.config.redis_url : nil
28
- redis_client = redis_url ? ::Redis.new(url: redis_url) : ::Redis.new
28
+ # Use database 1 for Magick by default to avoid conflicts with Rails cache (which uses DB 0)
29
+ # Users can override this in their config/initializers/magick.rb
30
+ redis_db = 1
31
+ redis_options = {}
32
+ redis_options[:url] = redis_url if redis_url
33
+ redis_options[:db] = redis_db
34
+ redis_client = redis_url ? ::Redis.new(redis_options) : ::Redis.new(db: redis_db)
29
35
  memory_adapter = Adapters::Memory.new
30
36
  redis_adapter = Adapters::Redis.new(redis_client)
31
37
  magick.adapter_registry = Adapters::Registry.new(memory_adapter, redis_adapter)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.23'
4
+ VERSION = '0.9.24'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magick-feature-flags
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.23
4
+ version: 0.9.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-24 00:00:00.000000000 Z
11
+ date: 2025-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec