prefab-cloud-ruby 0.0.8 → 0.0.9

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
  SHA1:
3
- metadata.gz: 4b7a0a43eadc51e6ebfbd7573865ba48dd139bd1
4
- data.tar.gz: 30e99adf8c06969e29034fbff3a711838b471e0a
3
+ metadata.gz: bedbb49cbc2b12ed093d5740ddbcfa26398faefe
4
+ data.tar.gz: fcd3d2e87c37927243f2ad475e0564d3a99eecc0
5
5
  SHA512:
6
- metadata.gz: 29262f1a91dd5c7322152e27fe4352c4a6974f17d891f29bdaed79aedda4c258c13ba4576337936d6b5fcf7b22a3a0eb793e1a4ce9b1b1185dac95e9f88bb352
7
- data.tar.gz: cf12cc1441b1a8786f40cf309128129570fe061b9eb41a1c59ed8cc0159a54376cd3711809e4e76f52edefecccb5409b59c3d37ce7997bd38d1ee5da7801c1ad
6
+ metadata.gz: b4cf667a4d6495d12a19af12018f50d423b8dd5b32dcbe0b95fcbfbded7ddfa127ae1d804d3fc44db7df5bb4e2a6c3d8c4d25255b9e274b7e4a469b629b5ed08
7
+ data.tar.gz: ad6a8ffb65d448f35a7276910cc36825491f4ee467bd7fd02e4486f1ee68fe1d27d28848a2c9c8be3189aa8339662a83b64960a2b7c5a255574634c656cbca00
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.0.9
@@ -8,8 +8,10 @@ require 'prefab/config_resolver'
8
8
  require 'prefab/client'
9
9
  require 'prefab/ratelimit_client'
10
10
  require 'prefab/config_client'
11
+ require 'prefab/feature_flag_client'
11
12
  require 'prefab/auth_interceptor'
12
13
  require 'prefab/noop_cache'
13
14
  require 'prefab/noop_stats'
14
15
  require 'prefab/retry'
16
+ require 'prefab/murmer3'
15
17
 
data/lib/prefab/client.rb CHANGED
@@ -34,6 +34,10 @@ module Prefab
34
34
  @ratelimit_client ||= Prefab::RateLimitClient.new(self, timeout)
35
35
  end
36
36
 
37
+ def feature_flag_client
38
+ @feature_flag_client ||= Prefab::FeatureFlagClient.new(self)
39
+ end
40
+
37
41
  private
38
42
 
39
43
  def ssl_certs
@@ -2,10 +2,10 @@ module Prefab
2
2
  class ConfigClient
3
3
  RECONNECT_WAIT = 5
4
4
 
5
- def initialize(client, timeout)
6
- @client = client
5
+ def initialize(base_client, timeout)
6
+ @base_client = base_client
7
7
  @timeout = timeout
8
- @config_resolver = EzConfig::ConfigResolver.new(client)
8
+ @config_resolver = EzConfig::ConfigResolver.new(base_client)
9
9
  boot_resolver
10
10
  end
11
11
 
@@ -15,6 +15,7 @@ module Prefab
15
15
 
16
16
  def set(config_delta)
17
17
  Retry.it method(:stub_with_timout), :upsert, config_delta, @timeout
18
+ @config_resolver.set(config_delta)
18
19
  end
19
20
 
20
21
  def to_s
@@ -26,20 +27,20 @@ module Prefab
26
27
  def stub
27
28
  Prefab::ConfigService::Stub.new(nil,
28
29
  nil,
29
- channel_override: @client.channel,
30
- interceptors: [@client.interceptor])
30
+ channel_override: @base_client.channel,
31
+ interceptors: [@base_client.interceptor])
31
32
  end
32
33
 
33
34
  def stub_with_timout
