process_settings 0.14.0 → 0.18.0.pre.1
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 +4 -4
- data/README.md +14 -1
- data/bin/combine_process_settings +31 -8
- data/bin/diff_process_settings +1 -4
- data/lib/process_settings/abstract_monitor.rb +29 -5
- data/lib/process_settings/file_monitor.rb +32 -1
- data/lib/process_settings/helpers/watchdog.rb +37 -0
- data/lib/process_settings/monitor.rb +1 -1
- data/lib/process_settings/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15a15a0c5c7ea497858038e4b4be06e63aea209c8e3033662c28963d19839d39
|
4
|
+
data.tar.gz: ba4e23e5216b60ecc7e5e999dcac7d1679c90fd2cf6b712624da9a04aa629c1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44058ab68dbb74cdf889bd57ac0bff0324f35f4608a27f67623da3b17a8ee0b4f9bfef24311395e969f941db28c2a5137c79c3233db75659fcc9e1f4239797e1
|
7
|
+
data.tar.gz: 3310afc60c343999207561d0e2820dc6ff4e985f71b1e25b8402fe7f3f39c762dd560f83cf78a5ccd57fa937a13c9dfdb43f41e57e8aa3326eb3a63d05f628bd
|
data/README.md
CHANGED
@@ -167,6 +167,19 @@ 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
|
+
|
170
183
|
### Testing
|
171
184
|
For testing, it is often necessary to set a specific override hash for the process_settings values to use in
|
172
185
|
that use case. The `ProcessSettings::Testing::RSpec::Helpers` and `ProcessSettings::Testing::Minitest::Helpers` modules are provided for this purpose.
|
@@ -181,7 +194,7 @@ require 'process_settings/testing/helpers'
|
|
181
194
|
RSpec.configure do |config|
|
182
195
|
# ...
|
183
196
|
|
184
|
-
include ProcessSettings::Testing::RSpec::Helpers
|
197
|
+
config.include ProcessSettings::Testing::RSpec::Helpers
|
185
198
|
|
186
199
|
# Note: the include above will automatically register a global after block that will reset process_settings to their initial values.
|
187
200
|
# ...
|
@@ -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
|
-
|
119
|
-
if
|
120
|
-
|
121
|
-
|
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
|
-
|
124
|
-
mv
|
125
|
-
|
126
|
-
|
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)
|
data/bin/diff_process_settings
CHANGED
@@ -11,14 +11,16 @@ module ProcessSettings
|
|
11
11
|
OnChangeDeprecation = ActiveSupport::Deprecation.new('1.0', 'ProcessSettings::Monitor')
|
12
12
|
|
13
13
|
class AbstractMonitor
|
14
|
+
|
14
15
|
attr_reader :min_polling_seconds, :logger
|
15
|
-
attr_reader :static_context, :statically_targeted_settings
|
16
|
+
attr_reader :static_context, :statically_targeted_settings, :full_context_cache
|
16
17
|
|
17
18
|
def initialize(logger:)
|
18
19
|
@logger = logger or raise ArgumentError, "logger must be not be nil"
|
19
20
|
@on_change_callbacks = []
|
20
21
|
@when_updated_blocks = Set.new
|
21
22
|
@static_context = {}
|
23
|
+
@full_context_cache = {}
|
22
24
|
end
|
23
25
|
|
24
26
|
# This is the main entry point for looking up settings on the Monitor instance.
|
@@ -94,10 +96,20 @@ module ProcessSettings
|
|
94
96
|
def targeted_value(*path, dynamic_context:, required: true)
|
95
97
|
# Merging the static context in is necessary to make sure that the static context isn't shifting
|
96
98
|
# this can be rather costly to do every time if the dynamic context is not changing
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
99
|
+
|
100
|
+
# Warn in the case where dynamic context was attempting to change a static value
|
101
|
+
changes = dynamic_context.each_with_object({}) do |(key, dynamic_value), result|
|
102
|
+
if static_context.has_key?(key)
|
103
|
+
static_value = static_context[key]
|
104
|
+
if static_value != dynamic_value
|
105
|
+
result[key] = [static_value, dynamic_value]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
changes.empty? or warn("WARNING: static context overwritten by dynamic!\n#{changes.inspect}")
|
111
|
+
|
112
|
+
full_context = full_context_from_cache(dynamic_context)
|
101
113
|
result = statically_targeted_settings.reduce(:not_found) do |latest_result, target_and_settings|
|
102
114
|
# find last value from matching targets
|
103
115
|
if target_and_settings.target.target_key_matches?(full_context)
|
@@ -119,6 +131,18 @@ module ProcessSettings
|
|
119
131
|
end
|
120
132
|
end
|
121
133
|
|
134
|
+
def full_context_from_cache(dynamic_context)
|
135
|
+
if (full_context = full_context_cache[dynamic_context])
|
136
|
+
full_context
|
137
|
+
else
|
138
|
+
dynamic_context.deep_merge(static_context).tap do |full_context|
|
139
|
+
if full_context_cache.size <= 1000
|
140
|
+
full_context_cache[dynamic_context] = full_context
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
122
146
|
private
|
123
147
|
|
124
148
|
class << self
|
@@ -8,6 +8,8 @@ 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'
|
12
|
+
require 'exception_handling'
|
11
13
|
|
12
14
|
module ProcessSettings
|
13
15
|
class FileMonitor < AbstractMonitor
|
@@ -24,10 +26,39 @@ module ProcessSettings
|
|
24
26
|
start_internal(enable_listen_thread?(environment))
|
25
27
|
end
|
26
28
|
|
29
|
+
def start_watchdog_thread(file_path = :missing)
|
30
|
+
if file_path == :missing
|
31
|
+
ActiveSupport::Deprecation.warn("ProcessEnvSetup.start_watchdog_thread with no arguments is deprecated")
|
32
|
+
file_path = ProcessSettings::Monitor.file_path
|
33
|
+
end
|
34
|
+
|
35
|
+
@watchdog_thread and raise ArgumentError, "watchdog thread already running!"
|
36
|
+
@watchdog_thread = Thread.new do
|
37
|
+
watchdog = ProcessSettings::Watchdog.new(file_path)
|
38
|
+
loop do
|
39
|
+
ExceptionHandling.ensure_safe("ProcessSettings::Watchdog thread") do
|
40
|
+
sleep(1.minute)
|
41
|
+
watchdog.check
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def stop_watchdog_thread
|
48
|
+
if @watchdog_thread
|
49
|
+
Thread.kill(@watchdog_thread)
|
50
|
+
@watchdog_thread = nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
27
54
|
def start
|
28
55
|
start_internal(enable_listen_thread?)
|
29
56
|
end
|
30
|
-
deprecate :
|
57
|
+
deprecate start: :restart_after_fork, deprecator: ActiveSupport::Deprecation.new('1.0', 'ProcessSettings')
|
58
|
+
|
59
|
+
def restart_after_fork
|
60
|
+
start_internal(enable_listen_thread?)
|
61
|
+
end
|
31
62
|
|
32
63
|
def listen_thread_running?
|
33
64
|
!@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
|
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')
|
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.
|
4
|
+
version: 0.18.0.pre.1
|
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
|
@@ -122,9 +123,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
123
|
version: '0'
|
123
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
125
|
requirements:
|
125
|
-
- - "
|
126
|
+
- - ">"
|
126
127
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
128
|
+
version: 1.3.1
|
128
129
|
requirements: []
|
129
130
|
rubygems_version: 3.0.3
|
130
131
|
signing_key:
|