process_settings 0.15.0 → 0.18.0

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: 17af8e8b00a219e790b36fc20282e0c4c7d7c04cfd7f911ea636f056b675cf70
4
- data.tar.gz: 198665150f57b95634698ab3474dd587dbefbc48ecf41d10084a6f872a0a3949
3
+ metadata.gz: aab773e1074ed3b357a5938e9366b56e69bdc6e36ace59835fdea8bbbe49516d
4
+ data.tar.gz: 3fe8bb70b1e59498420c878abb86cf2e1b0289694e4ed941a0a11b1a6abc5bce
5
5
  SHA512:
6
- metadata.gz: ea2bf7c35886a10807a3b8ab171133776c1381371b628ac08cbe413a2cbc8d4bc8c600142fc44c0734fb1e2dc6bb2143be40c68c5b99217404fa6a085ccf2326
7
- data.tar.gz: b92dbd32b2935e7d3ba4b8b95f33dc032e018a273505f7daaba4126d1ef7f132fe40e79535780c45a3283df9daa3c006ce1312c2cf2c2eb472b7b123f09fa10f
6
+ metadata.gz: 939389ccdfaf3fb4880251e34abc36f15e11963ef1ce69e102208a04e084df0c870eeb906103f9859d6ce7af1e27938d713e153ab16768ee6bbaf0e6c6bb5262
7
+ data.tar.gz: bf6190c8812f0fcb73507bfc4b1cda3e2f9370e5c398df8cdbda2a112a23c80479dcf7189d8fb157ae4725b3bcfafe9bef1bb23a63a27eb3351b33d6644e2d04
data/README.md CHANGED
@@ -167,6 +167,32 @@ This will be applied in any process that has (`service_name == "frontend"` OR `s
167
167
  ### Precedence
168
168
  The settings YAML files are always combined in alphabetical order by file path. Later settings take precedence over the earlier ones.
169
169
 
170
+ ### Forked Processes
171
+ When using `ProcessSettings` within an environment that is forking threads (like `unicorn` web servers), you can restart the `FileMonitor`
172
+ after the fork with `restart_after_fork`.
173
+ ```ruby
174
+ # unicorn.rb
175
+
176
+ preload_app true
177
+
178
+ after_fork do
179
+ ProcessSettings.instance.restart_after_fork
180
+ end
181
+ ```
182
+
183
+ ### Start Watchdog
184
+ When using `ProcessSettings` you can start a watchdog thread using `start_watchdog_thread` on a particular file path. The watchdog will poll once a minute to double-check if any changes have been missed due to a bug in the `listen` gem or the supporting OS drivers like `inotify`.
185
+ You may not start the watchdog thread it has already been started.
186
+ ```ruby
187
+ ProcessSettings.instance.start_watchdog_thread(file_path)
188
+ ```
189
+
190
+ ### Stop Watchdog
191
+ When using `ProcessSettings` you can stop a watchdog thread using `stop_watchdog_thread`. Once stopped you may start a new watchdog thread.
192
+ ```ruby
193
+ ProcessSettings.instance.stop_watchdog_thread
194
+ ```
195
+
170
196
  ### Testing
171
197
  For testing, it is often necessary to set a specific override hash for the process_settings values to use in
172
198
  that use case. The `ProcessSettings::Testing::RSpec::Helpers` and `ProcessSettings::Testing::Minitest::Helpers` modules are provided for this purpose.
@@ -181,7 +207,7 @@ require 'process_settings/testing/helpers'
181
207
  RSpec.configure do |config|
182
208
  # ...
183
209
 
184
- include ProcessSettings::Testing::RSpec::Helpers
210
+ config.include ProcessSettings::Testing::RSpec::Helpers
185
211
 
186
212
  # Note: the include above will automatically register a global after block that will reset process_settings to their initial values.
187
213
  # ...
@@ -92,6 +92,26 @@ def warn_if_old_libyaml_version
92
92
  end
93
93
  end
94
94
 
95
+ def settings_files_match?(filename_1, filename_2)
96
+ filename_1 && filename_2 &&
97
+ File.exist?(filename_1) && File.exist?(filename_2) &&
98
+ diff_process_settings(filename_1, filename_2)
99
+ end
100
+
101
+ def diff_process_settings(filename_1, filename_2)
102
+ system(<<~EOS)
103
+ bundle exec diff_process_settings --silent "#{filename_1}" "#{filename_2}"
104
+ EOS
105
+ status_code = $?.exitstatus
106
+ case status_code
107
+ when 0
108
+ true
109
+ when 1
110
+ false
111
+ else
112
+ raise "diff_process_settings failed with code #{status_code}"
113
+ end
114
+ end
95
115
 
96
116
  #
97
117
  # main
@@ -115,14 +135,17 @@ tmp_output_filename = "#{output_filename}.tmp"
115
135
  system("rm -f #{tmp_output_filename}")
116
136
  File.write(tmp_output_filename, yaml_with_warning_comment)
117
137
 
118
- system(<<~EOS)
119
- if bundle exec diff_process_settings --silent #{tmp_output_filename} #{output_filename} ; then
120
- #{"echo #{options.root_folder}: unchanged;" if options.verbose}
121
- rm -f #{tmp_output_filename};
138
+ if settings_files_match?(options.initial_filename, tmp_output_filename)
139
+ if settings_files_match?(output_filename, tmp_output_filename)
140
+ puts "#{options.root_folder}: unchanged" if options.verbose
141
+ FileUtils.rm_f(tmp_output_filename)
122
142
  else
123
- #{"echo #{options.root_folder}: UPDATING;" if options.verbose}
124
- mv #{tmp_output_filename} #{output_filename};
125
- fi
126
- EOS
143
+ puts "#{options.root_folder}: UPDATING (changed now)" if options.verbose
144
+ FileUtils.mv(tmp_output_filename, output_filename)
145
+ end
146
+ else
147
+ puts "#{options.root_folder}: UPDATING (unchanged now, but changed from initial)" if options.verbose
148
+ FileUtils.mv(tmp_output_filename, output_filename)
149
+ end
127
150
 
128
151
  exit(0)
@@ -34,10 +34,7 @@ input_files =
34
34
  if path == '-'
35
35
  ''
36
36
  else
37
- unless File.exists?(path)
38
- warn "#{path} not found--must be a path to combined_process_settings.yml"
39
- exit 1
40
- end
37
+ File.exist?(path) or path = '/dev/null'
41
38
  "< #{path}"
42
39
  end
43
40
  end
@@ -133,15 +133,11 @@ module ProcessSettings
133
133
 
134
134
  def full_context_from_cache(dynamic_context)
135
135
  if (full_context = full_context_cache[dynamic_context])
136
- logger.info("cache hit ...")
137
136
  full_context
138
137
  else
139
- logger.info("cache miss ...")
140
138
  dynamic_context.deep_merge(static_context).tap do |full_context|
141
139
  if full_context_cache.size <= 1000
142
140
  full_context_cache[dynamic_context] = full_context
143
- else
144
- logger.info("cache limit reached ...")
145
141
  end
146
142
  end
147
143
  end
@@ -8,6 +8,7 @@ require 'active_support/deprecation'
8
8
  require 'process_settings/abstract_monitor'
9
9
  require 'process_settings/targeted_settings'
10
10
  require 'process_settings/hash_path'
11
+ require 'process_settings/helpers/watchdog'
11
12
 
12
13
  module ProcessSettings
13
14
  class FileMonitor < AbstractMonitor
@@ -24,10 +25,32 @@ module ProcessSettings
24
25
  start_internal(enable_listen_thread?(environment))
25
26
  end
26
27
 
28
+ def start_watchdog_thread(file_path)
29
+ @watchdog_thread and raise ArgumentError, "watchdog thread already running!"
30
+ @watchdog_thread = Thread.new do
31
+ watchdog = Watchdog.new(file_path)
32
+ loop do
33
+ sleep(1.minute)
34
+ watchdog.check
35
+ rescue => ex
36
+ logger.error("ProcessSettings::Watchdog thread: #{ex.class.name}: #{ex.message}")
37
+ end
38
+ end
39
+ end
40
+
41
+ def stop_watchdog_thread
42
+ @watchdog_thread&.kill
43
+ @watchdog_thread = nil
44
+ end
45
+
27
46
  def start
28
47
  start_internal(enable_listen_thread?)
29
48
  end
30
- deprecate :start, deprecator: ActiveSupport::Deprecation.new('1.0', 'ProcessSettings') # will become private
49
+ deprecate start: :restart_after_fork, deprecator: ActiveSupport::Deprecation.new('1.0', 'ProcessSettings')
50
+
51
+ def restart_after_fork
52
+ start_internal(enable_listen_thread?)
53
+ end
31
54
 
32
55
  def listen_thread_running?
33
56
  !@listener.nil?
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ module ProcessSettings
6
+ class Watchdog
7
+ class OutOfSync < StandardError; end
8
+
9
+ MAX_MTIME_DIFFERENCE = 2.minutes
10
+
11
+ def initialize(process_settings_file_path)
12
+ @process_settings_file_path = process_settings_file_path or raise ArgumentError, "process_settings_file_path must be passed"
13
+ end
14
+
15
+ def check
16
+ if version_from_memory != version_from_disk && (Time.now - mtime_from_disk) > MAX_MTIME_DIFFERENCE
17
+ raise ProcessSettings::OutOfSync.new("ProcessSettings versions are out of sync!\n Version from Disk: #{version_from_disk}\n Version from Memory: #{version_from_memory}\n mtime of file: #{mtime_from_disk}")
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :process_settings_file_path
24
+
25
+ def version_from_memory
26
+ ProcessSettings.instance.untargeted_settings.version
27
+ end
28
+
29
+ def version_from_disk
30
+ ProcessSettings::TargetedSettings.from_file(process_settings_file_path, only_meta: true).version
31
+ end
32
+
33
+ def mtime_from_disk
34
+ File.mtime(process_settings_file_path)
35
+ end
36
+ end
37
+ end
@@ -46,7 +46,7 @@ module ProcessSettings
46
46
  def logger=(new_logger)
47
47
  ActiveSupport::Deprecation.warn("ProcessSettings::Monitor.logger is deprecated and will be removed in v1.0.")
48
48
  @logger = new_logger
49
- Listen.logger ||= new_logger
49
+ Listen.logger = new_logger unless Listen.instance_variable_get(:@logger)
50
50
  end
51
51
 
52
52
  deprecate :logger, :logger=, :file_path, :file_path=, deprecator: ActiveSupport::Deprecation.new('1.0', 'ProcessSettings')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ProcessSettings
4
- VERSION = '0.15.0'
4
+ VERSION = '0.18.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: process_settings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca
@@ -94,6 +94,7 @@ files:
94
94
  - lib/process_settings/file_monitor.rb
95
95
  - lib/process_settings/hash_path.rb
96
96
  - lib/process_settings/hash_with_hash_path.rb
97
+ - lib/process_settings/helpers/watchdog.rb
97
98
  - lib/process_settings/monitor.rb
98
99
  - lib/process_settings/replace_versioned_file.rb
99
100
  - lib/process_settings/settings.rb