prefab-cloud-ruby 0.23.2 → 0.23.4

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: 6e93ba84e2ea630c852605f1bf814b5388dcaa8fd83025402d2dbc9afa8340cc
4
- data.tar.gz: 8a751a537cbdf658015630b26e5cdc4d284bcc018c702712e60148e68890f0c0
3
+ metadata.gz: 74e19eed1d40cad8e3ea9b99a8755c90864a97b4a00e08d2df6a9fe5226f5ad0
4
+ data.tar.gz: 4ad94565a39139d01795007077128d8f997c05e6a8b785e019ca8b66308df5c5
5
5
  SHA512:
6
- metadata.gz: f460a0d55982126a5fb1591d380936355ab175e93963dddf18aba9a5b6c2f599d36784afc6afe85aef01870d3069d3aab1cb52967a1a508f2d76aa6287b3599f
7
- data.tar.gz: 1ce748c099c1414b98ca4476ec392e46068984196f4d6b261b6ee3acf696b86ae237ff63121a9a5050b500bd53fc957aadea7cec9d586cde75a3b8c4e543967c
6
+ metadata.gz: 7a10679fe049175ebaf01f12194f91bea1a98ce226bf0e0f3e87aea3ab299c888fb7cebfe67b942e77dc46367c9b05cc5acaf995870e1d6ee1f2b8aa744f2fa3
7
+ data.tar.gz: c6e506da285ae5f7913f81891e7f32a1b5365f1071bc90323f317aa2523fddb0bd41c2d8a53decf2afec000e234d535713e198f3ac8b4c98259c7904f03ce7ad
data/Gemfile CHANGED
@@ -4,14 +4,12 @@ gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
4
4
  gem 'faraday'
5
5
  gem 'googleapis-common-protos-types', platforms: :ruby
6
6
  gem 'google-protobuf', platforms: :ruby
7
- gem 'grpc', platforms: :ruby
8
7
  gem 'ld-eventsource'
9
8
  gem 'uuid'
10
9
 
11
10
  group :development do
12
11
  gem 'benchmark-ips'
13
12
  gem 'bundler'
14
- gem 'grpc-tools', platforms: :ruby
15
13
  gem 'juwelier', '~> 2.4.9'
16
14
  gem 'rdoc'
17
15
  gem 'simplecov', '>= 0'
data/Gemfile.lock CHANGED
@@ -33,10 +33,6 @@ GEM
33
33
  google-protobuf (3.22.2)
34
34
  googleapis-common-protos-types (1.5.0)
35
35
  google-protobuf (~> 3.14)
36
- grpc (1.53.0)
37
- google-protobuf (~> 3.21)
38
- googleapis-common-protos-types (~> 1.0)
39
- grpc-tools (1.43.1)
40
36
  hashie (3.6.0)
41
37
  highline (2.0.3)
42
38
  http (5.0.1)
@@ -124,8 +120,6 @@ DEPENDENCIES
124
120
  faraday
125
121
  google-protobuf
126
122
  googleapis-common-protos-types
127
- grpc
128
- grpc-tools
129
123
  juwelier (~> 2.4.9)
130
124
  ld-eventsource
131
125
  minitest
data/README.md CHANGED
@@ -28,9 +28,7 @@ See full documentation https://docs.prefab.cloud/docs/ruby-sdk/ruby
28
28
 
29
29
  ## Important note about Forking and realtime updates
30
30
 
31
- Many ruby web servers fork. GRPC does not like to be forked. See some details on GRPC and forking: https://github.com/grpc/grpc/issues/7951#issuecomment-335998583
32
-
33
- If you're using Puma or Unicorn, you can do the following.
31
+ Many ruby web servers fork. When the process is forked, the current realtime update stream is disconnected. If you're using Puma or Unicorn, do the following.
34
32
 
