prefab-cloud-ruby 1.1.1 → 1.1.2

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: 4207ba2ec83b02534caa1d8f23b75c2120c8d277253312a5bd98898c38baf156
4
- data.tar.gz: 8698e869c65017759a56c01f3ff57bc16b9597cc4d1edc219fc282f0ae371e0c
3
+ metadata.gz: 0d3348f914c10f7f7d45814ff1be70a067e227cbef499b6f44736f10aa5cc30c
4
+ data.tar.gz: 52699e5fee8f4f6ea26a89d3e3381cc6220d25d4720dcb69d7aee8de8f79e1c9
5
5
  SHA512:
6
- metadata.gz: 31eb4a0e5e132dd3136a48f9afa5f7d7decb73eccb43cc74329e9fc2b3c5a5b7109bc53a3c468e8272ae0d4ad04e3f85fb516eb16b6590325e23046e6f1c9d7b
7
- data.tar.gz: 9ea6c49f4ffe05d15654143c98dc7f74d405ac00bd919d3ae43547d9aa3ba4c3adcfa54626a0c393fa766cb420af29231032f07dd4da1c53d71c12da2cfc273a
6
+ metadata.gz: c960267ea9f3e09664e48965fb470c1a3093061b1e6906f8f73b4c80a396ab80e0b383b3e42209e6cc95d5fab778c6221a2b9292a2015603139a874586eddf22
7
+ data.tar.gz: 98cc728c27f1b1f6267c4b01b754d07f23fa60f8dfad95edd9e9288de666f6fc92539bf7124e85337023bf772946422c8919c75ebf487e9999a60251734a4d61
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.2 - 2023-10-13
4
+
5
+ - Add `cloud.prefab.client.criteria_evaluator` `debug` logging of evaluations (#150)
6
+ - Add `x_use_local_cache` for local caching (#148)
7
+ - Tests run in RubyMine (#147)
8
+
3
9
  ## 1.1.1 - 2023-10-11
4
10
 
5
11
  - Migrate happy-path client-initialization logging to `DEBUG` level rather than `INFO` (#144)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.1.2
data/lib/prefab/client.rb CHANGED
@@ -133,7 +133,7 @@ module Prefab
133
133
  # $prefab.set_rails_loggers
134
134
  # end
135
135
  def fork
136
- Prefab::Client.new(@options)
136
+ Prefab::Client.new(@options.for_fork)
137
137
  end
138
138
 
139
139
  private
@@ -5,6 +5,7 @@ module Prefab
5
5
  RECONNECT_WAIT = 5
6
6
  DEFAULT_CHECKPOINT_FREQ_SEC = 60
7
7
  SSE_READ_TIMEOUT = 300
8
+ STALE_CACHE_WARN_HOURS = 5
8
9
  AUTH_USER = 'authuser'
9
10
  LOGGING_KEY_PREFIX = "#{Prefab::LoggerClient::BASE_KEY}#{Prefab::LoggerClient::SEP}".freeze
10
11
 
@@ -112,6 +113,10 @@ module Prefab
112
113
 
113
114
  return if success
114
115
 
116
+ success = load_cache
117
+
118
+ return if success
119
+
115
120
  @base_client.log_internal ::Logger::WARN, 'No success loading checkpoints'
116
121
  end
117
122
 
@@ -130,6 +135,7 @@ module Prefab
130
135
  if resp.status == 200
131
136
  configs = PrefabProto::Configs.decode(resp.body)
132
137
  load_configs(configs, source)
138
+ cache_configs(configs)
133
139
  true
134
140
  else
135
141
  @base_client.log_internal ::Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
@@ -171,6 +177,47 @@ module Prefab
171
177
  finish_init!(source, project_id)
172
178
  end
173
179
 
180
+ def cache_path
181
+ return @cache_path unless @cache_path.nil?
182
+ @cache_path ||= calc_cache_path
183
+ FileUtils.mkdir_p(File.dirname(@cache_path))
184
+ @cache_path
185
+ end
186
+
187
+ def calc_cache_path
188
+ file_name = "prefab.cache.#{@base_client.options.api_key_id}.json"
189
+ dir = ENV.fetch('XDG_CACHE_HOME', File.join(Dir.home, '.cache'))
190
+ File.join(dir, file_name)
191
+ end
192
+
193
+ def cache_configs(configs)
194
+ return unless @options.use_local_cache && !@options.is_fork
195
+ File.open(cache_path, "w") do |f|
196
+ f.flock(File::LOCK_EX)
197
+ f.write(PrefabProto::Configs.encode_json(configs))
198
+ end
199
+ @base_client.log_internal ::Logger::DEBUG, "Cached configs to #{cache_path}"
200
+ rescue => e
201
+ @base_client.log_internal ::Logger::DEBUG, "Failed to cache configs to #{cache_path} #{e}"
202
+ end
203
+
204
+ def load_cache
205
+ return false unless @options.use_local_cache
206
+ File.open(cache_path) do |f|
207
+ f.flock(File::LOCK_SH)
208
+ configs = PrefabProto::Configs.decode_json(f.read)
209
+ load_configs(configs, :cache)
210
+
211
+ hours_old = ((Time.now - File.mtime(f)) / 60 / 60).round(2)
212
+ if hours_old > STALE_CACHE_WARN_HOURS
213
+ @base_client.log_internal ::Logger::INFO, "Stale Cache Load: #{hours_old} hours old"
214
+ end
215
+ end
216
+ rescue => e
217
+ @base_client.log_internal ::Logger::DEBUG, "Failed to read cached configs at #{cache_path}. #{e}"
218
+ false
219
+ end
220
+
174
221
  # A thread that checks for a checkpoint
175
222
  def start_checkpointing_thread
176
223
  Thread.new do
@@ -197,7 +244,7 @@ module Prefab
197
244
  source: source,
198
245
  project_id: project_id,
199
246
  project_env_id: @config_resolver.project_env_id,
200
- api_key: @base_client.options.api_key
247
+ api_key_id: @base_client.options.api_key_id
201
248
  )
202
249
  @base_client.log_internal ::Logger::INFO, presenter.to_s
203
250
  @base_client.log_internal ::Logger::DEBUG, to_s
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Prefab
4
4
  class ConfigClientPresenter
5
- def initialize(size:, source:, project_id:, project_env_id:, api_key:)
5
+ def initialize(size:, source:, project_id:, project_env_id:, api_key_id:)
6
6
  @size = size
7
7
  @source = source
8
8
  @project_id = project_id
9
9
  @project_env_id = project_env_id
10
- @api_key_id = api_key&.split("-")&.first
10
+ @api_key_id = api_key_id
11
11
  end
12
12
 
13
13
  def to_s
@@ -19,8 +19,10 @@ module Prefab
19
19
  end
20
20
 
21
21
  def evaluate(properties)
22
- evaluate_for_env(@project_env_id, properties) ||
22
+ rtn = evaluate_for_env(@project_env_id, properties) ||
23
23
  evaluate_for_env(0, properties)
24
+ @base_client.log_internal ::Logger::DEBUG, "Eval Key #{@config.key} Result #{rtn&.value} with #{properties.to_h}", :criteria_evaluator unless @config.config_type == :LOG_LEVEL
25
+ rtn
24
26
  end
25
27
 
26
28
  def all_criteria_match?(conditional_value, props)
@@ -22,6 +22,7 @@ module Prefab
22
22
  self.formatter = formatter
23
23
  @config_client = BootstrappingConfigClient.new
24
24
  @silences = Concurrent::Map.new(initial_capacity: 2)
25
+ @recurse_check = Concurrent::Map.new(initial_capacity: 2)
25
26
  @prefix = "#{prefix}#{prefix && '.'}"
26
27
 
27
28
  @log_path_aggregator = log_path_aggregator
@@ -37,13 +38,19 @@ module Prefab
37
38
  end
38
39
 
39
40
  def log_internal(message, path, progname, severity, log_context={}, &block)
41
+ return if @recurse_check[local_log_id]
42
+ @recurse_check[local_log_id] = true
43
+
40
44
  path = if path
41
45
  "#{INTERNAL_PREFIX}.#{path}"
42
46
  else
43
47
  INTERNAL_PREFIX
44
48
  end
45
-
46
- log(message, path, progname, severity, log_context, &block)
49
+ begin
50
+ log(message, path, progname, severity, log_context, &block)
51
+ ensure
52
+ @recurse_check[local_log_id] = false
53
+ end
47
54
  end
48
55
 
49
56
  def log(message, path, progname, severity, log_context={})
@@ -16,6 +16,8 @@ module Prefab
16
16
  attr_reader :prefab_config_classpath_dir
17
17
  attr_reader :prefab_envs
18
18
  attr_reader :collect_sync_interval
19
+ attr_reader :use_local_cache
20
+ attr_accessor :is_fork
19
21
 
20
22
  DEFAULT_LOG_FORMATTER = proc { |data|
21
23
  severity = data[:severity]
@@ -77,7 +79,8 @@ module Prefab
77
79
  context_max_size: DEFAULT_MAX_EVAL_SUMMARIES,
78
80
  collect_evaluation_summaries: true,
79
81
  collect_max_evaluation_summaries: DEFAULT_MAX_EVAL_SUMMARIES,
80
- allow_telemetry_in_local_mode: false
82
+ allow_telemetry_in_local_mode: false,
83
+ x_use_local_cache: false
81
84
  )
82
85
  @api_key = api_key
83
86
  @logdev = logdev
@@ -98,6 +101,8 @@ module Prefab
98
101
  @collect_evaluation_summaries = collect_evaluation_summaries
99
102
  @collect_max_evaluation_summaries = collect_max_evaluation_summaries
100
103
  @allow_telemetry_in_local_mode = allow_telemetry_in_local_mode
104
+ @use_local_cache = x_use_local_cache
105
+ @is_fork = false
101
106
 
102
107
  # defaults that may be overridden by context_upload_mode
103
108
  @collect_shapes = false
@@ -156,6 +161,16 @@ module Prefab
156
161
  ENV['PREFAB_CDN_URL'] || "#{@prefab_api_url.gsub(/\./, '-')}.global.ssl.fastly.net"
157
162
  end
158
163
 
164
+ def api_key_id
165
+ @api_key&.split("-")&.first
166
+ end
167
+
168
+ def for_fork
169
+ clone = self.clone
170
+ clone.is_fork = true
171
+ clone
172
+ end
173
+
159
174
  private
160
175
 
161
176
  def telemetry_allowed?(option)
@@ -2,16 +2,16 @@
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 1.1.1 ruby lib
5
+ # stub: prefab-cloud-ruby 1.1.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "1.1.1"
9
+ s.version = "1.1.2"
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-10-11"
14
+ s.date = "2023-10-13"
15
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.executables = ["console".freeze]
@@ -21,7 +21,6 @@ Gem::Specification.new do |s|
21
21
  "README.md"
22
22
  ]
23
23
  s.files = [
24
- ".envrc",
25
24
  ".envrc.sample",
26
25
  ".github/workflows/ruby.yml",
27
26
  ".gitmodules",
@@ -30,7 +30,7 @@ class MockBaseClient
30
30
  @logger
31
31
  end
32
32
 
33
- def log_internal(level, message); end
33
+ def log_internal(level, msg, path = nil, **tags); end
34
34
 
35
35
  def context_shape_aggregator; end
36
36
 
@@ -9,7 +9,8 @@ class TestConfigClient < Minitest::Test
9
9
  prefab_config_override_dir: 'none',
10
10
  prefab_config_classpath_dir: 'test',
11
11
  prefab_envs: 'unit_tests',
12
- prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY
12
+ prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY,
13
+ x_use_local_cache: true,
13
14
  )
14
15
 
15
16
  @config_client = Prefab::ConfigClient.new(MockBaseClient.new(options), 10)
@@ -73,4 +74,36 @@ class TestConfigClient < Minitest::Test
73
74
 
74
75
  assert_match(/format is invalid/, err.message)
75
76
  end
77
+
78
+ def test_caching
79
+ @config_client.send(:cache_configs,
80
+ PrefabProto::Configs.new(configs:
81
+ [PrefabProto::Config.new(key: 'test', id: 1,
82
+ rows: [PrefabProto::ConfigRow.new(
83
+ values: [
84
+ PrefabProto::ConditionalValue.new(
85
+ value: PrefabProto::ConfigValue.new(string: "test value")
86
+ )
87
+ ]
88
+ )])],
89
+ config_service_pointer: PrefabProto::ConfigServicePointer.new(project_id: 3, project_env_id: 5)))
90
+ @config_client.send(:load_cache)
91
+ assert_equal "test value", @config_client.get("test")
92
+ end
93
+
94
+ def test_cache_path_respects_xdg
95
+ options = Prefab::Options.new(
96
+ prefab_datasources: Prefab::Options::DATASOURCES::LOCAL_ONLY,
97
+ x_use_local_cache: true,
98
+ api_key: "123-ENV-KEY-SDK",)
99
+
100
+ config_client = Prefab::ConfigClient.new(MockBaseClient.new(options), 10)
101
+ assert_equal "#{Dir.home}/.cache/prefab.cache.123.json", config_client.send(:cache_path)
102
+
103
+ with_env('XDG_CACHE_HOME', '/tmp') do
104
+ config_client = Prefab::ConfigClient.new(MockBaseClient.new(options), 10)
105
+ assert_equal "/tmp/prefab.cache.123.json", config_client.send(:cache_path)
106
+ end
107
+ end
108
+
76
109
  end
@@ -724,6 +724,8 @@ class TestCriteriaEvaluator < Minitest::Test
724
724
  FakeLogger.new
725
725
  end
726
726
 
727
+ def log_internal(level, msg, path = nil, **tags); end
728
+
727
729
  def evaluation_summary_aggregator
728
730
  @evaluation_summary_aggregator ||= Prefab::EvaluationSummaryAggregator.new(client: self, max_keys: 9999, sync_interval: 9999)
729
731
  end
data/test/test_helper.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'minitest/autorun'
4
4
  require 'minitest/focus'
5
5
  require 'minitest/reporters'
6
- Minitest::Reporters.use!
6
+ Minitest::Reporters.use! unless ENV['RM_INFO']
7
7
 
8
8
  require 'prefab-cloud-ruby'
9
9
 
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: 1.1.1
4
+ version: 1.1.2
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-10-11 00:00:00.000000000 Z
11
+ date: 2023-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -180,7 +180,6 @@ extra_rdoc_files:
180
180
  - LICENSE.txt
181
181
  - README.md
182
182
  files:
183
- - ".envrc"
184
183
  - ".envrc.sample"
185
184
  - ".github/workflows/ruby.yml"
186
185
  - ".gitmodules"
data/.envrc DELETED
@@ -1,2 +0,0 @@
1
- export AWS_ACCESS_KEY_ID=
2
- export AWS_SECRET_ACCESS_KEY=