34
35
  Prefab::ConfigService::Stub.new(nil,
35
36
  nil,
36
- channel_override: @client.channel,
37
+ channel_override: @base_client.channel,
37
38
  timeout: @timeout,
38
- interceptors: [@client.interceptor])
39
+ interceptors: [@base_client.interceptor])
39
40
  end
40
41
 
41
42
  def boot_resolver
42
- config_req = Prefab::ConfigServicePointer.new(account_id: @client.account_id,
43
+ config_req = Prefab::ConfigServicePointer.new(account_id: @base_client.account_id,
43
44
  start_at_id: 0)
44
45
 
45
46
  Thread.new do
@@ -48,13 +49,13 @@ module Prefab
48
49
  resp = stub.get_config(config_req)
49
50
  resp.each do |r|
50
51
  r.deltas.each do |delta|
51
- @config_resolver.set(delta)
52
+ @config_resolver.set(delta, do_update: false)
52
53
  end
53
54
  @config_resolver.update
54
55
  end
55
56
  rescue => e
56
57
  sleep(RECONNECT_WAIT)
57
- @client.logger.info("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
58
+ @base_client.logger.info("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
58
59
  end
59
60
  end
60
61
  end
@@ -15,31 +15,22 @@ module EzConfig
15
15
  @lock.with_read_lock do
16
16
  @local_store.each do |k, v|
17
17
  value = v[:value]
18
- case value.type
19
- when :string then
20
- str << "#{k} #{value.string}"
21
- when :int then
22
- str << "#{k} #{value.int}"
23
- end
18
+ str << "|#{k}| |#{value_of(value)}|\n"
24
19
  end
25
20
  end
26
21
  str
27
22
  end
28
23
 
29
24
  def get(property)
30
- value = @lock.with_read_lock do
31
- @local_store[property][:value]
32
- end
33
- case value.type
34
- when :string then
35
- value.string
36
- when :int then
37
- value.int
25
+ config = @lock.with_read_lock do
26
+ @local_store[property]
38
27
  end
28
+ config ? value_of(config[:value]) : nil
39
29
  end
40
30
 
41
- def set(delta)
31
+ def set(delta, do_update: true)
42
32
  @config_loader.set(delta)
33
+ update if do_update
43
34
  end
44
35
 
45
36
  def update
@@ -48,6 +39,21 @@ module EzConfig
48
39
 
49
40
  private
50
41
 
42
+ def value_of(config_value)
43
+ case config_value.type
44
+ when :string
45
+ config_value.string
46
+ when :int
47
+ config_value.int
48
+ when :double
49
+ config_value.double
50
+ when :bool
51
+ config_value.bool
52
+ when :feature_flag
53
+ config_value.feature_flag
54
+ end
55
+ end
56
+
51
57
  def make_local
52
58
  store = {}
53
59
  @config_loader.calc_config.each do |prop, value|
@@ -72,8 +78,6 @@ module EzConfig
72
78
  @lock.with_write_lock do
73
79
  @local_store = store
74
80
  end
75
-
76
- @logger.info "Updated to #{to_s}"
77
81
  end
78
82
  end
79
83
  end
@@ -0,0 +1,54 @@
1
+ module Prefab
2
+ class FeatureFlagClient
3
+ MAX_32_FLOAT = 4294967294.0
4
+
5
+ def initialize(base_client)
6
+ @base_client = base_client
7
+ end
8
+
9
+ def upsert(feature_obj)
10
+ delta = Prefab::ConfigDelta.new(account_id: @base_client.account_id,
11
+ key: feature_config_name(feature_obj.feature),
12
+ value: Prefab::ConfigValue.new(feature_flag: feature_obj))
13
+ @base_client.config_client.set(delta)
14
+ end
15
+
16
+ def feature_is_on?(feature_name)
17
+ feature_is_on_for?(feature_name, nil)
18
+ end
19
+
20
+ def feature_is_on_for?(feature_name, lookup_key, attributes: [])
21
+ @base_client.stats.increment("prefab.featureflag.on", tags: ["feature:#{feature_name}"])
22
+
23
+ feature_obj = @base_client.config_client.get(feature_config_name(feature_name))
24
+ if feature_obj.nil?
25
+ return false
26
+ end
27
+
28
+ attributes << lookup_key if lookup_key
29
+ if (attributes & feature_obj.whitelisted).size > 0
30
+ return true
31
+ end
32
+
33
+ if lookup_key
34
+ return get_user_pct(feature_name, lookup_key) < feature_obj.pct
35
+ end
36
+
37
+ return feature_obj.pct > rand()
38
+ end
39
+
40
+ private
41
+
42
+
43
+ def get_user_pct(feature, lookup_key)
44
+ int_value = Murmur3.murmur3_32("#{@account_id}#{feature}#{lookup_key}")
45
+ int_value / MAX_32_FLOAT
46
+ end
47
+
48
+ def feature_config_name(feature)
49
+ "Feature.#{feature}"
50
+ end
51
+
52
+ end
53
+ end
54
+
@@ -0,0 +1,50 @@
1
+ class Murmur3
2
+ ## MurmurHash3 was written by Austin Appleby, and is placed in the public
3
+ ## domain. The author hereby disclaims copyright to this source code.
4
+
5
+ MASK32 = 0xffffffff
6
+
7
+ def self.murmur3_32_rotl(x, r)
8
+ ((x << r) | (x >> (32 - r))) & MASK32
9
+ end
10
+
11
+
12
+ def self.murmur3_32_fmix(h)
13
+ h &= MASK32
14
+ h ^= h >> 16
15
+ h = (h * 0x85ebca6b) & MASK32
16
+ h ^= h >> 13
17
+ h = (h * 0xc2b2ae35) & MASK32
18
+ h ^ (h >> 16)
19
+ end
20
+
21
+ def self.murmur3_32__mmix(k1)
22
+ k1 = (k1 * 0xcc9e2d51) & MASK32
23
+ k1 = murmur3_32_rotl(k1, 15)
24
+ (k1 * 0x1b873593) & MASK32
25
+ end
26
+
27
+ def self.murmur3_32(str, seed=0)
28
+ h1 = seed
29
+ numbers = str.unpack('V*C*')
30
+ tailn = str.length % 4
31
+ tail = numbers.slice!(numbers.size - tailn, tailn)
32
+ for k1 in numbers
33
+ h1 ^= murmur3_32__mmix(k1)
34
+ h1 = murmur3_32_rotl(h1, 13)
35
+ h1 = (h1*5 + 0xe6546b64) & MASK32
36
+ end
37
+
38
+ unless tail.empty?
39
+ k1 = 0
40
+ tail.reverse_each do |c1|
41
+ k1 = (k1 << 8) | c1
42
+ end
43
+ h1 ^= murmur3_32__mmix(k1)
44
+ end
45
+
46
+ h1 ^= str.length
47
+ murmur3_32_fmix(h1)
48
+ end
49
+
50
+ end
@@ -1,9 +1,9 @@
1
1
  module Prefab
2
2
  class RateLimitClient
3
3
 
4
- def initialize(client, timeout)
4
+ def initialize(base_client, timeout)
5
5
  @timeout = timeout
6
- @client = client
6
+ @base_client = base_client
7
7
  end
8
8
 
9
9
  def pass?(group)
@@ -13,14 +13,14 @@ module Prefab
13
13
 
14
14
  def acquire(groups, acquire_amount, allow_partial_response: false, on_error: :log_and_pass)
15
15
  expiry_cache_key = "prefab.ratelimit.expiry:#{groups.join(".")}"
16
- expiry = @client.shared_cache.read(expiry_cache_key)
16
+ expiry = @base_client.shared_cache.read(expiry_cache_key)
17
17
  if !expiry.nil? && Integer(expiry) > Time.now.utc.to_f * 1000
18
- @client.stats.increment("prefab.ratelimit.limitcheck.expirycache.hit", tags: [])
18
+ @base_client.stats.increment("prefab.ratelimit.limitcheck.expirycache.hit", tags: [])
19
19
  return Prefab::LimitResponse.new(passed: false, amount: 0)
20
20
  end
21
21
 
22
22
  req = Prefab::LimitRequest.new(
23
- account_id: @client.account_id,
23
+ account_id: @base_client.account_id,
24
24
  acquire_amount: acquire_amount,
25
25
  groups: groups,
26
26
  allow_partial_response: allow_partial_response
@@ -29,9 +29,9 @@ module Prefab
29
29
  result = Retry.it(method(:stub), :limit_check, req, @timeout)
30
30
 
31
31
  reset = result.limit_reset_at
32
- @client.shared_cache.write(expiry_cache_key, reset) unless reset < 1 # protobuf default int to 0
32
+ @base_client.shared_cache.write(expiry_cache_key, reset) unless reset < 1 # protobuf default int to 0
33
33
 
34
- @client.stats.increment("prefab.ratelimit.limitcheck", tags: ["policy_group:#{result.policy_group}", "pass:#{result.passed}"])
34
+ @base_client.stats.increment("prefab.ratelimit.limitcheck", tags: ["policy_group:#{result.policy_group}", "pass:#{result.passed}"])
35
35
 
36
36
  result
37
37
 
@@ -44,21 +44,21 @@ module Prefab
44
44
  def stub
45
45
  Prefab::RateLimitService::Stub.new(nil,
46
46
  nil,
47
- channel_override: @client.channel,
47
+ channel_override: @base_client.channel,
48
48
  timeout: @timeout,
49
- interceptors: [@client.interceptor])
49
+ interceptors: [@base_client.interceptor])
50
50
  end
51
51
 
52
52
  def handle_error(e, on_error, groups)
53
- @client.stats.increment("prefab.ratelimit.error", tags: ["type:limit"])
53
+ @base_client.stats.increment("prefab.ratelimit.error", tags: ["type:limit"])
54
54
 
55
55
  message = "ratelimit for #{groups} error: #{e.message}"
56
56
  case on_error
57
57
  when :log_and_pass
58
- @client.logger.warn(message)
58
+ @base_client.logger.warn(message)
59
59
  Prefab::LimitResponse.new(passed: true, amount: 0)
60
60
  when :log_and_hit
61
- @client.logger.warn(message)
61
+ @base_client.logger.warn(message)
62
62
  Prefab::LimitResponse.new(passed: false, amount: 0)
63
63
  when :throw
64
64
  raise e
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: prefab-cloud-ruby 0.0.8 ruby lib
5
+ # stub: prefab-cloud-ruby 0.0.9 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.0.8"
9
+ s.version = "0.0.9"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
@@ -31,6 +31,8 @@ Gem::Specification.new do |s|
31
31
  "lib/prefab/config_client.rb",
32
32
  "lib/prefab/config_loader.rb",
33
33
  "lib/prefab/config_resolver.rb",
34
+ "lib/prefab/feature_flag_client.rb",
35
+ "lib/prefab/murmer3.rb",
34
36
  "lib/prefab/noop_cache.rb",
35
37
  "lib/prefab/noop_stats.rb",
36
38
  "lib/prefab/ratelimit_client.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prefab-cloud-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
@@ -148,6 +148,8 @@ files:
148
148
  - lib/prefab/config_client.rb
149
149
  - lib/prefab/config_loader.rb
150
150
  - lib/prefab/config_resolver.rb
151
+ - lib/prefab/feature_flag_client.rb
152
+ - lib/prefab/murmer3.rb
151
153
  - lib/prefab/noop_cache.rb
152
154
  - lib/prefab/noop_stats.rb
153
155
  - lib/prefab/ratelimit_client.rb