magick-feature-flags 0.9.34 → 0.9.35

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: 72089ec7fc15f5c7f854f03caf17896d4810f99d1067be9fadc17c9c5569b341
4
- data.tar.gz: e928f66dd35ada0bb734c424178ba3c6db78f934b49599c21296506da697c4c2
3
+ metadata.gz: 5a2d57b267028e1f3d30a1a5dc4e540d6b254d41ea1b436f5ab0e25278c0f213
4
+ data.tar.gz: 0c1a022445c14e1534b312f4cfc71ec71b9a576d4b66a5d860323a480df79380
5
5
  SHA512:
6
- metadata.gz: 0241c50192a89be6f429eac0367538687d8bb5d5dcd897cb1e7804cca4ce452d8e975132b62a0e1bc193f380294df1f0a9b2923680559964944fb9046be8fd94
7
- data.tar.gz: dadadc9da7abcb0d031ec65c2ca016d9be0c4d914f1dd7240fbae12c614c8fc140e697a095e1fba744e0f691a6b2cacd3ab843c24ed479b4ecfdbd01c9ad04d6
6
+ metadata.gz: a0576cc8537ef6df5ff4ce95f973994a477936d94cde874b24945505139fbf411798fb54d100544f1848ad1b2388164b64a95421732daa33d753bcef7c14cce5
7
+ data.tar.gz: 803584894d4c2494c324df94613a80a34ca268f522ffabe36bc162e989ca25be8aaa9d1f919f10584e722b47863940a1610c6f7cbc75d6960b2070304e4efe55
@@ -3,18 +3,17 @@
3
3
  module Magick
4
4
  module Adapters
5
5
  class ActiveRecord < Base
6
- @table_created_mutex = Mutex.new
7
- @table_created = false
8
-
9
6
  def initialize(model_class: nil)
10
7
  @model_class = model_class || default_model_class
11
- ensure_table_exists
8
+ # Verify table exists - raise clear error if it doesn't
9
+ unless @model_class.table_exists?
10
+ raise AdapterError, "Table 'magick_features' does not exist. Please run: rails generate magick:active_record && rails db:migrate"
11
+ end
12
12
  rescue StandardError => e
13
13
  raise AdapterError, "Failed to initialize ActiveRecord adapter: #{e.message}"
14
14
  end
15
15
 
16
16
  def get(feature_name, key)
17
- ensure_table_exists unless @model_class.table_exists?
18
17
  feature_name_str = feature_name.to_s
19
18
  record = @model_class.find_by(feature_name: feature_name_str)
20
19
  return nil unless record
@@ -24,16 +23,10 @@ module Magick
24
23
  value = data.is_a?(Hash) ? data[key.to_s] : nil
25
24
  deserialize_value(value)
26
25
  rescue StandardError => e
27
- # If table doesn't exist, try to create it and retry once
28
- if e.message.include?('no such table') || e.message.include?("doesn't exist")
29
- ensure_table_exists
30
- retry
31
- end
32
26
  raise AdapterError, "Failed to get from ActiveRecord: #{e.message}"
33
27
  end
34
28
 
35
29
  def set(feature_name, key, value)
36
- ensure_table_exists unless @model_class.table_exists?
37
30
  feature_name_str = feature_name.to_s
38
31
  retries = 5
39
32
  begin
@@ -48,54 +41,31 @@ module Magick
48
41
  record.save!
49
42
  rescue ::ActiveRecord::StatementInvalid, ::ActiveRecord::ConnectionTimeoutError => e
50
43
  # SQLite busy/locked errors - retry with exponential backoff
51
- if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout') || e.message.include?('no such table')) && retries > 0
44
+ if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout')) && retries > 0
52
45
  retries -= 1
53
- # If it's a "no such table" error, ensure table exists
54
- if e.message.include?('no such table')
55
- ensure_table_exists
56
- end
57
46
  sleep(0.01 * (6 - retries)) # Exponential backoff: 0.01, 0.02, 0.03, 0.04, 0.05
58
47
  retry
59
48
  end
60
49
  raise AdapterError, "Failed to set in ActiveRecord: #{e.message}"
61
50
  rescue StandardError => e