35
33
  ```ruby
36
34
  #config/initializers/prefab.rb
data/Rakefile CHANGED
@@ -17,7 +17,7 @@ Juwelier::Tasks.new do |gem|
17
17
  gem.homepage = 'http://github.com/prefab-cloud/prefab-cloud-ruby'
18
18
  gem.license = 'MIT'
19
19
  gem.summary = %(Prefab Ruby Infrastructure)
20
- gem.description = %(RateLimits & Config as a service)
20
+ gem.description = %(Feature Flags, Live Config, and Dynamic Log Levels as a service)
21
21
  gem.email = 'jdwyer@prefab.cloud'
22
22
  gem.authors = ['Jeff Dwyer']
23
23
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.2
1
+ 0.23.4
data/lib/prefab/client.rb CHANGED
@@ -31,14 +31,8 @@ module Prefab
31
31
  @api_key = @options.api_key
32
32
  raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
33
33
 
34
- @interceptor = Prefab::AuthInterceptor.new(@api_key)
35
34
  @prefab_api_url = @options.prefab_api_url
36
- @prefab_grpc_url = @options.prefab_grpc_url
37
- log_internal ::Logger::INFO,
38
- "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
39
- at_exit do
40
- channel.destroy
41
- end
35
+ log_internal ::Logger::INFO, "Prefab Connecting to: #{@prefab_api_url}"
42
36
  end
43
37
  # start config client
44
38
  config_client
@@ -54,19 +48,10 @@ module Prefab
54
48
  Thread.current[:prefab_log_properties] = {}
55
49
  end
56
50
 
57
- def channel
58
- credentials = http_secure? ? creds : :this_channel_is_insecure
59
- @_channel ||= GRPC::Core::Channel.new(@prefab_grpc_url, nil, credentials)
60
- end
61
-
62
51
  def config_client(timeout: 5.0)
63
52
  @config_client ||= Prefab::ConfigClient.new(self, timeout)
64
53
  end
65
54
 
66
- def ratelimit_client(timeout: 5.0)
67
- @ratelimit_client ||= Prefab::RateLimitClient.new(self, timeout)
68
- end
69
-
70
55
  def feature_flag_client
71
56
  @feature_flag_client ||= Prefab::FeatureFlagClient.new(self)
72
57
  end
@@ -97,39 +82,6 @@ module Prefab
97
82
  log.log_internal msg, path, nil, level
98
83
  end
99
84
 
100
- def request(service, method, req_options: {}, params: {})
101
- # Future-proofing since we previously bumped into a conflict with a service with a `send` method
102
- raise ArgumentError, 'Cannot call public_send on an grpc service in Ruby' if method.to_s == 'public_send'
103
-
104
- opts = { timeout: 10 }.merge(req_options)
105
-
106
- attempts = 0
107
- start_time = Time.now
108
-
109
- begin
110
- attempts += 1
111
-
112
- stub_for(service, opts[:timeout]).public_send(method, *params)
113
- rescue StandardError => e
114
- log_internal ::Logger::WARN, e
115
-
116
- raise e if Time.now - start_time > opts[:timeout]
117
-
118
- sleep_seconds = [BASE_SLEEP_SEC * (2**(attempts - 1)), MAX_SLEEP_SEC].min
119
- sleep_seconds *= (0.5 * (1 + rand))
120
- sleep_seconds = [BASE_SLEEP_SEC, sleep_seconds].max
121
- log_internal ::Logger::INFO, "Sleep #{sleep_seconds} and Reset #{service} #{method}"
122
- sleep sleep_seconds
123
- reset!
124
- retry
125
- end
126
- end
127
-
128
- def reset!
129
- @stubs.clear
130
- @_channel = nil
131
- end
132
-
133
85
  def enabled?(feature_name, lookup_key = nil, attributes = {})
134
86
  feature_flag_client.feature_is_on_for?(feature_name, lookup_key, attributes: attributes)
135
87
  end
@@ -142,6 +94,10 @@ module Prefab
142
94
  end
143
95
  end
144
96
 
97
+ def post(path, body)
98
+ Prefab::HttpConnection.new(@options.prefab_api_url, @api_key).post(path, body)
99
+ end
100
+
145
101
  private
146
102
 
147
103
  def is_ff?(key)
@@ -149,35 +105,5 @@ module Prefab
149
105
 
150
106
  raw && raw.allowable_values.any?
151
107
  end
152
-
153
- def http_secure?
154
- ENV['PREFAB_CLOUD_HTTP'] != 'true'
155
- end
156
-
157
- def stub_for(service, timeout)
158
- @stubs["#{service}_#{timeout}"] ||= service::Stub.new(nil,
159
- nil,
160
- timeout: timeout,
161
- channel_override: channel,
162
- interceptors: [@interceptor])
163
- end
164
-
165
- def creds
166
- GRPC::Core::ChannelCredentials.new(ssl_certs)
167
- end
168
-
169
- def ssl_certs
170
- ssl_certs = ''
171
- Dir["#{OpenSSL::X509::DEFAULT_CERT_DIR}/*.pem"].each do |cert|
172
- ssl_certs += File.open(cert).read
173
- end
174
- if OpenSSL::X509::DEFAULT_CERT_FILE && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
175
- ssl_certs += File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
176
- end
177
- ssl_certs
178
- rescue StandardError => e
179
- log.warn("Issue loading SSL certs #{e.message}")
180
- ssl_certs
181
- end
182
108
  end
183
109
  end
@@ -26,8 +26,6 @@ module Prefab
26
26
  @base_client.log_internal ::Logger::DEBUG, 'Initialize ConfigClient: AcquiredWriteLock'
27
27
  @initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
28
28
 
29
- @cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
30
-
31
29
  if @options.local_only?
32
30
  finish_init!(:local_only)
33
31
  else
@@ -58,11 +56,6 @@ module Prefab
58
56
  @config_resolver.update
59
57
  end
60
58
 
61
- def reset
62
- @base_client.reset!
63
- @_stub = nil
64
- end
65
-
66
59
  def to_s
67
60
  @config_resolver.to_s
68
61
  end
@@ -108,54 +101,28 @@ module Prefab
108
101
  @config_resolver.get(key, lookup_key, properties)
109
102
  end
110
103
 
111
- def stub
112
- @_stub = Prefab::ConfigService::Stub.new(nil,
113
- nil,
114
- channel_override: @base_client.channel,
115
- interceptors: [@base_client.interceptor, @cancellable_interceptor])
116
- end
117
-
118
104
  def load_checkpoint
119
105
  success = load_checkpoint_api_cdn
120
106
 
121
107
  return if success
122
108
 
123
- @base_client.log_internal ::Logger::INFO, 'LoadCheckpoint: Fallback to GRPC API'
124
-
125
- success = load_checkpoint_from_grpc_api
109
+ success = load_checkpoint_api
126
110
 
127
111
  return if success
128
112
 
129
113
  @base_client.log_internal ::Logger::WARN, 'No success loading checkpoints'
130
114
  end
131
115
 
132
- def load_checkpoint_from_grpc_api
133
- config_req = Prefab::ConfigServicePointer.new(start_at_id: @config_loader.highwater_mark)
134
-
135
- resp = stub.get_all_config(config_req)
136
- load_configs(resp, :remote_api_grpc)
137
- true
138
- rescue GRPC::Unauthenticated
139
- @base_client.log_internal ::Logger::WARN, 'Unauthenticated'
140
- rescue StandardError => e
141
- @base_client.log_internal ::Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
142
- false
143
- end
144
-
145
116
  def load_checkpoint_api_cdn
146
- url = "#{@options.url_for_api_cdn}/api/v1/configs/0"
147
- conn = if Faraday::VERSION[0].to_i >= 2
148
- Faraday.new(url) do |conn|
149
- conn.request :authorization, :basic, AUTH_USER, @base_client.api_key
150
- end
151
- else
152
- Faraday.new(url) do |conn|
153
- conn.request :basic_auth, AUTH_USER, @base_client.api_key
154
- end
155
- end
117
+ conn = Prefab::HttpConnection.new("#{@options.url_for_api_cdn}/api/v1/configs/0", @base_client.api_key)
156
118
  load_url(conn, :remote_cdn_api)
157
119
  end
158
120
 
121
+ def load_checkpoint_api
122
+ conn = Prefab::HttpConnection.new("#{@options.prefab_api_url}/api/v1/configs/0", @base_client.api_key)
123
+ load_url(conn, :remote_api)
124
+ end
125
+
159
126
  def load_url(conn, source)
160
127
  resp = conn.get('')
161
128
  if resp.status == 200
@@ -0,0 +1,16 @@
1
+ module Prefab
2
+ class ExponentialBackoff
3
+ def initialize(max_delay:, initial_delay: 2, multiplier: 2)
4
+ @initial_delay = initial_delay
5
+ @max_delay = max_delay
6
+ @multiplier = multiplier
7
+ @delay = initial_delay
8
+ end
9
+
10
+ def call
11
+ delay = @delay
12
+ @delay = [@delay * @multiplier, @max_delay].min
13
+ delay
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class HttpConnection
5
+ AUTH_USER = 'authuser'
6
+ PROTO_HEADERS = { 'Content-Type' => 'application/x-protobuf', 'Accept' => 'application/x-protobuf' }.freeze
7
+
8
+ def initialize(api_root, api_key)
9
+ @api_root = api_root
10
+ @api_key = api_key
11
+ end
12
+
13
+ def get(path)
14
+ connection.get(path)
15
+ end
16
+
17
+ def post(path, body)
18
+ connection(PROTO_HEADERS).post(path, body.to_proto)
19
+ end
20
+
21
+ def connection(headers = {})
22
+ if Faraday::VERSION[0].to_i >= 2
23
+ Faraday.new(@api_root) do |conn|
24
+ conn.request :authorization, :basic, AUTH_USER, @api_key
25
+
26
+ conn.headers.merge!(headers)
27
+ end
28
+ else
29
+ Faraday.new(@api_root) do |conn|
30
+ conn.request :basic_auth, AUTH_USER, @api_key
31
+
32
+ conn.headers.merge!(headers)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -14,9 +14,13 @@ module Prefab
14
14
 
15
15
  def initialize(client:, max_paths:, sync_interval:)
16
16
  @max_paths = max_paths
17
- @sync_interval = sync_interval
18
17
  @client = client
19
18
  @start_at = now
19
+ @sync_interval = if sync_interval.is_a?(Numeric)
20
+ proc { sync_interval }
21
+ else
22
+ sync_interval || ExponentialBackoff.new(initial_delay: 8, max_delay: 60 * 10)
23
+ end
20
24
 
21
25
  @pool = Concurrent::ThreadPoolExecutor.new(
22
26
  fallback_policy: :discard,
@@ -72,16 +76,16 @@ module Prefab
72
76
  namespace: @client.namespace
73
77
  )
74
78
 
75
- @client.request Prefab::LoggerReportingService, :send, req_options: {}, params: loggers
79
+ @client.post('/api/v1/known-loggers', loggers)
76
80
  end
77
81
  end
78
82
 
79
83
  def start_periodic_sync
80
84
  Thread.new do
81
- log_internal "Initialized log path collector instance_hash=#{@client.instance_hash} max_paths=#{@max_paths} sync_interval=#{@sync_interval}"
85
+ log_internal "Initialized log path collector instance_hash=#{@client.instance_hash} max_paths=#{@max_paths}"
82
86
 
83
87
  loop do
84
- sleep @sync_interval
88
+ sleep @sync_interval.call
85
89
  sync
86
90
  end
87
91
  end
@@ -10,7 +10,6 @@ module Prefab
10
10
  attr_reader :shared_cache
11
11
  attr_reader :namespace
12
12
  attr_reader :prefab_api_url
13
- attr_reader :prefab_grpc_url
14
13
  attr_reader :on_no_default
15
14
  attr_reader :initialization_timeout_sec
16
15
  attr_reader :on_init_failure
@@ -39,7 +38,6 @@ module Prefab
39
38
  end
40
39
 
41
40
  DEFAULT_MAX_PATHS = 1_000
42
- DEFAULT_SYNC_INTERVAL = 60
43
41
 
44
42
  private def init(
45
43
  api_key: ENV['PREFAB_API_KEY'],
@@ -50,7 +48,6 @@ module Prefab
50
48
  log_formatter: DEFAULT_LOG_FORMATTER,
51
49
  log_prefix: nil,
52
50
  prefab_api_url: ENV['PREFAB_API_URL'] || 'https://api.prefab.cloud',
53
- prefab_grpc_url: ENV['PREFAB_GRPC_URL'] || 'grpc.prefab.cloud:443',
54
51
  on_no_default: ON_NO_DEFAULT::RAISE, # options :raise, :warn_and_return_nil,
55
52
  initialization_timeout_sec: 10, # how long to wait before on_init_failure
56
53
  on_init_failure: ON_INITIALIZATION_FAILURE::RAISE, # options :unlock_and_continue, :lock_and_keep_trying, :raise
@@ -62,7 +59,7 @@ module Prefab
62
59
  prefab_envs: ENV['PREFAB_ENVS'].nil? ? [] : ENV['PREFAB_ENVS'].split(','),
63
60
  collect_logs: true,
64
61
  collect_max_paths: DEFAULT_MAX_PATHS,
65
- collect_sync_interval: DEFAULT_SYNC_INTERVAL
62
+ collect_sync_interval: nil
66
63
  )
67
64
  @api_key = api_key
68
65
  @logdev = logdev
@@ -72,7 +69,6 @@ module Prefab
72
69
  @log_formatter = log_formatter
73
70
  @log_prefix = log_prefix
74
71
  @prefab_api_url = remove_trailing_slash(prefab_api_url)
75
- @prefab_grpc_url = prefab_grpc_url
76
72
  @on_no_default = on_no_default
77
73
  @initialization_timeout_sec = initialization_timeout_sec
78
74
  @on_init_failure = on_init_failure
@@ -8,10 +8,10 @@ require 'openssl'
8
8
  require 'ld-eventsource'
9
9
  require 'prefab_pb'
10
10
  require 'prefab/error'
11
+ require 'prefab/exponential_backoff'
11
12
  require 'prefab/errors/initialization_timeout_error'
12
13
  require 'prefab/errors/invalid_api_key_error'
13
14
  require 'prefab/errors/missing_default_error'
14
- require 'prefab_services_pb'
15
15
  require 'prefab/options'
16
16
  require 'prefab/internal_logger'
17
17
  require 'prefab/sse_logger'
@@ -22,13 +22,11 @@ require 'prefab/config_loader'
22
22
  require 'prefab/local_config_parser'
23
23
  require 'prefab/yaml_config_parser'
24
24
  require 'prefab/config_resolver'
25
+ require 'prefab/http_connection'
25
26
  require 'prefab/client'
26
- require 'prefab/ratelimit_client'
27
27
  require 'prefab/config_client'
28
28
  require 'prefab/feature_flag_client'
29
29
  require 'prefab/logger_client'
30
- require 'prefab/auth_interceptor'
31
- require 'prefab/cancellable_interceptor'
32
30
  require 'prefab/noop_cache'
33
31
  require 'prefab/noop_stats'
34
32
  require 'prefab/murmer3'
@@ -2,17 +2,17 @@
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.23.2 ruby lib
5
+ # stub: prefab-cloud-ruby 0.23.4 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.23.2"
9
+ s.version = "0.23.4"
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]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2023-04-04"
15
- s.description = "RateLimits & Config as a service".freeze
14
+ s.date = "2023-04-12"
15
+ s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
16
16
  s.email = "jdwyer@prefab.cloud".freeze
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE.txt",
@@ -33,8 +33,6 @@ Gem::Specification.new do |s|
33
33
  "VERSION",
34
34
  "compile_protos.sh",
35
35
  "lib/prefab-cloud-ruby.rb",
36
- "lib/prefab/auth_interceptor.rb",
37
- "lib/prefab/cancellable_interceptor.rb",
38
36
  "lib/prefab/client.rb",
39
37
  "lib/prefab/config_client.rb",
40
38
  "lib/prefab/config_loader.rb",
@@ -45,7 +43,9 @@ Gem::Specification.new do |s|
45
43
  "lib/prefab/errors/initialization_timeout_error.rb",
46
44
  "lib/prefab/errors/invalid_api_key_error.rb",
47
45
  "lib/prefab/errors/missing_default_error.rb",
46
+ "lib/prefab/exponential_backoff.rb",
48
47
  "lib/prefab/feature_flag_client.rb",
48
+ "lib/prefab/http_connection.rb",
49
49
  "lib/prefab/internal_logger.rb",
50
50
  "lib/prefab/local_config_parser.rb",
51
51
  "lib/prefab/log_path_collector.rb",
@@ -54,7 +54,6 @@ Gem::Specification.new do |s|
54
54
  "lib/prefab/noop_cache.rb",
55
55
  "lib/prefab/noop_stats.rb",
56
56
  "lib/prefab/options.rb",
57
- "lib/prefab/ratelimit_client.rb",
58
57
  "lib/prefab/sse_logger.rb",
59
58
  "lib/prefab/weighted_value_resolver.rb",
60
59
  "lib/prefab/yaml_config_parser.rb",
@@ -71,6 +70,7 @@ Gem::Specification.new do |s|
71
70
  "test/test_config_resolver.rb",
72
71
  "test/test_config_value_unwrapper.rb",
73
72
  "test/test_criteria_evaluator.rb",
73
+ "test/test_exponential_backoff.rb",
74
74
  "test/test_feature_flag_client.rb",
75
75
  "test/test_helper.rb",
76
76
  "test/test_integration.rb",
@@ -94,12 +94,10 @@ Gem::Specification.new do |s|
94
94
  s.add_runtime_dependency(%q<faraday>.freeze, [">= 0"])
95
95
  s.add_runtime_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
96
96
  s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
97
- s.add_runtime_dependency(%q<grpc>.freeze, [">= 0"])
98
97
  s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
99
98
  s.add_runtime_dependency(%q<uuid>.freeze, [">= 0"])
100
99
  s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
101
100
  s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
102
- s.add_development_dependency(%q<grpc-tools>.freeze, [">= 0"])
103
101
  s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
104
102
  s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
105
103
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
@@ -108,12 +106,10 @@ Gem::Specification.new do |s|
108
106
  s.add_dependency(%q<faraday>.freeze, [">= 0"])
109
107
  s.add_dependency(%q<googleapis-common-protos-types>.freeze, [">= 0"])
110
108
  s.add_dependency(%q<google-protobuf>.freeze, [">= 0"])
111
- s.add_dependency(%q<grpc>.freeze, [">= 0"])
112
109
  s.add_dependency(%q<ld-eventsource>.freeze, [">= 0"])
113
110
  s.add_dependency(%q<uuid>.freeze, [">= 0"])
114
111
  s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
115
112
  s.add_dependency(%q<bundler>.freeze, [">= 0"])
116
- s.add_dependency(%q<grpc-tools>.freeze, [">= 0"])
117
113
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
118
114
  s.add_dependency(%q<rdoc>.freeze, [">= 0"])
119
115
  s.add_dependency(%q<simplecov>.freeze, [">= 0"])
@@ -91,8 +91,7 @@ class IntegrationTest
91
91
  prefab_envs: ['unit_tests'],
92
92
  prefab_datasources: Prefab::Options::DATASOURCES::ALL,
93
93
  api_key: ENV['PREFAB_INTEGRATION_TEST_API_KEY'],
94
- prefab_api_url: 'https://api.staging-prefab.cloud',
95
- prefab_grpc_url: 'grpc.staging-prefab.cloud:443'
94
+ prefab_api_url: 'https://api.staging-prefab.cloud'
96
95
  }.merge(@client_overrides))
97
96
  end
98
97
  end
data/test/test_client.rb CHANGED
@@ -77,13 +77,6 @@ class TestClient < Minitest::Test
77
77
  assert_equal 'all-features', @client.get('flag_with_a_value')
78
78
  end
79
79
 
80
- def test_ssl_certs
81
- certs = @client.send(:ssl_certs).split('-----BEGIN CERTIFICATE-----')
82
-
83
- # This is a smoke test to make sure multiple certs are loaded
84
- assert certs.length > 1
85
- end
86
-
87
80
  def test_initialization_with_an_options_object
88
81
  options_hash = {
89
82
  namespace: 'test-namespace',
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestExponentialBackoff < Minitest::Test
6
+ def test_backoff
7
+ backoff = Prefab::ExponentialBackoff.new(max_delay: 120)
8
+
9
+ assert_equal 2, backoff.call
10
+ assert_equal 4, backoff.call
11
+ assert_equal 8, backoff.call
12
+ assert_equal 16, backoff.call
13
+ assert_equal 32, backoff.call
14
+ assert_equal 64, backoff.call
15
+ assert_equal 120, backoff.call
16
+ assert_equal 120, backoff.call
17
+ end
18
+ end
@@ -4,6 +4,9 @@ require 'test_helper'
4
4
  require 'timecop'
5
5
 
6
6
  class TestLogPathCollector < Minitest::Test
7
+ MAX_WAIT = 2
8
+ SLEEP_TIME = 0.01
9
+
7
10
  def test_sync
8
11
  Timecop.freeze do
9
12
  client = new_client(namespace: 'this.is.a.namespace')
@@ -13,29 +16,32 @@ class TestLogPathCollector < Minitest::Test
13
16
 
14
17
  requests = []
15
18
 
16
- client.define_singleton_method(:request) do |*params|
19
+ client.define_singleton_method(:post) do |*params|
17
20
  requests.push(params)
18
21
  end
19
22
 
20
23
  client.log_path_collector.send(:sync)
21
24
 
22
25
  # let the flush thread run
23
- sleep 0.01 while requests.length == 0
26
+
27
+ wait_time = 0
28
+ while requests.length == 0
29
+ wait_time += SLEEP_TIME
30
+ sleep SLEEP_TIME
31
+
32
+ raise "Waited #{MAX_WAIT} seconds for the flush thread to run, but it never did" if wait_time > MAX_WAIT
33
+ end
24
34
 
25
35
  assert_equal requests, [[
26
- Prefab::LoggerReportingService,
27
- :send,
28
- {
29
- req_options: {},
30
- params: Prefab::Loggers.new(
31
- loggers: [Prefab::Logger.new(logger_name: 'test.test_log_path_collector.test_sync',
32
- infos: 2, errors: 3)],
33
- start_at: (Time.now.utc.to_f * 1000).to_i,
34
- end_at: (Time.now.utc.to_f * 1000).to_i,
35
- instance_hash: client.instance_hash,
36
- namespace: 'this.is.a.namespace'
37
- )
38
- }
36
+ '/api/v1/known-loggers',
37
+ Prefab::Loggers.new(
38
+ loggers: [Prefab::Logger.new(logger_name: 'test.test_log_path_collector.test_sync',
39
+ infos: 2, errors: 3)],
40
+ start_at: (Time.now.utc.to_f * 1000).to_i,
41
+ end_at: (Time.now.utc.to_f * 1000).to_i,
42
+ instance_hash: client.instance_hash,
43
+ namespace: 'this.is.a.namespace'
44
+ )
39
45
  ]]
40
46
  end
41
47
  end
@@ -47,8 +53,7 @@ class TestLogPathCollector < Minitest::Test
47
53
  prefab_config_override_dir: 'none',
48
54
  prefab_config_classpath_dir: 'test',
49
55
  prefab_envs: ['unit_tests'],
50
- api_key: '123-development-yourapikey-SDK',
51
- collect_sync_interval: 1000 # we'll trigger sync manually in our test
56
+ api_key: '123-development-yourapikey-SDK'
52
57
  }.merge(overrides))
53
58
 
54
59
  Prefab::Client.new(options)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prefab-cloud-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.2
4
+ version: 0.23.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-04 00:00:00.000000000 Z
11
+ date: 2023-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -72,20 +72,6 @@ dependencies:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
- - !ruby/object:Gem::Dependency
76
- name: grpc
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
- type: :runtime
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
75
  - !ruby/object:Gem::Dependency
90
76
  name: ld-eventsource
91
77
  requirement: !ruby/object:Gem::Requirement
@@ -142,20 +128,6 @@ dependencies:
142
128
  - - ">="
143
129
  - !ruby/object:Gem::Version
144
130
  version: '0'
145
- - !ruby/object:Gem::Dependency
146
- name: grpc-tools
147
- requirement: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - ">="
150
- - !ruby/object:Gem::Version
151
- version: '0'
152
- type: :development
153
- prerelease: false
154
- version_requirements: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - ">="
157
- - !ruby/object:Gem::Version
158
- version: '0'
159
131
  - !ruby/object:Gem::Dependency
160
132
  name: juwelier
161
133
  requirement: !ruby/object:Gem::Requirement
@@ -198,7 +170,7 @@ dependencies:
198
170
  - - ">="
199
171
  - !ruby/object:Gem::Version
200
172
  version: '0'
201
- description: RateLimits & Config as a service
173
+ description: Feature Flags, Live Config, and Dynamic Log Levels as a service
202
174
  email: jdwyer@prefab.cloud
203
175
  executables: []
204
176
  extensions: []
@@ -220,8 +192,6 @@ files:
220
192
  - VERSION
221
193
  - compile_protos.sh
222
194
  - lib/prefab-cloud-ruby.rb
223
- - lib/prefab/auth_interceptor.rb
224
- - lib/prefab/cancellable_interceptor.rb
225
195
  - lib/prefab/client.rb
226
196
  - lib/prefab/config_client.rb
227
197
  - lib/prefab/config_loader.rb
@@ -232,7 +202,9 @@ files:
232
202
  - lib/prefab/errors/initialization_timeout_error.rb
233
203
  - lib/prefab/errors/invalid_api_key_error.rb
234
204
  - lib/prefab/errors/missing_default_error.rb
205
+ - lib/prefab/exponential_backoff.rb
235
206
  - lib/prefab/feature_flag_client.rb
207
+ - lib/prefab/http_connection.rb
236
208
  - lib/prefab/internal_logger.rb
237
209
  - lib/prefab/local_config_parser.rb
238
210
  - lib/prefab/log_path_collector.rb
@@ -241,7 +213,6 @@ files:
241
213
  - lib/prefab/noop_cache.rb
242
214
  - lib/prefab/noop_stats.rb
243
215
  - lib/prefab/options.rb
244
- - lib/prefab/ratelimit_client.rb
245
216
  - lib/prefab/sse_logger.rb
246
217
  - lib/prefab/weighted_value_resolver.rb
247
218
  - lib/prefab/yaml_config_parser.rb
@@ -258,6 +229,7 @@ files:
258
229
  - test/test_config_resolver.rb
259
230
  - test/test_config_value_unwrapper.rb
260
231
  - test/test_criteria_evaluator.rb
232
+ - test/test_exponential_backoff.rb
261
233
  - test/test_feature_flag_client.rb
262
234
  - test/test_helper.rb
263
235
  - test/test_integration.rb
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class AuthInterceptor < GRPC::ClientInterceptor
5
- VERSION = File.exist?('VERSION') ? File.read('VERSION').chomp : ''
6
- CLIENT = "prefab-cloud-ruby.#{VERSION}".freeze
7
-
8
- def initialize(api_key)
9
- @api_key = api_key
10
- end
11
-
12
- def request_response(request:, call:, method:, metadata:, &block)
13
- shared(metadata, &block)
14
- end
15
-
16
- def client_streamer(requests:, call:, method:, metadata:, &block)
17
- shared(metadata, &block)
18
- end
19
-
20
- def server_streamer(request:, call:, method:, metadata:, &block)
21
- shared(metadata, &block)
22
- end
23
-
24
- def bidi_streamer(requests:, call:, method:, metadata:, &block)
25
- shared(metadata, &block)
26
- end
27
-
28
- def shared(metadata)
29
- metadata['auth'] = @api_key
30
- metadata['client'] = CLIENT
31
- yield
32
- end
33
- end
34
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class CancellableInterceptor < GRPC::ClientInterceptor
5
- WAIT_SEC = 3
6
-
7
- def initialize(base_client)
8
- @base_client = base_client
9
- end
10
-
11
- def cancel
12
- @call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
13
- i = 0
14
- while i < WAIT_SEC
15
- if @call.instance_variable_get('@wrapped').cancelled?
16
- @base_client.log_internal ::Logger::DEBUG, 'Cancelled streaming.'
17
- return
18
- else
19
- @base_client.log_internal ::Logger::DEBUG, 'Unable to cancel streaming. Trying again'
20
- @call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
21
- i += 1
22
- sleep(1)
23
- end
24
- end
25
- @base_client.log_internal ::Logger::INFO, 'Unable to cancel streaming.'
26
- end
27
-
28
- def request_response(request:, call:, method:, metadata:, &block)
29
- shared(call, &block)
30
- end
31
-
32
- def client_streamer(requests:, call:, method:, metadata:, &block)
33
- shared(call, &block)
34
- end
35
-
36
- def server_streamer(request:, call:, method:, metadata:, &block)
37
- shared(call, &block)
38
- end
39
-
40
- def bidi_streamer(requests:, call:, method:, metadata:, &block)
41
- shared(call, &block)
42
- end
43
-
44
- def shared(call)
45
- @call = call
46
- yield
47
- end
48
- end
49
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class RateLimitClient
5
- def initialize(base_client, timeout)
6
- @timeout = timeout
7
- @base_client = base_client
8
- end
9
-
10
- def pass?(group)
11
- result = acquire([group], 1)
12
- result.passed
13
- end
14
-
15
- def acquire(groups, acquire_amount, allow_partial_response: false, on_error: :log_and_pass)
16
- expiry_cache_key = "prefab.ratelimit.expiry:#{groups.join('.')}"
17
- expiry = @base_client.shared_cache.read(expiry_cache_key)
18
- if !expiry.nil? && Integer(expiry) > Time.now.utc.to_f * 1000
19
- @base_client.stats.increment('prefab.ratelimit.limitcheck.expirycache.hit', tags: [])
20
- return Prefab::LimitResponse.new(passed: false, amount: 0)
21
- end
22
-
23
- req = Prefab::LimitRequest.new(
24
- account_id: @base_client.account_id,
25
- acquire_amount: acquire_amount,
26
- groups: groups,
27
- allow_partial_response: allow_partial_response
28
- )
29
-
30
- result = @base_client.request Prefab::RateLimitService, :limit_check, req_options: { timeout: @timeout },
31
- params: req
32
-
33
- reset = result.limit_reset_at
34
- @base_client.shared_cache.write(expiry_cache_key, reset) unless reset < 1 # protobuf default int to 0
35
-
36
- @base_client.stats.increment('prefab.ratelimit.limitcheck',
37
- tags: ["policy_group:#{result.policy_group}", "pass:#{result.passed}"])
38
-
39
- result
40
- rescue StandardError => e
41
- handle_error(e, on_error, groups)
42
- end
43
-
44
- def upsert(key, policy_name, limit, burst: nil, safety_level: nil)
45
- burst = limit if burst.nil?
46
- limit_definition = Prefab::LimitDefinition.new(
47
- account_id: @base_client.account_id,
48
- policy_name: Object.const_get("Prefab::LimitResponse::LimitPolicyNames::#{policy_name}"),
49
- limit: limit,
50
- burst: burst
51
- )
52
- limit_definition.safety_level = safety_level unless safety_level.nil?
53
- config_value = Prefab::ConfigValue.new(limit_definition: limit_definition)
54
- config_delta = Prefab::ConfigClient.value_to_delta(key, config_value)
55
- upsert_req = Prefab::UpsertRequest.new(config_delta: config_delta)
56
-
57
- @base_client.request Prefab::ConfigService, :upsert, req_options: { timeout: @timeout }, params: upsert_req
58
- end
59
-
60
- private
61
-
62
- def handle_error(e, on_error, groups)
63
- @base_client.stats.increment('prefab.ratelimit.error', tags: ['type:limit'])
64
-
65
- message = "ratelimit for #{groups} error: #{e.message}"
66
- case on_error
67
- when :log_and_pass
68
- @base_client.log.warn(message)
69
- Prefab::LimitResponse.new(passed: true, amount: 0)
70
- when :log_and_hit
71
- @base_client.log.warn(message)
72
- Prefab::LimitResponse.new(passed: false, amount: 0)
73
- when :throw
74
- raise e
75
- end
76
- end
77
- end
78
- end