prefab-cloud-ruby 0.23.2 → 0.23.4

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 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