62
- # If table doesn't exist, try to create it and retry once
63
- if (e.message.include?('no such table') || e.message.include?("doesn't exist")) && retries > 0
64
- retries -= 1
65
- ensure_table_exists
66
- sleep(0.01)
67
- retry
68
- end
69
51
  raise AdapterError, "Failed to set in ActiveRecord: #{e.message}"
70
52
  end
71
53
  end
72
54
 
73
55
  def delete(feature_name)
74
- ensure_table_exists unless @model_class.table_exists?
75
56
  feature_name_str = feature_name.to_s
76
57
  retries = 5
77
58
  begin
78
59
  @model_class.where(feature_name: feature_name_str).destroy_all
79
60
  rescue ::ActiveRecord::StatementInvalid, ::ActiveRecord::ConnectionTimeoutError => e
80
61
  # SQLite busy/locked errors - retry with exponential backoff
81
- if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout') || e.message.include?('no such table')) && retries > 0
62
+ if (e.message.include?('database is locked') || e.message.include?('busy') || e.message.include?('timeout')) && retries > 0
82
63
  retries -= 1
83
- # If it's a "no such table" error, ensure table exists
84
- if e.message.include?('no such table')
85
- ensure_table_exists
86
- end
87
64
  sleep(0.01 * (6 - retries)) # Exponential backoff: 0.01, 0.02, 0.03, 0.04, 0.05
88
65
  retry
89
66
  end
90
67
  raise AdapterError, "Failed to delete from ActiveRecord: #{e.message}"
91
68
  rescue StandardError => e
92
- # If table doesn't exist, try to create it and retry once
93
- if (e.message.include?('no such table') || e.message.include?("doesn't exist")) && retries > 0
94
- retries -= 1
95
- ensure_table_exists
96
- sleep(0.01)
97
- retry
98
- end
99
69
  raise AdapterError, "Failed to delete from ActiveRecord: #{e.message}"
100
70
  end
101
71
  end
@@ -148,66 +118,6 @@ module Magick
148
118
  end)
149
119
  end
150
120
 
151
- def ensure_table_exists
152
- return if @model_class.table_exists?
153
-
154
- # Use a non-blocking mutex to prevent deadlocks
155
- mutex = self.class.instance_variable_get(:@table_created_mutex)
156
- if mutex.try_lock
157
- begin
158
- # Double-check after acquiring lock
159
- return if @model_class.table_exists?
160
-
161
- create_table
162
- self.class.instance_variable_set(:@table_created, true)
163
- ensure
164
- mutex.unlock
165
- end
166
- else
167
- # Another thread is creating the table, wait for it to complete
168
- # Use a longer timeout with exponential backoff to avoid hanging
169
- 20.times do |i|
170
- sleep(0.01 * (i + 1)) # Exponential backoff: 0.01, 0.02, 0.03, ...
171
- return if @model_class.table_exists?
172
- end
173
- # If we still don't have the table, try one more time
174
- unless @model_class.table_exists?
175
- # Last attempt: try to acquire lock and create
176
- if mutex.try_lock
177
- begin
178
- return if @model_class.table_exists?
179
- create_table
180
- self.class.instance_variable_set(:@table_created, true)
181
- ensure
182
- mutex.unlock
183
- end
184
- end
185
- end
186
- end
187
- rescue StandardError => e
188
- # Don't raise if table exists now (might have been created by another thread)
189
- return if @model_class.table_exists?
190
- raise e
191
- end
192
-
193
- def create_table
194
- connection = @model_class.connection
195
- return if connection.table_exists?('magick_features')
196
-
197
- connection.create_table :magick_features do |t|
198
- t.string :feature_name, null: false, index: { unique: true }
199
- t.text :data
200
- t.timestamps
201
- end
202
- rescue StandardError => e
203
- # Table might already exist or migration might be needed
204
- # Check if table exists now (might have been created by another thread)
205
- return if connection.table_exists?('magick_features')
206
-
207
- warn "Magick: Could not create magick_features table: #{e.message}" if defined?(Rails) && Rails.env.development?
208
- raise e if defined?(Rails) && Rails.env.test?
209
- end
210
-
211
121
  def serialize_value(value)
212
122
  # For ActiveRecord 8.1+ with attribute :json, we can store booleans as-is
213
123
  # For older versions with serialize, we convert to strings
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.34'
4
+ VERSION = '0.9.35'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magick-feature-flags
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.34
4
+ version: 0.9.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov