flipper-dalli 1.2.2 → 1.3.0.pre

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: 46bffba42a4bebe087a33ed757147ef0abe92bad5bc8417cad5eebd55c083e0b
4
- data.tar.gz: 585bcfdef7495be45d2004c5a57123a0c8841f872cb7802796acbf90b1325aea
3
+ metadata.gz: 8f61a124b7b37fb466805ba6b3116c4358bea6757c0c5b18028ad1f3311d92e4
4
+ data.tar.gz: 5508210d2f06e474a4394da8f73a985f0ec4b11a604bb06b83ab72d6732b7a7c
5
5
  SHA512:
6
- metadata.gz: a99d7deea1e125024a759a91857ce5ed564e68952e1096cd79e288b372d592adebb7f6530119fee3c26d7e266489d58057430b16a7762b252151323a0d79bc19
7
- data.tar.gz: 42874c56819a0ce8ad2061910ae0c11121a9a1173dff6203b5cff45939c7627c574a65fcca17876c2b81b29148fe58d3b4fc4ce04c34fba22927033ce501090d
6
+ metadata.gz: d05e5cacdee0773d5a9ae166d5b84f1256ab04d6946f448fa96a3fab6e9c8d5484f340b94d79bd3d75d335cb50af7169a2a5bcebf0385bed363ae06f737075f0
7
+ data.tar.gz: c2621f5dcaf75bab12072616c57cec73fd5e35c233aa2bc5cd8296ecefcf94a63663a59b7f1c5df1f76b0f4f4001aabe5684492a7c31a15aba3b2ca7ff9a59df
@@ -1,127 +1,32 @@
1
1
  require 'dalli'
2
2
  require 'flipper'
3
+ require 'flipper/adapters/cache_base'
3
4
 
4
5
  module Flipper
5
6
  module Adapters
6
7
  # Public: Adapter that wraps another adapter with the ability to cache
7
8
  # adapter calls in Memcached using the Dalli gem.
8
- class Dalli
9
- include ::Flipper::Adapter
10
-
11
- # Internal
12
- attr_reader :cache
13
-
14
- # Public: The ttl for all cached data.
15
- attr_reader :ttl
16
-
17
- # Public
18
- def initialize(adapter, cache, ttl = 0)
19
- @adapter = adapter
20
- @cache = cache
21
- @ttl = ttl
22
-
23
- @cache_version = 'v1'.freeze
24
- @namespace = "flipper/#{@cache_version}".freeze
25
- @features_key = "#{@namespace}/features".freeze
26
- @get_all_key = "#{@namespace}/get_all".freeze
27
- end
28
-
29
- # Public
30
- def features
31
- read_feature_keys
32
- end
33
-
34
- # Public
35
- def add(feature)
36
- result = @adapter.add(feature)
37
- @cache.delete(@features_key)
38
- result
39
- end
40
-
41
- # Public
42
- def remove(feature)
43
- result = @adapter.remove(feature)
44
- @cache.delete(@features_key)
45
- @cache.delete(key_for(feature.key))
46
- result
47
- end
48
-
49
- # Public
50
- def clear(feature)
51
- result = @adapter.clear(feature)
52
- @cache.delete(key_for(feature.key))
53
- result
54
- end
55
-
56
- # Public
57
- def get(feature)
58
- @cache.fetch(key_for(feature.key), @ttl) do
59
- @adapter.get(feature)
60
- end
61
- end
62
-
63
- def get_multi(features)
64
- read_many_features(features)
65
- end
66
-
67
- def get_all
68
- if @cache.add(@get_all_key, Time.now.to_i, @ttl)
69
- response = @adapter.get_all
70
- response.each do |key, value|
71
- @cache.set(key_for(key), value, @ttl)
72
- end
73
- @cache.set(@features_key, response.keys.to_set, @ttl)
74
- response
75
- else
76
- features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) }
77
- read_many_features(features)
78
- end
79
- end
80
-
81
- # Public
82
- def enable(feature, gate, thing)
83
- result = @adapter.enable(feature, gate, thing)
84
- @cache.delete(key_for(feature.key))
85
- result
86
- end
87
-
88
- # Public
89
- def disable(feature, gate, thing)
90
- result = @adapter.disable(feature, gate, thing)
91
- @cache.delete(key_for(feature.key))
92
- result
9
+ class Dalli < CacheBase
10
+ def initialize(adapter, cache, ttl = 0, prefix: nil)
11
+ super
93
12
  end
94
13
 
95
14
  private
96
15
 
97
- def key_for(key)
98
- "#{@namespace}/feature/#{key}"
16
+ def cache_fetch(key, &block)
17
+ @cache.fetch(key, @ttl, &block)
99
18
  end
100
19
 
101
- def read_feature_keys
102
- @cache.fetch(@features_key, @ttl) { @adapter.features }
20
+ def cache_read_multi(keys)
21
+ @cache.get_multi(keys)
103
22
  end
104
23
 
105
- # Internal: Given an array of features, attempts to read through cache in
106
- # as few network calls as possible.
107
- def read_many_features(features)
108
- keys = features.map { |feature| key_for(feature.key) }
109
- cache_result = @cache.get_multi(keys)
110
- uncached_features = features.reject { |feature| cache_result[key_for(feature.key)] }
111
-
112
- if uncached_features.any?
113
- response = @adapter.get_multi(uncached_features)
114
- response.each do |key, value|
115
- @cache.set(key_for(key), value, @ttl)
116
- cache_result[key_for(key)] = value
117
- end
118
- end
24
+ def cache_write(key, value)
25
+ @cache.set(key, value, @ttl)
26
+ end
119
27
 
120
- result = {}
121
- features.each do |feature|
122
- result[feature.key] = cache_result[key_for(feature.key)]
123
- end
124
- result
28
+ def cache_delete(key)
29
+ @cache.delete(key)
125
30
  end
