flipper-active_record 0.26.0 → 0.26.1

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: 96994023a61be27a5bb30418c7bc367c82689ee4f964bb838996b84ff93e4725
4
- data.tar.gz: 3fbd092338493aaa9da024517e35a2daef1c176526858b94bb93ccafa3c876a9
3
+ metadata.gz: 5244fa15983cbfcba71bcf8d555d71d3e9142314d035dc7dad084ea413d0abfd
4
+ data.tar.gz: 810a08e5c66d66765f8a09e5b4554804792200b0fa8084fd38bb12a2db44cb08
5
5
  SHA512:
6
- metadata.gz: 2d1d74510fbba3f6b933c277c7747d2e0057e26aefb472889ef7dc463f80316cd7e8d0a9dd2f4d7916888b2bf40266ecb68dd84889f38efab59b058885d54c2c
7
- data.tar.gz: f885cc037bfd91646b895056b54802ec512d56c1a670c98fa93d67d7c8de3b39bd1e5512ce630fc95e71c1db604ca69430a5aca1cf962d6de322d100c00a4ceb
6
+ metadata.gz: 20d45ea8dc75cbab5be320ea9ca22487f7a15483088c2370d48bb0e1b01e1fbf6cb657f9cd3f17ffd02c4fbeef1e20a80d3f328eb5a29d03961ddbfc87bd1439
7
+ data.tar.gz: 5d3871e886483ee2c9cefa90ef1fb918b4fd795c46121251b62c4c7543868578ba519f6f9547a58a749c87f4840e09496ef9c403f1765b91851be8bfd44c5d6b
@@ -53,18 +53,20 @@ module Flipper
53
53
 
54
54
  # Public: The set of known features.
55
55
  def features
56
- @feature_class.all.map(&:key).to_set
56
+ with_connection(@feature_class) { @feature_class.all.map(&:key).to_set }
57
57
  end
58
58
 
59
59
  # Public: Adds a feature to the set of known features.
60
60
  def add(feature)
61
- # race condition, but add is only used by enable/disable which happen
62
- # super rarely, so it shouldn't matter in practice
63
- @feature_class.transaction do
64
- unless @feature_class.where(key: feature.key).first
65
- begin
66
- @feature_class.create! { |f| f.key = feature.key }
67
- rescue ::ActiveRecord::RecordNotUnique
61
+ with_connection(@feature_class) do
62
+ # race condition, but add is only used by enable/disable which happen
63
+ # super rarely, so it shouldn't matter in practice
64
+ @feature_class.transaction do
65
+ unless @feature_class.where(key: feature.key).first
66
+ begin
67
+ @feature_class.create! { |f| f.key = feature.key }
68
+ rescue ::ActiveRecord::RecordNotUnique
69
+ end
68
70
  end
69
71
  end
70
72
  end
@@ -74,16 +76,18 @@ module Flipper
74
76
 
75
77
  # Public: Removes a feature from the set of known features.
76
78
  def remove(feature)
77
- @feature_class.transaction do
78
- @feature_class.where(key: feature.key).destroy_all
79
- clear(feature)
79
+ with_connection(@feature_class) do
80
+ @feature_class.transaction do
81
+ @feature_class.where(key: feature.key).destroy_all
82
+ clear(feature)
83
+ end
80
84
  end
81
85
  true
82
86
  end
83
87
 
84
88
  # Public: Clears the gate values for a feature.
85
89
  def clear(feature)
86
- @gate_class.where(feature_key: feature.key).destroy_all
90
+ with_connection(@gate_class) { @gate_class.where(feature_key: feature.key).destroy_all }
87
91
  true
88
92
  end
89
93
 
@@ -91,35 +95,39 @@ module Flipper
91
95
  #
92
96
  # Returns a Hash of Flipper::Gate#key => value.
93
97
  def get(feature)
94
- db_gates = @gate_class.where(feature_key: feature.key)
98
+ db_gates = with_connection(@gate_class) { @gate_class.where(feature_key: feature.key) }
95
99
  result_for_feature(feature, db_gates)
96
100
  end
97
101
 
98
102
  def get_multi(features)
