prefab-cloud-ruby 1.1.1 → 1.1.2

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