julewire-core 1.0.1 → 1.1.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/CHANGELOG.md +22 -0
- data/docs/configuration.md +14 -7
- data/docs/context-and-propagation.md +20 -0
- data/docs/contracts.md +23 -49
- data/docs/extensions-and-api.md +78 -82
- data/docs/health-schema.md +12 -2
- data/docs/internals.md +21 -20
- data/docs/outputs-and-lifecycle.md +10 -6
- data/docs/records-and-data-policy.md +36 -5
- 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.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 +51 -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 +13 -63
- 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 +14 -13
- 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 +16 -9
- data/lib/julewire/core/diagnostics/failure_snapshot.rb +6 -2
- 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 +12 -17
- data/lib/julewire/core/execution/lineage.rb +16 -56
- data/lib/julewire/core/execution/measurement_handle.rb +6 -10
- data/lib/julewire/core/execution/scope.rb +15 -20
- data/lib/julewire/core/execution/scope_fields.rb +3 -12
- 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 +29 -19
- data/lib/julewire/core/fields/field_stack.rb +68 -134
- data/lib/julewire/core/fields/internal/deletion.rb +6 -9
- data/lib/julewire/core/fields/internal.rb +19 -38
- 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 +7 -12
- 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 +17 -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 +14 -14
- data/lib/julewire/core/propagation.rb +15 -2
- 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 +125 -262
- 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 +19 -37
- data/lib/julewire/core/records/severity.rb +7 -15
- data/lib/julewire/core/runtime.rb +67 -71
- 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 +2 -12
- data/lib/julewire/core/serialization/bounded_traversal.rb +49 -66
- 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 +23 -50
- 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 +63 -48
- data/lib/julewire/core/serialization/value_copy.rb +60 -227
- 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 +6 -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
|
@@ -6,8 +6,8 @@ module Julewire
|
|
|
6
6
|
# Fiber-local context stack used by the runtime facade. Use Julewire.context
|
|
7
7
|
# and Julewire.with_execution instead of reaching into this class directly.
|
|
8
8
|
class ContextStore # rubocop:disable Metrics/ClassLength
|
|
9
|
-
|
|
10
|
-
private_constant :
|
|
9
|
+
PropagationOverlay = Data.define(:execution, :link_executions)
|
|
10
|
+
private_constant :PropagationOverlay
|
|
11
11
|
|
|
12
12
|
class << self
|
|
13
13
|
def current
|
|
@@ -26,10 +26,7 @@ module Julewire
|
|
|
26
26
|
@scopes = []
|
|
27
27
|
@ambient_fields = Fields::StackSet.new
|
|
28
28
|
@execution_overlays = []
|
|
29
|
-
@execution_lineage_overlays = []
|
|
30
29
|
@propagation_execution_hash = nil
|
|
31
|
-
@propagation_scope_snapshot = nil
|
|
32
|
-
@linked_propagation_scope_snapshot = nil
|
|
33
30
|
end
|
|
34
31
|
|
|
35
32
|
def current_scope = @scopes.last
|
|
@@ -85,26 +82,24 @@ module Julewire
|
|
|
85
82
|
current_field_stack(:attributes).value_for(key, default: default)
|
|
86
83
|
end
|
|
87
84
|
|
|
88
|
-
def add_context(fields =
|
|
85
|
+
def add_context(fields = nil, owned: false, **keyword_fields)
|
|
89
86
|
add_field(:context, field_input(fields, keyword_fields), owned: owned)
|
|
90
87
|
end
|
|
91
88
|
|
|
92
|
-
def add_carry(fields =
|
|
89
|
+
def add_carry(fields = nil, owned: false, **keyword_fields)
|
|
93
90
|
add_field(:carry, field_input(fields, keyword_fields), owned: owned)
|
|
94
91
|
end
|
|
95
92
|
|
|
96
|
-
def add_attributes(fields =
|
|
93
|
+
def add_attributes(fields = nil, owned: false, **keyword_fields)
|
|
97
94
|
add_field(:attributes, field_input(fields, keyword_fields), owned: owned)
|
|
98
95
|
end
|
|
99
96
|
|
|
100
|
-
def add_neutral(fields =
|
|
97
|
+
def add_neutral(fields = nil, owned: false, **keyword_fields)
|
|
101
98
|
add_field(:neutral, field_input(fields, keyword_fields), owned: owned)
|
|
102
99
|
end
|
|
103
100
|
|
|
104
101
|
def delete_carry(path)
|
|
105
102
|
path = Fields::Internal.normalize_path(path)
|
|
106
|
-
return if path.empty?
|
|
107
|
-
|
|
108
103
|
if current_scope
|
|
109
104
|
current_scope.delete_carry(path)
|
|
110
105
|
else
|
|
@@ -112,19 +107,19 @@ module Julewire
|
|
|
112
107
|
end
|
|
113
108
|
end
|
|
114
109
|
|
|
115
|
-
def with_context(fields =
|
|
110
|
+
def with_context(fields = nil, owned: false, **keyword_fields, &)
|
|
116
111
|
with_scope_or_ambient_overlay(:context, field_input(fields, keyword_fields), owned: owned, &)
|
|
117
112
|
end
|
|
118
113
|
|
|
119
|
-
def with_carry(fields =
|
|
114
|
+
def with_carry(fields = nil, owned: false, **keyword_fields, &)
|
|
120
115
|
with_scope_or_ambient_overlay(:carry, field_input(fields, keyword_fields), owned: owned, &)
|
|
121
116
|
end
|
|
122
117
|
|
|
123
|
-
def with_attributes(fields =
|
|
118
|
+
def with_attributes(fields = nil, owned: false, **keyword_fields, &)
|
|
124
119
|
with_scope_or_ambient_overlay(:attributes, field_input(fields, keyword_fields), owned: owned, &)
|
|
125
120
|
end
|
|
126
121
|
|
|
127
|
-
def with_neutral(fields =
|
|
122
|
+
def with_neutral(fields = nil, owned: false, **keyword_fields, &)
|
|
128
123
|
with_scope_or_ambient_overlay(:neutral, field_input(fields, keyword_fields), owned: owned, &)
|
|
129
124
|
end
|
|
130
125
|
|
|
@@ -140,30 +135,21 @@ module Julewire
|
|
|
140
135
|
end
|
|
141
136
|
end
|
|
142
137
|
|
|
143
|
-
def with_propagation(context
|
|
144
|
-
scope = current_scope
|
|
138
|
+
def with_propagation(context:, carry:, execution:, link_executions:, owned:, &)
|
|
145
139
|
execution = if owned
|
|
146
|
-
|
|
140
|
+
Serialization::DeepFreeze.validate_symbol_hash(execution)
|
|
147
141
|
else
|
|
148
142
|
Fields::FieldSet.deep_symbolize_keys(execution)
|
|
149
143
|
end
|
|
150
|
-
@execution_overlays.push(execution)
|
|
151
|
-
@execution_lineage_overlays.push(link_executions ? Execution::Lineage.from_execution_hash(execution) : nil)
|
|
144
|
+
@execution_overlays.push(PropagationOverlay.new(execution: execution, link_executions: link_executions))
|
|
152
145
|
invalidate_propagation_cache!
|
|
153
146
|
|
|
154
147
|
begin
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
scope.with_context(context, owned: owned, &)
|
|
158
|
-
end
|
|
159
|
-
else
|
|
160
|
-
@ambient_fields.with(:carry, carry, owned: owned) do
|
|
161
|
-
@ambient_fields.with(:context, context, owned: owned, &)
|
|
162
|
-
end
|
|
148
|
+
with_carry(carry, owned: owned) do
|
|
149
|
+
with_context(context, owned: owned, &)
|
|
163
150
|
end
|
|
164
151
|
ensure
|
|
165
152
|
@execution_overlays.pop
|
|
166
|
-
@execution_lineage_overlays.pop
|
|
167
153
|
invalidate_propagation_cache!
|
|
168
154
|
end
|
|
169
155
|
end
|
|
@@ -214,12 +200,12 @@ module Julewire
|
|
|
214
200
|
Execution::Scope.new(
|
|
215
201
|
type: options.fetch(:type),
|
|
216
202
|
id: options[:id],
|
|
217
|
-
execution: merged_execution_hash(options.fetch(:execution,
|
|
203
|
+
execution: merged_execution_hash(options.fetch(:execution, {}), owned: options.fetch(:owned, false)),
|
|
218
204
|
execution_owned: true,
|
|
219
205
|
context: fields.stack(:context),
|
|
220
206
|
attributes: fields.stack(:attributes),
|
|
221
207
|
neutral: fields.stack(:neutral),
|
|
222
|
-
labels: options
|
|
208
|
+
labels: options[:labels],
|
|
223
209
|
carry: fields.stack(:carry),
|
|
224
210
|
parent: parent_scope || linked_propagation_scope_snapshot,
|
|
225
211
|
started_at: options[:started_at],
|
|
@@ -238,7 +224,7 @@ module Julewire
|
|
|
238
224
|
end
|
|
239
225
|
|
|
240
226
|
def add_scope_stack(stack_set, options, section:, key:)
|
|
241
|
-
value = options
|
|
227
|
+
value = options[key]
|
|
242
228
|
if options.fetch(:owned, false)
|
|
243
229
|
stack_set.add(section, value, owned: true)
|
|
244
230
|
else
|
|
@@ -246,37 +232,26 @@ module Julewire
|
|
|
246
232
|
end
|
|
247
233
|
end
|
|
248
234
|
|
|
249
|
-
def add_field(section, fields, owned:
|
|
235
|
+
def add_field(section, fields, owned:)
|
|
250
236
|
scope = current_scope
|
|
251
237
|
if scope
|
|
252
238
|
scope.add_field(section, fields, owned: owned)
|
|
253
|
-
elsif owned
|
|
254
|
-
@ambient_fields.add(section, fields, owned: true)
|
|
255
239
|
else
|
|
256
|
-
@ambient_fields.add(section, fields)
|
|
240
|
+
@ambient_fields.add(section, fields, owned: owned)
|
|
257
241
|
end
|
|
258
242
|
end
|
|
259
243
|
|
|
260
244
|
def field_input(fields, keyword_fields)
|
|
261
245
|
return fields if keyword_fields.empty?
|
|
262
|
-
return keyword_fields if empty_field_input?(fields)
|
|
263
|
-
|
|
264
|
-
fields.is_a?(Hash) ? fields.merge(keyword_fields) : fields
|
|
265
|
-
end
|
|
266
246
|
|
|
267
|
-
|
|
268
|
-
fields.nil? || (fields.respond_to?(:empty?) && fields.empty?)
|
|
247
|
+
Fields::FieldSet.coerce(fields, keyword_fields, invalid: :wrap)
|
|
269
248
|
end
|
|
270
249
|
|
|
271
|
-
def with_scope_or_ambient_overlay(section, fields, owned
|
|
250
|
+
def with_scope_or_ambient_overlay(section, fields, owned:, &)
|
|
272
251
|
scope = current_scope
|
|
273
252
|
return scope.with_field(section, fields, owned: owned, &) if scope
|
|
274
253
|
|
|
275
|
-
|
|
276
|
-
@ambient_fields.with(section, fields, owned: true, &)
|
|
277
|
-
else
|
|
278
|
-
@ambient_fields.with(section, fields, &)
|
|
279
|
-
end
|
|
254
|
+
@ambient_fields.with(section, fields, owned: owned, &)
|
|
280
255
|
end
|
|
281
256
|
|
|
282
257
|
def current_field_stack(section)
|
|
@@ -297,32 +272,31 @@ module Julewire
|
|
|
297
272
|
end
|
|
298
273
|
|
|
299
274
|
def execution_hash
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
Fields::FieldSet.deep_dup(propagation_execution_hash)
|
|
275
|
+
Fields::FieldSet.deep_dup_owned(propagation_execution_hash)
|
|
303
276
|
end
|
|
304
277
|
|
|
305
278
|
def propagation_execution_hash
|
|
306
|
-
@propagation_execution_hash ||= Fields::Internal.
|
|
307
|
-
|
|
308
|
-
|
|
279
|
+
@propagation_execution_hash ||= Fields::Internal.frozen_owned_copy(
|
|
280
|
+
@execution_overlays.reduce({}) do |memo, overlay|
|
|
281
|
+
Fields::Internal.merge_owned!(memo, overlay.execution)
|
|
282
|
+
end
|
|
283
|
+
)
|
|
309
284
|
end
|
|
310
285
|
|
|
311
286
|
def propagation_scope_snapshot
|
|
312
287
|
execution = propagation_execution_hash
|
|
313
288
|
return if execution.empty?
|
|
314
289
|
|
|
315
|
-
@propagation_scope_snapshot ||= Execution::ScopeSnapshot.new(execution: execution)
|
|
290
|
+
@propagation_scope_snapshot ||= Execution::ScopeSnapshot.new(execution: execution, owned: true)
|
|
316
291
|
end
|
|
317
292
|
|
|
318
293
|
def linked_propagation_scope_snapshot
|
|
319
|
-
|
|
320
|
-
return unless lineage
|
|
294
|
+
return unless linked_propagation?
|
|
321
295
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
296
|
+
@linked_propagation_scope_snapshot ||= Execution::ScopeSnapshot.new(
|
|
297
|
+
execution: propagation_execution_hash,
|
|
298
|
+
owned: true
|
|
299
|
+
)
|
|
326
300
|
end
|
|
327
301
|
|
|
328
302
|
def invalidate_propagation_cache!
|
|
@@ -331,56 +305,46 @@ module Julewire
|
|
|
331
305
|
@linked_propagation_scope_snapshot = nil
|
|
332
306
|
end
|
|
333
307
|
|
|
334
|
-
def
|
|
335
|
-
|
|
336
|
-
execution = @execution_overlays.fetch(index)
|
|
337
|
-
next if execution.empty?
|
|
338
|
-
|
|
339
|
-
return @execution_lineage_overlays.fetch(index)
|
|
340
|
-
end
|
|
341
|
-
nil
|
|
308
|
+
def linked_propagation?
|
|
309
|
+
@execution_overlays.reverse_each.find { |overlay| !overlay.execution.empty? }&.link_executions
|
|
342
310
|
end
|
|
343
311
|
|
|
344
|
-
def merged_execution_hash(execution)
|
|
345
|
-
|
|
346
|
-
return inherited unless execution.is_a?(Hash) && !execution.empty?
|
|
312
|
+
def merged_execution_hash(execution, owned:)
|
|
313
|
+
return Fields::FieldSet.merge!(inherited_execution_hash, execution) unless owned
|
|
347
314
|
|
|
348
|
-
|
|
315
|
+
Serialization::DeepFreeze.validate_symbol_hash(execution)
|
|
316
|
+
Fields::Internal.merge_owned!(inherited_execution_hash, execution)
|
|
349
317
|
end
|
|
350
318
|
|
|
351
319
|
def inherited_execution_hash
|
|
352
320
|
scope = current_scope
|
|
353
321
|
return execution_hash unless scope
|
|
354
322
|
|
|
355
|
-
|
|
356
|
-
return inherited if @execution_overlays.empty?
|
|
357
|
-
|
|
358
|
-
overlay = execution_hash
|
|
359
|
-
return inherited if overlay.empty?
|
|
360
|
-
|
|
361
|
-
Fields::FieldSet.merge!(inherited, overlay)
|
|
323
|
+
Fields::Internal.merge_owned!(scope.inheritable_execution_hash, execution_hash)
|
|
362
324
|
end
|
|
363
325
|
|
|
364
326
|
def finish_scope(scope, on_finish, on_finish_failure, active_exception: nil)
|
|
365
327
|
return unless on_finish
|
|
366
328
|
|
|
367
|
-
contain_finish_failure(on_finish_failure, active_exception
|
|
368
|
-
|
|
329
|
+
contain_finish_failure(on_finish_failure, active_exception, phase: :summary_finish) do
|
|
330
|
+
scope.finish_owned unless scope.finished?
|
|
331
|
+
end
|
|
332
|
+
contain_finish_failure(on_finish_failure, active_exception, phase: :summary_emit) { on_finish.call(scope) }
|
|
369
333
|
end
|
|
370
334
|
|
|
371
|
-
def contain_finish_failure(on_finish_failure, active_exception)
|
|
335
|
+
def contain_finish_failure(on_finish_failure, active_exception, phase:)
|
|
372
336
|
yield
|
|
373
337
|
rescue StandardError => e
|
|
374
|
-
report_finish_failure(on_finish_failure, e)
|
|
338
|
+
report_finish_failure(on_finish_failure, e, phase: phase)
|
|
375
339
|
rescue SystemStackError => e
|
|
376
340
|
# Preserve the app's active stack error during unwind.
|
|
377
341
|
raise unless active_exception
|
|
378
342
|
|
|
379
|
-
report_finish_failure(on_finish_failure, e)
|
|
343
|
+
report_finish_failure(on_finish_failure, e, phase: phase)
|
|
380
344
|
end
|
|
381
345
|
|
|
382
|
-
def report_finish_failure(on_finish_failure, error)
|
|
383
|
-
on_finish_failure
|
|
346
|
+
def report_finish_failure(on_finish_failure, error, phase:)
|
|
347
|
+
on_finish_failure.call(error, phase: phase)
|
|
384
348
|
rescue StandardError
|
|
385
349
|
nil
|
|
386
350
|
end
|
|
@@ -25,7 +25,7 @@ module Julewire
|
|
|
25
25
|
when :raise then raise "julewire punk chaos output failure"
|
|
26
26
|
when :reject then false
|
|
27
27
|
when :sleep
|
|
28
|
-
sleep(@sleep_seconds)
|
|
28
|
+
Kernel.sleep(@sleep_seconds)
|
|
29
29
|
@output.write(value)
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -77,7 +77,7 @@ module Julewire
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def trigger?
|
|
80
|
-
@
|
|
80
|
+
@random.rand < @rate
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def chaos_mode
|
|
@@ -6,7 +6,7 @@ module Julewire
|
|
|
6
6
|
module Destinations
|
|
7
7
|
class Collection
|
|
8
8
|
def initialize(destinations, on_drop:, on_failure:)
|
|
9
|
-
@destinations = destinations.freeze
|
|
9
|
+
@destinations = destinations.dup.freeze
|
|
10
10
|
@on_drop = on_drop
|
|
11
11
|
@on_failure = on_failure
|
|
12
12
|
end
|
|
@@ -23,9 +23,9 @@ module Julewire
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def validate_destinations(destinations)
|
|
26
|
-
destinations.
|
|
26
|
+
destinations.each do |destination|
|
|
27
27
|
Registry.validate!(destination)
|
|
28
|
-
end
|
|
28
|
+
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -76,7 +76,7 @@ module Julewire
|
|
|
76
76
|
|
|
77
77
|
lifecycle_destinations(skip_resource_identities).each do |destination|
|
|
78
78
|
remaining_timeout = Scheduling::Deadline.remaining(deadline)
|
|
79
|
-
if attempted && deadline && remaining_timeout
|
|
79
|
+
if attempted && deadline && remaining_timeout.zero?
|
|
80
80
|
ok = false
|
|
81
81
|
break
|
|
82
82
|
end
|
|
@@ -96,7 +96,7 @@ module Julewire
|
|
|
96
96
|
def lifecycle_destinations(skip_resource_identities)
|
|
97
97
|
return @destinations unless skip_resource_identities
|
|
98
98
|
|
|
99
|
-
@destinations.reject {
|
|
99
|
+
@destinations.reject { skip_resource_identities.key?(resource_identity(it)) }
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def call_destination_after_fork(destination)
|
|
@@ -108,13 +108,6 @@ module Julewire
|
|
|
108
108
|
destination: destination_name(destination),
|
|
109
109
|
phase: :destination_lifecycle
|
|
110
110
|
)
|
|
111
|
-
nil
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def skip_lifecycle_destination?(destination, identities)
|
|
115
|
-
return false unless identities
|
|
116
|
-
|
|
117
|
-
identities.key?(resource_identity(destination))
|
|
118
111
|
end
|
|
119
112
|
|
|
120
113
|
def resource_identity(destination)
|
|
@@ -125,7 +118,7 @@ module Julewire
|
|
|
125
118
|
|
|
126
119
|
def emit_to_destination(destination, record)
|
|
127
120
|
result = destination.emit(record)
|
|
128
|
-
record_drop(:destination_rejected, destination, record) if result == false
|
|
121
|
+
record_drop(:destination_rejected, metadata: destination_metadata(destination, record)) if result == false
|
|
129
122
|
rescue StandardError => e
|
|
130
123
|
metadata = destination_metadata(destination, record)
|
|
131
124
|
notify_failure(
|
|
@@ -133,8 +126,7 @@ module Julewire
|
|
|
133
126
|
**metadata,
|
|
134
127
|
phase: :destination
|
|
135
128
|
)
|
|
136
|
-
record_drop(:destination_exception,
|
|
137
|
-
nil
|
|
129
|
+
record_drop(:destination_exception, metadata: metadata)
|
|
138
130
|
end
|
|
139
131
|
|
|
140
132
|
def destination_name(destination)
|
|
@@ -161,7 +153,7 @@ module Julewire
|
|
|
161
153
|
@on_failure.call(error, **metadata)
|
|
162
154
|
end
|
|
163
155
|
|
|
164
|
-
def record_drop(reason,
|
|
156
|
+
def record_drop(reason, metadata:)
|
|
165
157
|
@on_drop.call(reason, phase: :destination, **metadata)
|
|
166
158
|
end
|
|
167
159
|
|
|
@@ -43,7 +43,6 @@ module Julewire
|
|
|
43
43
|
on_drop: resolve(:on_drop, defaults),
|
|
44
44
|
on_failure: resolve(:on_failure, defaults),
|
|
45
45
|
output: output,
|
|
46
|
-
error_backtrace_lines: defaults.fetch(:error_backtrace_lines),
|
|
47
46
|
processors: resolve(:processors, defaults)
|
|
48
47
|
)
|
|
49
48
|
end
|
|
@@ -95,17 +94,10 @@ module Julewire
|
|
|
95
94
|
return defaults.fetch(key) if defaults.key?(key)
|
|
96
95
|
|
|
97
96
|
case key
|
|
98
|
-
when :
|
|
99
|
-
false
|
|
100
|
-
when :encoder, :formatter, :on_drop, :on_failure
|
|
97
|
+
when :encoder, :formatter
|
|
101
98
|
raise ArgumentError, "destination default #{key} is required"
|
|
102
99
|
when :max_record_bytes
|
|
103
100
|
DEFAULT_MAX_RECORD_BYTES
|
|
104
|
-
when :processors
|
|
105
|
-
[]
|
|
106
|
-
when :output
|
|
107
|
-
# No inherited output exists; build raises the required-output error.
|
|
108
|
-
nil
|
|
109
101
|
end
|
|
110
102
|
end
|
|
111
103
|
|
|
@@ -4,6 +4,8 @@ module Julewire
|
|
|
4
4
|
module Core
|
|
5
5
|
module Destinations
|
|
6
6
|
class Destination
|
|
7
|
+
include ProcessorHandling
|
|
8
|
+
|
|
7
9
|
COUNTER_KEYS = %i[
|
|
8
10
|
callback_error
|
|
9
11
|
encode_error
|
|
@@ -15,6 +17,7 @@ module Julewire
|
|
|
15
17
|
output_rejected
|
|
16
18
|
processor_dropped
|
|
17
19
|
processor_error
|
|
20
|
+
processor_invalid
|
|
18
21
|
received
|
|
19
22
|
record_too_large
|
|
20
23
|
].freeze
|
|
@@ -30,8 +33,7 @@ module Julewire
|
|
|
30
33
|
on_drop:,
|
|
31
34
|
on_failure:,
|
|
32
35
|
output:,
|
|
33
|
-
|
|
34
|
-
processors: []
|
|
36
|
+
processors: nil
|
|
35
37
|
)
|
|
36
38
|
@name = Destinations.normalize_name(name)
|
|
37
39
|
@formatter = validate_callable(formatter, name: :formatter)
|
|
@@ -43,7 +45,7 @@ module Julewire
|
|
|
43
45
|
raise ArgumentError, "destination #{@name.inspect} output is required" if output.nil?
|
|
44
46
|
|
|
45
47
|
@output = Sink.wrap(output, close_output: close_output)
|
|
46
|
-
@processor_chain = processor_chain(processors
|
|
48
|
+
@processor_chain = processor_chain(processors)
|
|
47
49
|
initialize_tracking
|
|
48
50
|
@write_step = build_write_step
|
|
49
51
|
end
|
|
@@ -57,10 +59,7 @@ module Julewire
|
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def emit_processed_record(record, degradation_marker:)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
clear_degradation_if_unchanged(degradation_marker)
|
|
63
|
-
nil
|
|
62
|
+
clear_degradation_if_unchanged(degradation_marker) if @write_step.call(record)
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
def flush(timeout: nil)
|
|
@@ -73,7 +72,7 @@ module Julewire
|
|
|
73
72
|
|
|
74
73
|
def after_fork!
|
|
75
74
|
initialize_tracking
|
|
76
|
-
@output.after_fork!
|
|
75
|
+
@output.after_fork!
|
|
77
76
|
self
|
|
78
77
|
rescue StandardError => e
|
|
79
78
|
notify_failure(
|
|
@@ -86,9 +85,7 @@ module Julewire
|
|
|
86
85
|
end
|
|
87
86
|
|
|
88
87
|
def resource_identity
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@output
|
|
88
|
+
@output.resource_identity
|
|
92
89
|
end
|
|
93
90
|
|
|
94
91
|
def health
|
|
@@ -146,7 +143,7 @@ module Julewire
|
|
|
146
143
|
|
|
147
144
|
def record_step_metadata(metadata)
|
|
148
145
|
record = metadata.delete(:record)
|
|
149
|
-
metadata[:record_metadata] = Records::Metadata.call(record)
|
|
146
|
+
metadata[:record_metadata] = Records::Metadata.call(record)
|
|
150
147
|
metadata
|
|
151
148
|
end
|
|
152
149
|
|
|
@@ -166,7 +163,7 @@ module Julewire
|
|
|
166
163
|
end
|
|
167
164
|
|
|
168
165
|
def record_loss(reason, metadata)
|
|
169
|
-
record_metadata = metadata.fetch(:record_metadata
|
|
166
|
+
record_metadata = metadata.fetch(:record_metadata)
|
|
170
167
|
@health.record_loss(
|
|
171
168
|
reason: reason,
|
|
172
169
|
counter: nil,
|
|
@@ -177,10 +174,6 @@ module Julewire
|
|
|
177
174
|
)
|
|
178
175
|
end
|
|
179
176
|
|
|
180
|
-
def clear_degradation
|
|
181
|
-
@health.clear_degradation
|
|
182
|
-
end
|
|
183
|
-
|
|
184
177
|
def clear_degradation_if_unchanged(marker)
|
|
185
178
|
@health.clear_degradation_if_unchanged(marker)
|
|
186
179
|
end
|
|
@@ -195,48 +188,6 @@ module Julewire
|
|
|
195
188
|
callback
|
|
196
189
|
end
|
|
197
190
|
|
|
198
|
-
def processor_chain(processors, error_backtrace_lines)
|
|
199
|
-
processors = processor_entries(processors)
|
|
200
|
-
return if processors.empty?
|
|
201
|
-
|
|
202
|
-
Processing::ProcessorChain.new(
|
|
203
|
-
processors: processors,
|
|
204
|
-
error_backtrace_lines: error_backtrace_lines,
|
|
205
|
-
on_error: method(:record_processor_error)
|
|
206
|
-
)
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def processor_entries(value)
|
|
210
|
-
case value
|
|
211
|
-
when Processing::ProcessorRegistry
|
|
212
|
-
value.to_a
|
|
213
|
-
else
|
|
214
|
-
Processing::ProcessorRegistry.new(Array(value)).to_a
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def process_record(record)
|
|
219
|
-
return record unless @processor_chain
|
|
220
|
-
|
|
221
|
-
processed = @processor_chain.call(Records::Draft.from_record(record, freeze_sections: false))
|
|
222
|
-
if processed.equal?(Processing::ProcessorChain::DROP)
|
|
223
|
-
increment_counter(:processor_dropped)
|
|
224
|
-
nil
|
|
225
|
-
elsif processed.is_a?(Processing::ProcessorChain::ErrorResult)
|
|
226
|
-
processed.draft.to_record
|
|
227
|
-
else
|
|
228
|
-
processed.to_record
|
|
229
|
-
end
|
|
230
|
-
rescue StandardError => e
|
|
231
|
-
notify_failure(e, phase: :destination_processor, record_metadata: Records::Metadata.call(record))
|
|
232
|
-
nil
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def record_processor_error(error, record_metadata)
|
|
236
|
-
increment_counter(:processor_error)
|
|
237
|
-
notify_failure(error, phase: :destination_processor, record_metadata: record_metadata)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
191
|
def call_output_lifecycle(method_name, timeout:)
|
|
241
192
|
Validation.validate_timeout!(timeout, name: :timeout)
|
|
242
193
|
call_output_lifecycle_safely(method_name, timeout)
|
|
@@ -244,8 +195,9 @@ module Julewire
|
|
|
244
195
|
|
|
245
196
|
def call_output_lifecycle_safely(method_name, timeout)
|
|
246
197
|
# Sink.wrap centralizes timeout-aware lifecycle dispatch for every output.
|
|
198
|
+
degradation_marker = @health.degradation_marker
|
|
247
199
|
result = @output.public_send(method_name, timeout: timeout)
|
|
248
|
-
|
|
200
|
+
clear_degradation_if_unchanged(degradation_marker) if method_name == :flush && result
|
|
249
201
|
result
|
|
250
202
|
rescue StandardError => e
|
|
251
203
|
notify_failure(
|
|
@@ -258,9 +210,7 @@ module Julewire
|
|
|
258
210
|
end
|
|
259
211
|
|
|
260
212
|
def output_class_name
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
@output.class.name
|
|
213
|
+
@output.output_class_name
|
|
264
214
|
end
|
|
265
215
|
end
|
|
266
216
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Julewire
|
|
4
|
+
module Core
|
|
5
|
+
module Destinations
|
|
6
|
+
# @api internal
|
|
7
|
+
module ProcessorHandling
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def processor_chain(processors)
|
|
11
|
+
processors = processor_entries(processors)
|
|
12
|
+
return if processors.empty?
|
|
13
|
+
|
|
14
|
+
Processing::ProcessorChain.new(
|
|
15
|
+
processors: processors,
|
|
16
|
+
on_error: method(:record_processor_error),
|
|
17
|
+
on_invalid: method(:record_invalid_processor_result),
|
|
18
|
+
on_invalid_draft: method(:record_invalid_processor_draft)
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def processor_entries(value)
|
|
23
|
+
case value
|
|
24
|
+
when Processing::ProcessorRegistry
|
|
25
|
+
value.to_a
|
|
26
|
+
else
|
|
27
|
+
Processing::ProcessorRegistry.new(Array(value)).to_a
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_record(record)
|
|
32
|
+
return record unless @processor_chain
|
|
33
|
+
|
|
34
|
+
processed = @processor_chain.call(Records::Draft.from_record(record))
|
|
35
|
+
if processed.equal?(Processing::ProcessorChain::DROP)
|
|
36
|
+
drop_processed_record
|
|
37
|
+
elsif processed.instance_of?(Processing::ProcessorChain::ErrorResult)
|
|
38
|
+
processed.draft.to_record
|
|
39
|
+
else
|
|
40
|
+
processed.to_record
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def drop_processed_record
|
|
45
|
+
increment_counter(:processor_dropped)
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def record_processor_error(error, record_metadata)
|
|
50
|
+
increment_counter(:processor_error)
|
|
51
|
+
notify_failure(error, phase: :destination_processor, record_metadata: record_metadata)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def record_invalid_processor_result(processor_name, result, record_metadata)
|
|
55
|
+
increment_counter(:processor_invalid)
|
|
56
|
+
error, metadata = Processing::InvalidResultFailure.build(
|
|
57
|
+
message: "destination processor returned unsupported result",
|
|
58
|
+
phase: :destination_processor_result,
|
|
59
|
+
processor_name: processor_name,
|
|
60
|
+
record_metadata: record_metadata,
|
|
61
|
+
result: result
|
|
62
|
+
)
|
|
63
|
+
notify_failure(error, **metadata)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def record_invalid_processor_draft(processor_name, error, record_metadata)
|
|
67
|
+
increment_counter(:processor_invalid)
|
|
68
|
+
notify_failure(
|
|
69
|
+
error,
|
|
70
|
+
phase: :destination_processor_data,
|
|
71
|
+
processor: processor_name,
|
|
72
|
+
record_metadata: record_metadata
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -49,7 +49,7 @@ module Julewire
|
|
|
49
49
|
def build(defaults:)
|
|
50
50
|
output_identities = {}.compare_by_identity
|
|
51
51
|
@definitions.map do |definition|
|
|
52
|
-
if definition.
|
|
52
|
+
if definition.instance_of?(Definition)
|
|
53
53
|
definition.build(defaults: defaults, output_identities: output_identities)
|
|
54
54
|
else
|
|
55
55
|
definition
|