99
- db_gates = @gate_class.where(feature_key: features.map(&:key))
100
- grouped_db_gates = db_gates.group_by(&:feature_key)
101
- result = {}
102
- features.each do |feature|
103
- result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
103
+ with_connection(@gate_class) do
104
+ db_gates = @gate_class.where(feature_key: features.map(&:key))
105
+ grouped_db_gates = db_gates.group_by(&:feature_key)
106
+ result = {}
107
+ features.each do |feature|
108
+ result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
109
+ end
110
+ result
104
111
  end
105
- result
106
112
  end
107
113
 
108
114
  def get_all
109
- features = ::Arel::Table.new(@feature_class.table_name.to_sym)
110
- gates = ::Arel::Table.new(@gate_class.table_name.to_sym)
111
- rows_query = features.join(gates, Arel::Nodes::OuterJoin)
112
- .on(features[:key].eq(gates[:feature_key]))
113
- .project(features[:key].as('feature_key'), gates[:key], gates[:value])
114
- rows = @feature_class.connection.select_all rows_query
115
- db_gates = rows.map { |row| @gate_class.new(row) }
116
- grouped_db_gates = db_gates.group_by(&:feature_key)
117
- result = Hash.new { |hash, key| hash[key] = default_config }
118
- features = grouped_db_gates.keys.map { |key| Flipper::Feature.new(key, self) }
119
- features.each do |feature|
120
- result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
115
+ with_connection(@feature_class) do
116
+ features = ::Arel::Table.new(@feature_class.table_name.to_sym)
117
+ gates = ::Arel::Table.new(@gate_class.table_name.to_sym)
118
+ rows_query = features.join(gates, Arel::Nodes::OuterJoin)
119
+ .on(features[:key].eq(gates[:feature_key]))
120
+ .project(features[:key].as('feature_key'), gates[:key], gates[:value])
121
+ rows = @feature_class.connection.select_all rows_query
122
+ db_gates = rows.map { |row| @gate_class.new(row) }
123
+ grouped_db_gates = db_gates.group_by(&:feature_key)
124
+ result = Hash.new { |hash, key| hash[key] = default_config }
125
+ features = grouped_db_gates.keys.map { |key| Flipper::Feature.new(key, self) }
126
+ features.each do |feature|
127
+ result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key])
128
+ end
129
+ result
121
130
  end
122
- result
123
131
  end
124
132
 
125
133
  # Public: Enables a gate for a given thing.
@@ -158,7 +166,9 @@ module Flipper
158
166
  when :integer
159
167
  set(feature, gate, thing)
160
168
  when :set
161
- @gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).destroy_all
169
+ with_connection(@gate_class) do
170
+ @gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).destroy_all
171
+ end
162
172
  else
163
173
  unsupported_data_type gate.data_type
164
174
  end
@@ -175,18 +185,20 @@ module Flipper
175
185
 
176
186
  def set(feature, gate, thing, options = {})
177
187
  clear_feature = options.fetch(:clear, false)
178
- @gate_class.transaction do
179
- clear(feature) if clear_feature
180
- @gate_class.where(feature_key: feature.key, key: gate.key).destroy_all
181
- begin
182
- @gate_class.create! do |g|
183
- g.feature_key = feature.key
184
- g.key = gate.key
185
- g.value = thing.value.to_s
188
+ with_connection(@gate_class) do
189
+ @gate_class.transaction do
190
+ clear(feature) if clear_feature
191
+ @gate_class.where(feature_key: feature.key, key: gate.key).destroy_all
192
+ begin
193
+ @gate_class.create! do |g|
194
+ g.feature_key = feature.key
195
+ g.key = gate.key
196
+ g.value = thing.value.to_s
197
+ end
198
+ rescue ::ActiveRecord::RecordNotUnique
199
+ # assume this happened concurrently with the same thing and its fine
200
+ # see https://github.com/jnunemaker/flipper/issues/544
186
201
  end
187
- rescue ::ActiveRecord::RecordNotUnique
188
- # assume this happened concurrently with the same thing and its fine
189
- # see https://github.com/jnunemaker/flipper/issues/544
190
202
  end
191
203
  end
192
204
 
@@ -194,10 +206,12 @@ module Flipper
194
206
  end
195
207
 
196
208
  def enable_multi(feature, gate, thing)
197
- @gate_class.create! do |g|
198
- g.feature_key = feature.key
199
- g.key = gate.key
200
- g.value = thing.value.to_s
209
+ with_connection(@gate_class) do
210
+ @gate_class.create! do |g|
211
+ g.feature_key = feature.key
212
+ g.key = gate.key
213
+ g.value = thing.value.to_s
214
+ end
201
215
  end