126
31
  end
127
32
  end
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.2.2'.freeze
2
+ VERSION = '1.3.0.pre'.freeze
3
3
 
4
4
  REQUIRED_RUBY_VERSION = '2.6'.freeze
5
5
  NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
@@ -7,7 +7,7 @@ RSpec.describe Flipper::Adapters::Dalli do
7
7
  Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
8
8
  end
9
9
  let(:cache) { Dalli::Client.new(ENV['MEMCACHED_URL']) }
10
- let(:adapter) { described_class.new(memory_adapter, cache) }
10
+ let(:adapter) { described_class.new(memory_adapter, cache, 10) }
11
11
  let(:flipper) { Flipper.new(adapter) }
12
12
 
13
13
  subject { adapter }
@@ -21,6 +21,72 @@ RSpec.describe Flipper::Adapters::Dalli do
21
21
 
22
22
  it_should_behave_like 'a flipper adapter'
23
23
 
24
+ it "knows ttl" do
25
+ expect(adapter.ttl).to eq(10)
26
+ end
27
+
28
+ it "knows features_cache_key" do
29
+ expect(adapter.features_cache_key).to eq("flipper/v1/features")
30
+ end
31
+
32
+ it "can expire features cache" do
33
+ # cache the features
34
+ adapter.features
35
+ expect(cache.get("flipper/v1/features")).not_to be(nil)
36
+
37
+ # expire cache
38
+ adapter.expire_features_cache
39
+ expect(cache.get("flipper/v1/features")).to be(nil)
40
+ end
41
+
42
+ it "can expire feature cache" do
43
+ # cache the features
44
+ adapter.get(flipper[:stats])
45
+ expect(cache.get("flipper/v1/feature/stats")).not_to be(nil)
46
+
47
+ # expire cache
48
+ adapter.expire_feature_cache("stats")
49
+ expect(cache.get("flipper/v1/feature/stats")).to be(nil)
50
+ end
51
+
52
+ it "can generate feature cache key" do
53
+ expect(adapter.feature_cache_key("stats")).to eq("flipper/v1/feature/stats")
54
+ end
55
+
56
+ context "when using a prefix" do
57
+ let(:adapter) { described_class.new(memory_adapter, cache, 0, prefix: "foo/") }
58
+ it_should_behave_like 'a flipper adapter'
59
+
60
+ it "knows features_cache_key" do
61
+ expect(adapter.features_cache_key).to eq("foo/flipper/v1/features")
62
+ end
63
+
64
+ it "can generate feature cache key" do
65
+ expect(adapter.feature_cache_key("stats")).to eq("foo/flipper/v1/feature/stats")
66
+ end
67
+
68
+ it "uses the prefix for all keys" do
69
+ # check individual feature get cached with prefix
70
+ adapter.get(flipper[:stats])
71
+ expect(cache.get("foo/flipper/v1/feature/stats")).not_to be(nil)
72
+
73
+ # check individual feature expired with prefix
74
+ adapter.remove(flipper[:stats])
75
+ expect(cache.get("foo/flipper/v1/feature/stats")).to be(nil)
76
+
77
+ # enable some stuff
78
+ flipper.enable_percentage_of_actors(:search, 10)
79
+ flipper.enable(:stats)
80
+
81
+ # populate the cache
82
+ adapter.get_all
83
+
84
+ # verify cached with prefix
85
+ expect(cache.get("foo/flipper/v1/feature/search")[:percentage_of_actors]).to eq("10")
86
+ expect(cache.get("foo/flipper/v1/feature/stats")[:boolean]).to eq("true")
87
+ end
88
+ end
89
+
24
90
  describe '#remove' do
25
91
  it 'expires feature' do
26
92
  feature = flipper[:stats]
@@ -68,17 +134,19 @@ RSpec.describe Flipper::Adapters::Dalli do
68
134
  adapter.get_all
69
135
  expect(cache.get("flipper/v1/feature/#{stats.key}")[:boolean]).to eq('true')
70
136
  expect(cache.get("flipper/v1/feature/#{search.key}")[:boolean]).to be(nil)
71
- expect(cache.get("flipper/v1/get_all")).to be_within(2).of(Time.now.to_i)
137
+ expect(cache.get("flipper/v1/features")).to eq(Set["stats", "search"])
72
138
  end
73
139
 
74
140
  it 'returns same result when already cached' do
75
141
  expect(adapter.get_all).to eq(adapter.get_all)
76
142
  end
77
143
 
78
- it 'only invokes one call to wrapped adapter' do
144
+ it 'only invokes two calls to wrapped adapter (for features set and gate data for each feature in set)' do
79
145
  memory_adapter.reset
80
146
  5.times { adapter.get_all }
81
- expect(memory_adapter.count(:get_all)).to eq(1)
147
+ expect(memory_adapter.count(:features)).to eq(1)
148
+ expect(memory_adapter.count(:get_multi)).to eq(1)
149
+ expect(memory_adapter.count).to eq(2)
82
150
  end
83
151
  end
84
152
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-dalli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-19 00:00:00.000000000 Z
11
+ date: 2024-03-14 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: 1.2.2
19
+ version: 1.3.0.pre
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: 1.2.2
26
+ version: 1.3.0.pre
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dalli
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -64,7 +64,7 @@ metadata:
64
64
  homepage_uri: https://www.flippercloud.io
65
65
  source_code_uri: https://github.com/flippercloud/flipper
66
66
  bug_tracker_uri: https://github.com/flippercloud/flipper/issues
67
- changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.2.2
67
+ changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.0.pre
68
68
  post_install_message:
69
69
  rdoc_options: []
70
70
  require_paths: