julewire-core 1.0.0 → 1.1.0
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/CHANGELOG.md +29 -0
- data/docs/advanced-configuration.md +2 -1
- data/docs/configuration.md +14 -7
- data/docs/context-and-propagation.md +26 -4
- data/docs/contracts.md +25 -50
- data/docs/extensions-and-api.md +87 -88
- data/docs/health-schema.md +12 -2
- data/docs/internals.md +24 -23
- data/docs/outputs-and-lifecycle.md +15 -8
- data/docs/records-and-data-policy.md +38 -7
- data/lib/julewire/core/cli/doctor.rb +20 -16
- data/lib/julewire/core/cli/line_helpers.rb +23 -12
- data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
- data/lib/julewire/core/cli/log_formats/record_decoder.rb +1 -1
- data/lib/julewire/core/cli/log_formats.rb +9 -9
- data/lib/julewire/core/cli/tail.rb +19 -14
- data/lib/julewire/core/cli/transcode.rb +4 -5
- data/lib/julewire/core/cli.rb +1 -1
- data/lib/julewire/core/configuration.rb +1 -1
- data/lib/julewire/core/context_store.rb +56 -87
- data/lib/julewire/core/destinations/chaos_output.rb +2 -2
- data/lib/julewire/core/destinations/collection.rb +8 -16
- data/lib/julewire/core/destinations/definition.rb +1 -9
- data/lib/julewire/core/destinations/destination.rb +16 -66
- data/lib/julewire/core/destinations/processor_handling.rb +78 -0
- data/lib/julewire/core/destinations/registry.rb +1 -1
- data/lib/julewire/core/destinations/sink.rb +0 -1
- data/lib/julewire/core/destinations/synchronized_output.rb +54 -17
- data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
- data/lib/julewire/core/destinations/write_step.rb +5 -5
- data/lib/julewire/core/destinations.rb +0 -7
- data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
- data/lib/julewire/core/diagnostics/doctor.rb +22 -15
- data/lib/julewire/core/diagnostics/failure_snapshot.rb +9 -3
- data/lib/julewire/core/diagnostics/health.rb +41 -57
- data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
- data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
- data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
- data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
- data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
- data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
- data/lib/julewire/core/diagnostics/tail.rb +18 -22
- data/lib/julewire/core/execution/boundary.rb +2 -8
- data/lib/julewire/core/execution/handle.rb +14 -17
- data/lib/julewire/core/execution/lineage.rb +26 -62
- data/lib/julewire/core/execution/measurement_handle.rb +6 -10
- data/lib/julewire/core/execution/scope.rb +17 -22
- data/lib/julewire/core/execution/scope_fields.rb +4 -13
- data/lib/julewire/core/execution/scope_identity.rb +1 -9
- data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
- data/lib/julewire/core/execution/summary_state.rb +16 -19
- data/lib/julewire/core/execution/view.rb +8 -17
- data/lib/julewire/core/facade_methods.rb +67 -65
- data/lib/julewire/core/fields/attribute_keys.rb +0 -2
- data/lib/julewire/core/fields/bags.rb +23 -27
- data/lib/julewire/core/fields/field_set.rb +52 -16
- data/lib/julewire/core/fields/field_stack.rb +69 -109
- data/lib/julewire/core/fields/internal/deletion.rb +6 -9
- data/lib/julewire/core/fields/internal.rb +35 -26
- data/lib/julewire/core/fields/lookup.rb +9 -13
- data/lib/julewire/core/fields/section_proxy.rb +1 -9
- data/lib/julewire/core/fields/stack_set.rb +9 -14
- data/lib/julewire/core/fields/static_labels.rb +1 -1
- data/lib/julewire/core/integration/configurable.rb +4 -3
- data/lib/julewire/core/integration/destination_health.rb +28 -7
- data/lib/julewire/core/integration/event_subscriber.rb +1 -1
- data/lib/julewire/core/integration/facade.rb +12 -15
- data/lib/julewire/core/integration/fork_hooks.rb +12 -36
- data/lib/julewire/core/integration/health.rb +0 -3
- data/lib/julewire/core/integration/ivar_state.rb +0 -5
- data/lib/julewire/core/integration/protocol.rb +20 -0
- data/lib/julewire/core/integration/settings.rb +4 -4
- data/lib/julewire/core/integration/subscriber_install.rb +2 -2
- data/lib/julewire/core/integration/subscription.rb +3 -2
- data/lib/julewire/core/integration/values.rb +0 -17
- data/lib/julewire/core/local_storage.rb +3 -19
- data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
- data/lib/julewire/core/processing/level_threshold.rb +7 -14
- data/lib/julewire/core/processing/match.rb +6 -4
- data/lib/julewire/core/processing/pipeline.rb +46 -40
- data/lib/julewire/core/processing/processor_chain.rb +22 -11
- data/lib/julewire/core/processing/processor_registry.rb +3 -4
- data/lib/julewire/core/processing/record_field_transform.rb +0 -4
- data/lib/julewire/core/processing/sampling.rb +1 -3
- data/lib/julewire/core/processing.rb +2 -2
- data/lib/julewire/core/propagation/carrier.rb +73 -19
- data/lib/julewire/core/propagation.rb +18 -4
- data/lib/julewire/core/records/build_input.rb +75 -0
- data/lib/julewire/core/records/console_formatter.rb +6 -6
- data/lib/julewire/core/records/deconstruct.rb +1 -1
- data/lib/julewire/core/records/display_message.rb +2 -4
- data/lib/julewire/core/records/draft.rb +127 -258
- data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
- data/lib/julewire/core/records/metadata.rb +4 -3
- data/lib/julewire/core/records/public_projection.rb +2 -2
- data/lib/julewire/core/records/raw_input.rb +1 -1
- data/lib/julewire/core/records/record.rb +30 -18
- data/lib/julewire/core/records/severity.rb +7 -15
- data/lib/julewire/core/runtime.rb +87 -72
- data/lib/julewire/core/runtime_registry.rb +20 -15
- data/lib/julewire/core/runtime_state.rb +5 -3
- data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
- data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
- data/lib/julewire/core/serialization/bounded_transform.rb +3 -2
- data/lib/julewire/core/serialization/bounded_traversal.rb +72 -83
- data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
- data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
- data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
- data/lib/julewire/core/serialization/exception_shape.rb +7 -6
- data/lib/julewire/core/serialization/json_encoder.rb +10 -13
- data/lib/julewire/core/serialization/serializer.rb +22 -49
- data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
- data/lib/julewire/core/serialization/text_encoder.rb +5 -5
- data/lib/julewire/core/serialization/truncation_metadata.rb +157 -0
- data/lib/julewire/core/serialization/value_copy.rb +183 -109
- data/lib/julewire/core/serialization/value_traversal.rb +10 -127
- data/lib/julewire/core/testing.rb +6 -56
- data/lib/julewire/core/validation.rb +2 -3
- data/lib/julewire/core/version.rb +1 -1
- data/lib/julewire/core.rb +2 -7
- metadata +7 -19
- data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
- data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
- data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
- data/lib/julewire/core/testing/chaos/destination.rb +0 -55
- data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
- data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
- data/lib/julewire/core/testing/chaos.rb +0 -80
- data/lib/julewire/core/testing/contracts/component.rb +0 -162
- data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
- data/lib/julewire/core/testing/contracts/integration.rb +0 -166
- data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
- data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
- data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
- data/lib/julewire/core/testing/contracts/wire.rb +0 -60
- data/lib/julewire/core/testing/contracts.rb +0 -24
- data/lib/julewire/core/testing/coverage.rb +0 -58
- data/lib/julewire/core/testing/test_reports.rb +0 -78
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: julewire-core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Grebennik
|
|
@@ -87,6 +87,7 @@ files:
|
|
|
87
87
|
- lib/julewire/core/destinations/collection.rb
|
|
88
88
|
- lib/julewire/core/destinations/definition.rb
|
|
89
89
|
- lib/julewire/core/destinations/destination.rb
|
|
90
|
+
- lib/julewire/core/destinations/processor_handling.rb
|
|
90
91
|
- lib/julewire/core/destinations/registry.rb
|
|
91
92
|
- lib/julewire/core/destinations/sink.rb
|
|
92
93
|
- lib/julewire/core/destinations/synchronized_output.rb
|
|
@@ -139,6 +140,7 @@ files:
|
|
|
139
140
|
- lib/julewire/core/integration/health.rb
|
|
140
141
|
- lib/julewire/core/integration/ivar_state.rb
|
|
141
142
|
- lib/julewire/core/integration/lifecycle.rb
|
|
143
|
+
- lib/julewire/core/integration/protocol.rb
|
|
142
144
|
- lib/julewire/core/integration/scoped.rb
|
|
143
145
|
- lib/julewire/core/integration/settings.rb
|
|
144
146
|
- lib/julewire/core/integration/subscriber_install.rb
|
|
@@ -147,6 +149,7 @@ files:
|
|
|
147
149
|
- lib/julewire/core/lifecycle_error.rb
|
|
148
150
|
- lib/julewire/core/local_storage.rb
|
|
149
151
|
- lib/julewire/core/processing.rb
|
|
152
|
+
- lib/julewire/core/processing/invalid_result_failure.rb
|
|
150
153
|
- lib/julewire/core/processing/level_threshold.rb
|
|
151
154
|
- lib/julewire/core/processing/match.rb
|
|
152
155
|
- lib/julewire/core/processing/pipeline.rb
|
|
@@ -157,6 +160,7 @@ files:
|
|
|
157
160
|
- lib/julewire/core/processing/sampling.rb
|
|
158
161
|
- lib/julewire/core/propagation.rb
|
|
159
162
|
- lib/julewire/core/propagation/carrier.rb
|
|
163
|
+
- lib/julewire/core/records/build_input.rb
|
|
160
164
|
- lib/julewire/core/records/console_formatter.rb
|
|
161
165
|
- lib/julewire/core/records/deconstruct.rb
|
|
162
166
|
- lib/julewire/core/records/display_message.rb
|
|
@@ -173,7 +177,6 @@ files:
|
|
|
173
177
|
- lib/julewire/core/runtime_registry.rb
|
|
174
178
|
- lib/julewire/core/runtime_state.rb
|
|
175
179
|
- lib/julewire/core/scheduling/deadline.rb
|
|
176
|
-
- lib/julewire/core/scheduling/deadline_scheduler.rb
|
|
177
180
|
- lib/julewire/core/scheduling/shared_scheduler.rb
|
|
178
181
|
- lib/julewire/core/sentinel.rb
|
|
179
182
|
- lib/julewire/core/serialization/backtrace_limiter.rb
|
|
@@ -187,25 +190,10 @@ files:
|
|
|
187
190
|
- lib/julewire/core/serialization/serializer.rb
|
|
188
191
|
- lib/julewire/core/serialization/serializer_pool.rb
|
|
189
192
|
- lib/julewire/core/serialization/text_encoder.rb
|
|
193
|
+
- lib/julewire/core/serialization/truncation_metadata.rb
|
|
190
194
|
- lib/julewire/core/serialization/value_copy.rb
|
|
191
195
|
- lib/julewire/core/serialization/value_traversal.rb
|
|
192
196
|
- lib/julewire/core/testing.rb
|
|
193
|
-
- lib/julewire/core/testing/chaos.rb
|
|
194
|
-
- lib/julewire/core/testing/chaos/catalog.rb
|
|
195
|
-
- lib/julewire/core/testing/chaos/core_runtime.rb
|
|
196
|
-
- lib/julewire/core/testing/chaos/destination.rb
|
|
197
|
-
- lib/julewire/core/testing/chaos/emitter.rb
|
|
198
|
-
- lib/julewire/core/testing/chaos/raising_output.rb
|
|
199
|
-
- lib/julewire/core/testing/contracts.rb
|
|
200
|
-
- lib/julewire/core/testing/contracts/component.rb
|
|
201
|
-
- lib/julewire/core/testing/contracts/deadline_scheduler.rb
|
|
202
|
-
- lib/julewire/core/testing/contracts/integration.rb
|
|
203
|
-
- lib/julewire/core/testing/contracts/integration_fields.rb
|
|
204
|
-
- lib/julewire/core/testing/contracts/record_draft.rb
|
|
205
|
-
- lib/julewire/core/testing/contracts/runtime.rb
|
|
206
|
-
- lib/julewire/core/testing/contracts/wire.rb
|
|
207
|
-
- lib/julewire/core/testing/coverage.rb
|
|
208
|
-
- lib/julewire/core/testing/test_reports.rb
|
|
209
197
|
- lib/julewire/core/validation.rb
|
|
210
198
|
- lib/julewire/core/version.rb
|
|
211
199
|
- lib/julewire/error.rb
|
|
@@ -231,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
231
219
|
- !ruby/object:Gem::Version
|
|
232
220
|
version: '0'
|
|
233
221
|
requirements: []
|
|
234
|
-
rubygems_version: 4.0.
|
|
222
|
+
rubygems_version: 4.0.16
|
|
235
223
|
specification_version: 4
|
|
236
224
|
summary: Execution-scoped structured logging core for Ruby applications.
|
|
237
225
|
test_files: []
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Scheduling
|
|
6
|
-
# @api integration_spi
|
|
7
|
-
class DeadlineScheduler
|
|
8
|
-
CLOCK = Process::CLOCK_MONOTONIC
|
|
9
|
-
Entry = Data.define(:deadline, :token, :callback)
|
|
10
|
-
|
|
11
|
-
def initialize(thread_name:, idle: :keep_alive)
|
|
12
|
-
@thread_name = thread_name
|
|
13
|
-
@idle = idle
|
|
14
|
-
@mutex = Mutex.new
|
|
15
|
-
@condition = ConditionVariable.new
|
|
16
|
-
@entries = {}
|
|
17
|
-
# A heap keeps timeout scheduling cheap without non-shareable scheduler dependencies.
|
|
18
|
-
@heap = []
|
|
19
|
-
@next_token = 0
|
|
20
|
-
@generation = 0
|
|
21
|
-
@pid = Process.pid
|
|
22
|
-
@thread = nil
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def schedule(timeout, &block)
|
|
26
|
-
raise ArgumentError, "block required" unless block
|
|
27
|
-
|
|
28
|
-
timeout = Float(timeout)
|
|
29
|
-
if timeout <= 0
|
|
30
|
-
yield
|
|
31
|
-
return
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
@mutex.synchronize do
|
|
35
|
-
token = next_token
|
|
36
|
-
entry = Entry.new(monotonic_time + timeout, token, block)
|
|
37
|
-
@entries[token] = entry
|
|
38
|
-
heap_push(entry)
|
|
39
|
-
ensure_thread
|
|
40
|
-
@condition.signal
|
|
41
|
-
token
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def cancel(token)
|
|
46
|
-
return unless token
|
|
47
|
-
|
|
48
|
-
@mutex.synchronize do
|
|
49
|
-
@entries.delete(token)
|
|
50
|
-
@condition.signal
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def after_fork!
|
|
55
|
-
if @pid == Process.pid
|
|
56
|
-
reset_same_process
|
|
57
|
-
else
|
|
58
|
-
reset_after_fork
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
self
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
private
|
|
65
|
-
|
|
66
|
-
def reset_same_process
|
|
67
|
-
@mutex.synchronize do
|
|
68
|
-
@generation += 1
|
|
69
|
-
@entries = {}
|
|
70
|
-
@heap = []
|
|
71
|
-
@next_token = 0
|
|
72
|
-
@thread = nil
|
|
73
|
-
@condition.broadcast
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def reset_after_fork
|
|
78
|
-
@mutex = Mutex.new
|
|
79
|
-
@condition = ConditionVariable.new
|
|
80
|
-
@entries = {}
|
|
81
|
-
@heap = []
|
|
82
|
-
@next_token = 0
|
|
83
|
-
@generation += 1
|
|
84
|
-
@pid = Process.pid
|
|
85
|
-
@thread = nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def next_token
|
|
89
|
-
@next_token += 1
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def ensure_thread
|
|
93
|
-
return if @thread&.alive?
|
|
94
|
-
|
|
95
|
-
generation = @generation
|
|
96
|
-
@thread = Thread.new { run(generation) }
|
|
97
|
-
@thread.name = @thread_name
|
|
98
|
-
@thread.report_on_exception = false
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def run(generation)
|
|
102
|
-
loop do
|
|
103
|
-
callback = next_expired_callback(generation)
|
|
104
|
-
return unless callback
|
|
105
|
-
|
|
106
|
-
safe_call(callback)
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def next_expired_callback(generation)
|
|
111
|
-
@mutex.synchronize do
|
|
112
|
-
loop do
|
|
113
|
-
return unless generation == @generation
|
|
114
|
-
|
|
115
|
-
discard_cancelled_head
|
|
116
|
-
if @heap.empty?
|
|
117
|
-
return clear_thread if exit_when_idle?
|
|
118
|
-
|
|
119
|
-
@condition.wait(@mutex)
|
|
120
|
-
next
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
entry = @heap.fetch(0)
|
|
124
|
-
remaining = entry.deadline - monotonic_time
|
|
125
|
-
if remaining.positive?
|
|
126
|
-
@condition.wait(@mutex, remaining)
|
|
127
|
-
else
|
|
128
|
-
heap_pop
|
|
129
|
-
@entries.delete(entry.token)
|
|
130
|
-
return entry.callback
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def discard_cancelled_head
|
|
137
|
-
heap_pop while @heap.any? && !@entries.key?(@heap.fetch(0).token)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def clear_thread
|
|
141
|
-
@thread = nil
|
|
142
|
-
nil
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def exit_when_idle?
|
|
146
|
-
@idle == :exit
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def safe_call(callback)
|
|
150
|
-
callback.call
|
|
151
|
-
rescue StandardError
|
|
152
|
-
nil
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def monotonic_time
|
|
156
|
-
Process.clock_gettime(CLOCK)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def heap_push(entry)
|
|
160
|
-
@heap << entry
|
|
161
|
-
sift_up(@heap.length - 1)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def heap_pop
|
|
165
|
-
return @heap.pop if @heap.one?
|
|
166
|
-
|
|
167
|
-
top = @heap.fetch(0)
|
|
168
|
-
@heap[0] = @heap.pop
|
|
169
|
-
sift_down(0)
|
|
170
|
-
top
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def sift_up(index)
|
|
174
|
-
while index.positive?
|
|
175
|
-
parent = (index - 1) / 2
|
|
176
|
-
break if earlier_or_equal?(@heap.fetch(parent), @heap.fetch(index))
|
|
177
|
-
|
|
178
|
-
swap_heap(index, parent)
|
|
179
|
-
index = parent
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def sift_down(index)
|
|
184
|
-
loop do
|
|
185
|
-
left = (index * 2) + 1
|
|
186
|
-
right = left + 1
|
|
187
|
-
smallest = index
|
|
188
|
-
smallest = left if left < @heap.length && earlier_or_equal?(@heap.fetch(left), @heap.fetch(smallest))
|
|
189
|
-
smallest = right if right < @heap.length && earlier_or_equal?(@heap.fetch(right), @heap.fetch(smallest))
|
|
190
|
-
break if smallest == index
|
|
191
|
-
|
|
192
|
-
swap_heap(index, smallest)
|
|
193
|
-
index = smallest
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def earlier_or_equal?(left, right)
|
|
198
|
-
left.deadline < right.deadline || (left.deadline == right.deadline && left.token <= right.token)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def swap_heap(left, right)
|
|
202
|
-
@heap[left], @heap[right] = @heap[right], @heap[left]
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
module Chaos
|
|
7
|
-
class Catalog
|
|
8
|
-
Entry = Data.define(:kind, :name, :exercise)
|
|
9
|
-
KINDS = %i[processor formatter encoder destination subscriber listener].freeze
|
|
10
|
-
|
|
11
|
-
attr_reader :entries
|
|
12
|
-
|
|
13
|
-
class << self
|
|
14
|
-
def build
|
|
15
|
-
catalog = new
|
|
16
|
-
yield catalog if block_given?
|
|
17
|
-
catalog
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def assert_contract(test_context, catalog:, errors:)
|
|
21
|
-
entries = catalog.entries
|
|
22
|
-
raise ArgumentError, "chaos catalog must have entries" if entries.empty?
|
|
23
|
-
|
|
24
|
-
entries.each do |entry|
|
|
25
|
-
assert_entry(test_context, entry, errors)
|
|
26
|
-
end
|
|
27
|
-
nil
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def assert_entry(test_context, entry, errors)
|
|
33
|
-
Chaos.assert_contained(
|
|
34
|
-
test_context,
|
|
35
|
-
errors: errors,
|
|
36
|
-
description: "#{entry.kind} #{entry.name}"
|
|
37
|
-
) do |error|
|
|
38
|
-
entry.exercise.call(error)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def initialize
|
|
44
|
-
@entries = []
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def processor(name, &) = register(:processor, name, &)
|
|
48
|
-
|
|
49
|
-
def formatter(name, &) = register(:formatter, name, &)
|
|
50
|
-
|
|
51
|
-
def encoder(name, &) = register(:encoder, name, &)
|
|
52
|
-
|
|
53
|
-
def destination(name, &) = register(:destination, name, &)
|
|
54
|
-
|
|
55
|
-
def subscriber(name, &) = register(:subscriber, name, &)
|
|
56
|
-
|
|
57
|
-
def listener(name, &) = register(:listener, name, &)
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def register(kind, name, &exercise)
|
|
62
|
-
raise ArgumentError, "unknown chaos component kind #{kind.inspect}" unless KINDS.include?(kind)
|
|
63
|
-
raise ArgumentError, "chaos component exercise block required" unless exercise
|
|
64
|
-
|
|
65
|
-
@entries << Entry.new(kind, Core.normalize_name(name), exercise)
|
|
66
|
-
self
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
module Chaos
|
|
7
|
-
module CoreRuntime
|
|
8
|
-
SCENARIOS = %i[
|
|
9
|
-
destination_processor
|
|
10
|
-
drop_callback
|
|
11
|
-
encoder
|
|
12
|
-
failure_callback
|
|
13
|
-
formatter
|
|
14
|
-
lifecycle_after_fork
|
|
15
|
-
lifecycle_flush
|
|
16
|
-
output
|
|
17
|
-
pipeline_processor
|
|
18
|
-
].freeze
|
|
19
|
-
|
|
20
|
-
private_constant :SCENARIOS
|
|
21
|
-
|
|
22
|
-
class << self
|
|
23
|
-
def assert_contract(test_context, runtime:, reset:, errors:)
|
|
24
|
-
SCENARIOS.each do |scenario|
|
|
25
|
-
errors.each do |error|
|
|
26
|
-
assert_scenario(test_context, runtime, reset, scenario, error)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
def assert_scenario(test_context, runtime, reset, scenario, error)
|
|
35
|
-
reset.call
|
|
36
|
-
send(:"exercise_#{scenario}", runtime, error)
|
|
37
|
-
rescue StandardError => e
|
|
38
|
-
test_context.flunk(
|
|
39
|
-
"expected core #{scenario} failure to be contained, " \
|
|
40
|
-
"#{error.class} leaked #{e.class}: #{e.message}"
|
|
41
|
-
)
|
|
42
|
-
ensure
|
|
43
|
-
reset.call
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def exercise_destination_processor(runtime, error)
|
|
47
|
-
configure_destination(runtime, processors: [Chaos.raiser(error)])
|
|
48
|
-
runtime.emit("chaos")
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def exercise_drop_callback(runtime, error)
|
|
52
|
-
trigger = RuntimeError.new("julewire chaos formatter trigger")
|
|
53
|
-
configure_destination(runtime, formatter: Chaos.raiser(trigger), on_drop: Chaos.raiser(error))
|
|
54
|
-
runtime.emit("chaos")
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def exercise_encoder(runtime, error)
|
|
58
|
-
configure_destination(runtime, encoder: Chaos.raiser(error))
|
|
59
|
-
runtime.emit("chaos")
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def exercise_failure_callback(runtime, error)
|
|
63
|
-
trigger = RuntimeError.new("julewire chaos output trigger")
|
|
64
|
-
configure_destination(
|
|
65
|
-
runtime,
|
|
66
|
-
output: RaisingOutput.new(trigger, failures: %i[write]),
|
|
67
|
-
runtime_on_failure: Chaos.raiser(error)
|
|
68
|
-
)
|
|
69
|
-
runtime.emit("chaos")
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def exercise_formatter(runtime, error)
|
|
73
|
-
configure_destination(runtime, formatter: Chaos.raiser(error))
|
|
74
|
-
runtime.emit("chaos")
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def exercise_lifecycle_after_fork(runtime, error)
|
|
78
|
-
configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[after_fork]))
|
|
79
|
-
runtime.after_fork!
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def exercise_lifecycle_flush(runtime, error)
|
|
83
|
-
configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[flush]))
|
|
84
|
-
runtime.flush
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def exercise_output(runtime, error)
|
|
88
|
-
configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[write]))
|
|
89
|
-
runtime.emit("chaos")
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def exercise_pipeline_processor(runtime, error)
|
|
93
|
-
runtime.configure do |config|
|
|
94
|
-
config.destinations.use(:default, output: NullOutput.new)
|
|
95
|
-
config.processors.use(Chaos.raiser(error))
|
|
96
|
-
end
|
|
97
|
-
runtime.emit("chaos")
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def configure_destination(runtime, output: NullOutput.new, formatter: nil, encoder: nil,
|
|
101
|
-
on_drop: nil, processors: nil, runtime_on_failure: nil)
|
|
102
|
-
runtime.configure do |config|
|
|
103
|
-
config.on_failure = runtime_on_failure if runtime_on_failure
|
|
104
|
-
config.destinations.clear
|
|
105
|
-
config.destinations.use(
|
|
106
|
-
:default,
|
|
107
|
-
encoder: encoder || Julewire::Core::Serialization::JsonEncoder.new,
|
|
108
|
-
formatter: formatter || Julewire::Core::Records::Formatter.new,
|
|
109
|
-
on_drop: on_drop,
|
|
110
|
-
output: output,
|
|
111
|
-
processors: processors || []
|
|
112
|
-
)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
module Chaos
|
|
7
|
-
module Destination
|
|
8
|
-
class << self
|
|
9
|
-
def assert_contract(test_context, record:, formatter:, encoder:, output:, callbacks:, errors:)
|
|
10
|
-
{
|
|
11
|
-
formatter: formatter,
|
|
12
|
-
encoder: encoder,
|
|
13
|
-
output: output,
|
|
14
|
-
callbacks: callbacks
|
|
15
|
-
}.compact.each do |scenario, builder|
|
|
16
|
-
assert_scenario(test_context, scenario, builder, record, errors)
|
|
17
|
-
end
|
|
18
|
-
nil
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def assert_scenario(test_context, scenario, builder, record, errors)
|
|
24
|
-
errors.each do |error|
|
|
25
|
-
assert_error_contained(test_context, scenario, builder, record, error)
|
|
26
|
-
end
|
|
27
|
-
nil
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def assert_error_contained(test_context, scenario, builder, record, error)
|
|
31
|
-
Chaos.assert_contained(
|
|
32
|
-
test_context,
|
|
33
|
-
errors: [error],
|
|
34
|
-
description: "destination #{scenario}"
|
|
35
|
-
) do |build_error|
|
|
36
|
-
destination = builder.call(build_error)
|
|
37
|
-
destination.emit(record)
|
|
38
|
-
ensure
|
|
39
|
-
close_destination(destination)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def close_destination(destination)
|
|
44
|
-
return unless destination.respond_to?(:close)
|
|
45
|
-
|
|
46
|
-
destination.close(timeout: 0)
|
|
47
|
-
rescue StandardError
|
|
48
|
-
nil
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
module Chaos
|
|
7
|
-
module Emitter
|
|
8
|
-
class << self
|
|
9
|
-
def assert_contract(test_context, component:, build:, exercise:, errors:)
|
|
10
|
-
Chaos.assert_contained(test_context, errors: errors, description: component) do |error|
|
|
11
|
-
emitter = build.call(error)
|
|
12
|
-
exercise.call(emitter, error)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
module Chaos
|
|
7
|
-
class RaisingOutput
|
|
8
|
-
def initialize(error, failures:)
|
|
9
|
-
@error = error
|
|
10
|
-
@failures = failures
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def write(value)
|
|
14
|
-
raise @error if @failures.include?(:write)
|
|
15
|
-
|
|
16
|
-
value.bytesize
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def flush
|
|
20
|
-
raise @error if @failures.include?(:flush)
|
|
21
|
-
|
|
22
|
-
self
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def close
|
|
26
|
-
raise @error if @failures.include?(:close)
|
|
27
|
-
|
|
28
|
-
self
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def after_fork!
|
|
32
|
-
raise @error if @failures.include?(:after_fork)
|
|
33
|
-
|
|
34
|
-
self
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private_constant :RaisingOutput
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Julewire
|
|
4
|
-
module Core
|
|
5
|
-
module Testing
|
|
6
|
-
# @api extension
|
|
7
|
-
module Chaos
|
|
8
|
-
DEFAULT_ERRORS = [
|
|
9
|
-
RuntimeError.new("julewire chaos runtime"),
|
|
10
|
-
ArgumentError.new("julewire chaos argument"),
|
|
11
|
-
TypeError.new("julewire chaos type")
|
|
12
|
-
].freeze
|
|
13
|
-
class << self
|
|
14
|
-
def assert_contained(test_context, errors: DEFAULT_ERRORS, description: nil)
|
|
15
|
-
raise ArgumentError, "block required" unless block_given?
|
|
16
|
-
|
|
17
|
-
errors.each do |error|
|
|
18
|
-
yield error
|
|
19
|
-
rescue StandardError => e
|
|
20
|
-
test_context.flunk(containment_message(description, error, e))
|
|
21
|
-
end
|
|
22
|
-
nil
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def assert_core_runtime_containment(test_context, runtime: Julewire, reset: nil, errors: DEFAULT_ERRORS)
|
|
26
|
-
reset ||= -> { runtime.reset! }
|
|
27
|
-
raise ArgumentError, "reset must respond to call" unless reset.respond_to?(:call)
|
|
28
|
-
|
|
29
|
-
CoreRuntime.assert_contract(test_context, runtime: runtime, reset: reset, errors: errors)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def catalog(&)
|
|
33
|
-
Catalog.build(&)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def assert_discovered_chaos_contracts(test_context, catalog:, errors: DEFAULT_ERRORS)
|
|
37
|
-
Catalog.assert_contract(test_context, catalog: catalog, errors: errors)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def assert_destination_chaos_contract(test_context, record:, formatter:, encoder:, output:,
|
|
41
|
-
callbacks: nil, errors: DEFAULT_ERRORS)
|
|
42
|
-
Destination.assert_contract(
|
|
43
|
-
test_context,
|
|
44
|
-
record: record,
|
|
45
|
-
formatter: formatter,
|
|
46
|
-
encoder: encoder,
|
|
47
|
-
output: output,
|
|
48
|
-
callbacks: callbacks,
|
|
49
|
-
errors: errors
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def assert_emitter_chaos_contract(test_context, component:, build:, exercise:, errors: DEFAULT_ERRORS)
|
|
54
|
-
Emitter.assert_contract(
|
|
55
|
-
test_context,
|
|
56
|
-
component: component,
|
|
57
|
-
build: build,
|
|
58
|
-
exercise: exercise,
|
|
59
|
-
errors: errors
|
|
60
|
-
)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def raiser(error = RuntimeError.new("julewire chaos"))
|
|
64
|
-
->(*) { raise error }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
def containment_message(description, expected_error, leaked_error)
|
|
70
|
-
unless description
|
|
71
|
-
return "expected #{expected_error.class} to be contained, leaked #{leaked_error.class}: #{leaked_error}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
"expected #{description} chaos to be contained, leaked #{leaked_error.class}: #{leaked_error}"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|