202
216
 
203
217
  nil
@@ -227,6 +241,10 @@ module Flipper
227
241
  end
228
242
  result
229
243
  end
244
+
245
+ def with_connection(model = @feature_class, &block)
246
+ model.connection_pool.with_connection(&block)
247
+ end
230
248
  end
231
249
  end
232
250
  end
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.26.0'.freeze
2
+ VERSION = '0.26.1'.freeze
3
3
  end
@@ -57,4 +57,126 @@ RSpec.describe Flipper::Adapters::ActiveRecord do
57
57
  expect(Flipper.adapter.adapter).to be_a(Flipper::Adapters::ActiveRecord)
58
58
  end
59
59
  end
60
+
61
+ context "ActiveRecord connection_pool" do
62
+ before do
63
+ ActiveRecord::Base.clear_active_connections!
64
+ end
65
+
66
+ context "#features" do
67
+ it "does not hold onto connections" do
68
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
69
+ subject.features
70
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
71
+ end
72
+
73
+ it "does not release previously held connection" do
74
+ ActiveRecord::Base.connection # establish a new connection
75
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
76
+ subject.features
77
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
78
+ end
79
+ end
80
+
81
+ context "#get_all" do
82
+ it "does not hold onto connections" do
83
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
84
+ subject.get_all
85
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
86
+ end
87
+
88
+ it "does not release previously held connection" do
89
+ ActiveRecord::Base.connection # establish a new connection
90
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
91
+ subject.get_all
92
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
93
+ end
94
+ end
95
+
96
+ context "#add / #remove / #clear" do
97
+ let(:feature) { Flipper::Feature.new(:search, subject) }
98
+
99
+ it "does not hold onto connections" do
100
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
101
+ subject.add(feature)
102
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
103
+ subject.remove(feature)
104
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
105
+ subject.clear(feature)
106
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
107
+ end
108
+
109
+ it "does not release previously held connection" do
110
+ ActiveRecord::Base.connection # establish a new connection
111
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
112
+ subject.add(feature)
113
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
114
+ subject.remove(feature)
115
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
116
+ subject.clear(feature)
117
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
118
+ end
119
+ end
120
+
121
+ context "#get_multi" do
122
+ let(:feature) { Flipper::Feature.new(:search, subject) }
123
+
124
+ it "does not hold onto connections" do
125
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
126
+ subject.get_multi([feature])
127
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
128
+ end
129
+
130
+ it "does not release previously held connection" do
131
+ ActiveRecord::Base.connection # establish a new connection
132
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
133
+ subject.get_multi([feature])
134
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
135
+ end
136
+ end
137
+
138
+ context "#enable/#disable boolean" do
139
+ let(:feature) { Flipper::Feature.new(:search, subject) }
140
+ let(:gate) { feature.gate(:boolean)}
141
+
142
+ it "does not hold onto connections" do
143
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
144
+ subject.enable(feature, gate, gate.wrap(true))
145
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
146
+ subject.disable(feature, gate, gate.wrap(false))
147
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
148
+ end
149
+
150
+ it "does not release previously held connection" do
151
+ ActiveRecord::Base.connection # establish a new connection
152
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
153
+ subject.enable(feature, gate, gate.wrap(true))
154
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
155
+ subject.disable(feature, gate, gate.wrap(false))
156
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
157
+ end
158
+ end
159
+
160
+ context "#enable/#disable set" do
161
+ let(:feature) { Flipper::Feature.new(:search, subject) }
162
+ let(:gate) { feature.gate(:group) }
163
+
164
+ it "does not hold onto connections" do
165
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
166
+ subject.enable(feature, gate, gate.wrap(:admin))
167
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
168
+ subject.disable(feature, gate, gate.wrap(:admin))
169
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false)
170
+ end
171
+
172
+ it "does not release previously held connection" do
173
+ ActiveRecord::Base.connection # establish a new connection
174
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
175
+ subject.enable(feature, gate, gate.wrap(:admin))
176
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
177
+ subject.disable(feature, gate, gate.wrap(:admin))
178
+ expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true)
179
+ end
180
+ end
181
+ end
60
182
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-active_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.0
4
+ version: 0.26.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-05 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flipper
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.26.0
19
+ version: 0.26.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.26.0
26
+ version: 0.26.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement