determinator 2.0.0 → 2.1.0
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +40 -6
- data/lib/determinator.rb +6 -0
- data/lib/determinator/cache/fetch_wrapper.rb +42 -4
- data/lib/determinator/retrieve/http_retriever.rb +35 -0
- data/lib/determinator/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e45f2f0be23112e3ef9836aa58973333a2bb409ac3718d02dcc464f117c4911
|
|
4
|
+
data.tar.gz: 4489393bdd074715f545a1ce35419f0794fb47ed99a72af90f63d8885cf5c4fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9c6833636ff9724dd8c6533d532aee97edac0303168bda759d8e2a693221bd4f9d10c55bde8adcd51d8859d35902892188f023a15482aad054a273b426ff169
|
|
7
|
+
data.tar.gz: ab4692c91ffcceb61018d08c165bf6c3bb7a6873103ffe91c6bf494d64f103a8f9a88e46462291024ea9476a00a9afdcfc32c863ade7807dc112857516e218e4
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -116,6 +116,46 @@ This configures the `Determinator.instance` with:
|
|
|
116
116
|
|
|
117
117
|
You may also want to configure a `determinator` helper method inside your web request scope, see below for more information.
|
|
118
118
|
|
|
119
|
+
### Using over http
|
|
120
|
+
|
|
121
|
+
Using the HttpRetriever will cause a request to be sent to actor tracking every time a feature is checked. The impact of this can be mitigated somewhat by having a short lived memory cache, but we're limited in
|
|
122
|
+
the length of time we can cache for without some way of notifying the cache that an item has changed.
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
faraday_connection = Faraday.new("http://actor-tracking.local") do |conn|
|
|
126
|
+
conn.headers['User-Agent'] = "Determinator - my service name"
|
|
127
|
+
conn.basic_auth('my-service-name', 'actor-tracking-token')
|
|
128
|
+
conn.adapter Faraday.default_adapter
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
Determinator.configure(
|
|
132
|
+
retrieval: Determinator::Retrieve::HttpRetriever.new(
|
|
133
|
+
connection: faraday_connection,
|
|
134
|
+
),
|
|
135
|
+
feature_cache: Determinator::Cache::FetchWrapper.new(
|
|
136
|
+
ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute),
|
|
137
|
+
ActiveSupport::Cache::RedisCacheStore.new
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
In this set up we've got two caches - some limited local cache and a larger redis cache that's shared between instances. The memory cache ensure that we're able to perform determination lookups in tight loops without excessive calls to redis.
|
|
143
|
+
|
|
144
|
+
We don't set a TTL on the redis cache (although we could) because we intend to expire the caches manually when we receive an update from our event bus:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
feature_name = Determinator.retrieval.get_name("http://actor-tracking.local/features/some_feature")
|
|
148
|
+
Determinator.feature_cache.expire(feature_name)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
or in instances where the event bus provides a full feature object with a name it's simply:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
Determinator.feature_cache.expire(deserialized_kafka_feature.name)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This will expire both the limited local cache and the larger shared cache.
|
|
158
|
+
|
|
119
159
|
## Further Usage
|
|
120
160
|
|
|
121
161
|
Once this is done you can ask for a determination like this:
|
|
@@ -154,12 +194,6 @@ determinator.which_variant(:my_experiment_name)
|
|
|
154
194
|
|
|
155
195
|
Check the example Rails app in the `examples` directory for more information on how to make use of this gem.
|
|
156
196
|
|
|
157
|
-
### Routemaster
|
|
158
|
-
|
|
159
|
-
Determinator's [Routemaster](https://github.com/deliveroo/routemaster) integration requires your application to be subscribed to the a `features` topic.
|
|
160
|
-
|
|
161
|
-
The drain must expire the routemaster cache on receipt of events, making use of `Routemaster::Drain::CacheBusting.new` or similar is recommended.
|
|
162
|
-
|
|
163
197
|
### Using Determinator in RSpec
|
|
164
198
|
|
|
165
199
|
* Include the `spec_helper.rb`.
|
data/lib/determinator.rb
CHANGED
|
@@ -7,6 +7,7 @@ require 'determinator/serializers/json'
|
|
|
7
7
|
|
|
8
8
|
module Determinator
|
|
9
9
|
class << self
|
|
10
|
+
attr_reader :feature_cache, :retrieval
|
|
10
11
|
# @param :retrieval [Determinator::Retrieve::Routemaster] A retrieval instance for Features
|
|
11
12
|
# @param :errors [#call, nil] a proc, accepting an error, which will be called with any errors which occur while determinating
|
|
12
13
|
# @param :missing_feature [#call, nil] a proc, accepting a feature name, which will be called any time a feature is requested but isn't available
|
|
@@ -15,6 +16,7 @@ module Determinator
|
|
|
15
16
|
self.on_error(&errors) if errors
|
|
16
17
|
self.on_missing_feature(&missing_feature) if missing_feature
|
|
17
18
|
@feature_cache = feature_cache if feature_cache.respond_to?(:call)
|
|
19
|
+
@retrieval = retrieval
|
|
18
20
|
@instance = Control.new(retrieval: retrieval)
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -89,5 +91,9 @@ module Determinator
|
|
|
89
91
|
|
|
90
92
|
@feature_cache.call(name) { yield }
|
|
91
93
|
end
|
|
94
|
+
|
|
95
|
+
def invalidate_cache(name)
|
|
96
|
+
@feature_cache.expire(name)
|
|
97
|
+
end
|
|
92
98
|
end
|
|
93
99
|
end
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
module Determinator
|
|
2
2
|
module Cache
|
|
3
3
|
class FetchWrapper
|
|
4
|
-
# @param
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
# @param *caches [ActiveSupport::Cache] If a list then the head of the the
|
|
5
|
+
# list should will be checked before the tail. If the head is empty but
|
|
6
|
+
# the tail is not then the head will be filled with the value of the tail.
|
|
7
|
+
def initialize(*caches)
|
|
8
|
+
@caches = caches
|
|
7
9
|
end
|
|
8
10
|
|
|
11
|
+
# Call walks through each cache, returning a value if the item exists in
|
|
12
|
+
# any cache, otherwise popularing each cache with the value of yield.
|
|
9
13
|
def call(feature_name)
|
|
10
|
-
|
|
14
|
+
value = read_and_upfill(feature_name)
|
|
15
|
+
# nil is an acceptable value in the case of a missing feature definition
|
|
16
|
+
return nil if value.nil?
|
|
17
|
+
return value if value != false
|
|
18
|
+
|
|
19
|
+
value_to_write = yield
|
|
20
|
+
@caches.each do |cache|
|
|
21
|
+
cache.write(key(feature_name), value_to_write)
|
|
22
|
+
end
|
|
23
|
+
return value_to_write
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def expire(feature_name)
|
|
27
|
+
@caches.each{ |c| c.delete(key(feature_name)) }
|
|
11
28
|
end
|
|
12
29
|
|
|
13
30
|
private
|
|
@@ -15,6 +32,27 @@ module Determinator
|
|
|
15
32
|
def key(feature_name)
|
|
16
33
|
"determinator:feature_cache:#{feature_name}"
|
|
17
34
|
end
|
|
35
|
+
|
|
36
|
+
# Walks through the list of caches, returning the first stored value.
|
|
37
|
+
#
|
|
38
|
+
# If a value is found in a cache after the first then all caches earlier
|
|
39
|
+
# in that list will be backfilled.
|
|
40
|
+
#
|
|
41
|
+
# @param url [String] a feature name
|
|
42
|
+
# @return [false, nil, Feature] false when no value is found, otherwise
|
|
43
|
+
# the value stored in the cache (including nil)
|
|
44
|
+
def read_and_upfill(feature_name)
|
|
45
|
+
@caches.each.with_index do |cache, index|
|
|
46
|
+
if cache.exist?(key(feature_name))
|
|
47
|
+
value = cache.read(key(feature_name))
|
|
48
|
+
@caches[0...index].each do |cache|
|
|
49
|
+
cache.write(key(feature_name), value)
|
|
50
|
+
end
|
|
51
|
+
return value
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
18
56
|
end
|
|
19
57
|
end
|
|
20
58
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Determinator
|
|
4
|
+
module Retrieve
|
|
5
|
+
|
|
6
|
+
class HttpRetriever
|
|
7
|
+
def initialize(connection:)
|
|
8
|
+
raise ArgumentError, "client must be a Faraday::Connection" unless connection.is_a?(Faraday::Connection)
|
|
9
|
+
@connection = connection
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def retrieve(name)
|
|
13
|
+
begin
|
|
14
|
+
response = @connection.get("/features/#{name}")
|
|
15
|
+
return Determinator::Serializers::JSON.load(response.body) if response.status == 200
|
|
16
|
+
rescue => e
|
|
17
|
+
Determinator.notice_error(e)
|
|
18
|
+
end
|
|
19
|
+
nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns a feature name given a actor-tracking url. Used so we are able
|
|
23
|
+
# to expire a cache using a feature name given an event url.
|
|
24
|
+
#
|
|
25
|
+
# Not intended to be generic, and makes no guarantees about support for
|
|
26
|
+
# alternative url schemes.
|
|
27
|
+
#
|
|
28
|
+
# @param url [String] a actor tracking url
|
|
29
|
+
# @return [String, nil] a feature name or nil
|
|
30
|
+
def get_name(url)
|
|
31
|
+
(url.match('features\/(.*)\z') || [])[1]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/determinator/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: determinator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- JP Hastings-Spital
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-05-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -197,6 +197,7 @@ files:
|
|
|
197
197
|
- lib/determinator/feature.rb
|
|
198
198
|
- lib/determinator/retrieve/dynaconf.rb
|
|
199
199
|
- lib/determinator/retrieve/file.rb
|
|
200
|
+
- lib/determinator/retrieve/http_retriever.rb
|
|
200
201
|
- lib/determinator/retrieve/null_retriever.rb
|
|
201
202
|
- lib/determinator/serializers/json.rb
|
|
202
203
|
- lib/determinator/target_group.rb
|
|
@@ -222,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
222
223
|
version: '0'
|
|
223
224
|
requirements: []
|
|
224
225
|
rubyforge_project:
|
|
225
|
-
rubygems_version: 2.7.
|
|
226
|
+
rubygems_version: 2.7.6
|
|
226
227
|
signing_key:
|
|
227
228
|
specification_version: 4
|
|
228
229
|
summary: Determine which experiments and features a specific actor should see.
|