flipper-active_record 0.26.0 → 0.26.1

Sign up to get free protection for your applications and to get access to all the features.
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