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
|
@@ -1,64 +1,41 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
require "concurrent/atomic/atomic_fixnum"
|
|
5
|
+
|
|
3
6
|
module Julewire
|
|
4
7
|
module Core
|
|
5
8
|
module Diagnostics
|
|
6
9
|
module InvalidSeverityReporter
|
|
7
|
-
@warned =
|
|
8
|
-
@mutex = Mutex.new
|
|
10
|
+
@warned = Concurrent::AtomicBoolean.new
|
|
9
11
|
|
|
10
12
|
class RuntimeCounter
|
|
11
13
|
def initialize
|
|
12
|
-
@
|
|
13
|
-
@count = 0
|
|
14
|
-
@last = nil
|
|
14
|
+
@count = Concurrent::AtomicFixnum.new
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def call(value
|
|
18
|
-
metadata = InvalidSeverityReporter.metadata(value
|
|
19
|
-
@
|
|
20
|
-
@count += 1
|
|
21
|
-
@last = metadata
|
|
22
|
-
end
|
|
17
|
+
def call(value)
|
|
18
|
+
metadata = InvalidSeverityReporter.metadata(value)
|
|
19
|
+
@count.increment
|
|
23
20
|
InvalidSeverityReporter.warn_once(metadata)
|
|
24
21
|
rescue StandardError
|
|
25
22
|
nil
|
|
26
23
|
end
|
|
27
24
|
|
|
28
25
|
def health
|
|
29
|
-
@
|
|
30
|
-
{
|
|
31
|
-
count: @count,
|
|
32
|
-
last_event: @last&.fetch(:event, nil),
|
|
33
|
-
last_source: @last&.fetch(:source, nil),
|
|
34
|
-
last_value_class: @last&.fetch(:value_class, nil)
|
|
35
|
-
}.compact
|
|
36
|
-
end
|
|
26
|
+
{ count: @count.value }
|
|
37
27
|
end
|
|
38
28
|
|
|
39
29
|
def reset!
|
|
40
|
-
@
|
|
41
|
-
@count = 0
|
|
42
|
-
@last = nil
|
|
43
|
-
end
|
|
44
|
-
nil
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def reset_after_fork!
|
|
48
|
-
@mutex = Mutex.new
|
|
49
|
-
@count = 0
|
|
50
|
-
@last = nil
|
|
51
|
-
nil
|
|
30
|
+
@count.value = 0
|
|
52
31
|
end
|
|
53
32
|
end
|
|
54
33
|
|
|
55
34
|
private_constant :RuntimeCounter
|
|
56
35
|
|
|
57
36
|
class << self
|
|
58
|
-
def call(value
|
|
59
|
-
warning_only.call(value
|
|
60
|
-
rescue StandardError
|
|
61
|
-
nil
|
|
37
|
+
def call(value)
|
|
38
|
+
warning_only.call(value)
|
|
62
39
|
end
|
|
63
40
|
|
|
64
41
|
def counter = RuntimeCounter.new
|
|
@@ -71,22 +48,11 @@ module Julewire
|
|
|
71
48
|
end
|
|
72
49
|
|
|
73
50
|
def reset!
|
|
74
|
-
@
|
|
75
|
-
nil
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def reset_after_fork!
|
|
79
|
-
@mutex = Mutex.new
|
|
80
|
-
@warned = false
|
|
81
|
-
nil
|
|
51
|
+
@warned.make_false
|
|
82
52
|
end
|
|
83
53
|
|
|
84
|
-
def metadata(value
|
|
85
|
-
{
|
|
86
|
-
event: event,
|
|
87
|
-
source: source,
|
|
88
|
-
value_class: value.class.name || value.class.to_s
|
|
89
|
-
}.compact.freeze
|
|
54
|
+
def metadata(value)
|
|
55
|
+
{ value_class: value.class.to_s }.freeze
|
|
90
56
|
rescue StandardError
|
|
91
57
|
{ value_class: "unknown" }.freeze
|
|
92
58
|
end
|
|
@@ -98,12 +64,7 @@ module Julewire
|
|
|
98
64
|
end
|
|
99
65
|
|
|
100
66
|
def first_warning?
|
|
101
|
-
@
|
|
102
|
-
return false if @warned
|
|
103
|
-
|
|
104
|
-
@warned = true
|
|
105
|
-
true
|
|
106
|
-
end
|
|
67
|
+
@warned.make_true
|
|
107
68
|
end
|
|
108
69
|
end
|
|
109
70
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
require "concurrent/atomic/atomic_reference"
|
|
5
|
+
|
|
3
6
|
module Julewire
|
|
4
7
|
module Core
|
|
5
8
|
module Diagnostics
|
|
@@ -39,31 +42,26 @@ module Julewire
|
|
|
39
42
|
@interval = Validation.validate_integer_limit!(interval, name: :interval, positive: true)
|
|
40
43
|
@include_ok = include_ok ? true : false
|
|
41
44
|
@scheduler = scheduler
|
|
42
|
-
@
|
|
43
|
-
@last_signature =
|
|
44
|
-
@
|
|
45
|
-
@
|
|
46
|
-
@stopped = false
|
|
47
|
-
@token = nil
|
|
45
|
+
@last_failure = Concurrent::AtomicReference.new
|
|
46
|
+
@last_signature = Concurrent::AtomicReference.new
|
|
47
|
+
@running = Concurrent::AtomicBoolean.new
|
|
48
|
+
@schedule_mutex = Mutex.new
|
|
48
49
|
@serializer_pool_key = :"julewire_core_meta_observer_serializers_#{object_id}"
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def start!
|
|
52
|
-
@
|
|
53
|
-
return self if @started && !@stopped
|
|
53
|
+
return self unless @running.make_true
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
@stopped = false
|
|
57
|
-
schedule_next
|
|
58
|
-
end
|
|
55
|
+
schedule_next
|
|
59
56
|
self
|
|
60
57
|
end
|
|
61
58
|
|
|
62
59
|
def stop!
|
|
63
|
-
token = @
|
|
64
|
-
@
|
|
65
|
-
|
|
66
|
-
@token
|
|
60
|
+
token = @schedule_mutex.synchronize do
|
|
61
|
+
@running.make_false
|
|
62
|
+
token = @token
|
|
63
|
+
@token = nil
|
|
64
|
+
token
|
|
67
65
|
end
|
|
68
66
|
@scheduler.cancel(token) if token
|
|
69
67
|
self
|
|
@@ -72,12 +70,7 @@ module Julewire
|
|
|
72
70
|
def sample!
|
|
73
71
|
health = @runtime.health
|
|
74
72
|
signature = signature_for(health)
|
|
75
|
-
|
|
76
|
-
changed = signature != @last_signature
|
|
77
|
-
@last_signature = signature
|
|
78
|
-
changed
|
|
79
|
-
end
|
|
80
|
-
return false unless changed
|
|
73
|
+
return false if signature.eql?(@last_signature.get_and_set(signature))
|
|
81
74
|
return false unless emit_health?(health)
|
|
82
75
|
|
|
83
76
|
emit_health(health)
|
|
@@ -88,35 +81,38 @@ module Julewire
|
|
|
88
81
|
end
|
|
89
82
|
|
|
90
83
|
def health
|
|
91
|
-
@
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
end
|
|
84
|
+
failure = @last_failure.get
|
|
85
|
+
{
|
|
86
|
+
event: @event,
|
|
87
|
+
include_ok: @include_ok,
|
|
88
|
+
interval: @interval,
|
|
89
|
+
last_failure: failure,
|
|
90
|
+
observed_runtime: @runtime_name,
|
|
91
|
+
running: @running.true?,
|
|
92
|
+
status: failure ? :degraded : :ok,
|
|
93
|
+
target_runtime: @target_name
|
|
94
|
+
}.compact.freeze
|
|
103
95
|
end
|
|
104
96
|
|
|
105
97
|
private
|
|
106
98
|
|
|
107
99
|
def schedule_next
|
|
108
|
-
@
|
|
100
|
+
@schedule_mutex.synchronize do
|
|
101
|
+
@token = @scheduler.schedule(@interval) { scheduled_sample } if @running.true?
|
|
102
|
+
end
|
|
109
103
|
end
|
|
110
104
|
|
|
111
105
|
def scheduled_sample
|
|
106
|
+
return false unless @running.true?
|
|
107
|
+
|
|
112
108
|
sample!
|
|
113
|
-
|
|
109
|
+
schedule_next
|
|
114
110
|
rescue StandardError => e
|
|
115
111
|
record_failure(e)
|
|
116
112
|
end
|
|
117
113
|
|
|
118
114
|
def emit_health?(health)
|
|
119
|
-
@include_ok || health[:status]
|
|
115
|
+
@include_ok || !health[:status].eql?(:ok)
|
|
120
116
|
end
|
|
121
117
|
|
|
122
118
|
def emit_health(health)
|
|
@@ -137,23 +133,20 @@ module Julewire
|
|
|
137
133
|
end
|
|
138
134
|
|
|
139
135
|
def signature_for(health)
|
|
140
|
-
|
|
141
|
-
return build_serializer.serialize(health).hash if serializer.in_use?
|
|
142
|
-
|
|
143
|
-
serializer.serialize(health).hash
|
|
136
|
+
Serialization::SerializerPool.serialize_with(cached_serializer, health) { build_serializer }
|
|
144
137
|
end
|
|
145
138
|
|
|
146
139
|
def cached_serializer
|
|
147
|
-
Serialization::SerializerPool.serializer(@serializer_pool_key,
|
|
140
|
+
Serialization::SerializerPool.serializer(@serializer_pool_key, nil) { build_serializer }
|
|
148
141
|
end
|
|
149
142
|
|
|
150
143
|
def build_serializer
|
|
151
|
-
|
|
144
|
+
Serializer.new(compact_empty: true)
|
|
152
145
|
end
|
|
153
146
|
|
|
154
147
|
def record_failure(error)
|
|
155
148
|
failure = FailureSnapshot.build(error, phase: :meta_observer)
|
|
156
|
-
@
|
|
149
|
+
@last_failure.set(failure)
|
|
157
150
|
end
|
|
158
151
|
end
|
|
159
152
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_reference"
|
|
4
|
+
|
|
3
5
|
module Julewire
|
|
4
6
|
module Core
|
|
5
7
|
module Diagnostics
|
|
@@ -8,6 +10,9 @@ module Julewire
|
|
|
8
10
|
DEFAULT_NAME = :tail
|
|
9
11
|
COUNTER_KEYS = %i[captured failures].freeze
|
|
10
12
|
Entry = Data.define(:sequence, :at, :record)
|
|
13
|
+
State = Data.define(:sequence, :items)
|
|
14
|
+
EMPTY_ENTRIES = [].freeze
|
|
15
|
+
private_constant :State, :EMPTY_ENTRIES
|
|
11
16
|
|
|
12
17
|
class << self
|
|
13
18
|
def attach!(runtime = Julewire, **)
|
|
@@ -39,11 +44,10 @@ module Julewire
|
|
|
39
44
|
|
|
40
45
|
def emit(record)
|
|
41
46
|
snapshot = snapshot_record(record)
|
|
42
|
-
@
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@entries.shift while @entries.length > @capacity
|
|
47
|
+
@state_ref.update do |state|
|
|
48
|
+
sequence = state.sequence + 1
|
|
49
|
+
entries = (state.items + [Entry.new(sequence, Time.now.utc, snapshot)]).last(@capacity)
|
|
50
|
+
State.new(sequence, entries)
|
|
47
51
|
end
|
|
48
52
|
@health.increment(:captured)
|
|
49
53
|
nil
|
|
@@ -54,7 +58,7 @@ module Julewire
|
|
|
54
58
|
|
|
55
59
|
def entries(limit: nil)
|
|
56
60
|
limit = normalize_limit(limit)
|
|
57
|
-
snapshot = @
|
|
61
|
+
snapshot = @state_ref.get.items.dup
|
|
58
62
|
limit ? snapshot.last(limit) : snapshot
|
|
59
63
|
end
|
|
60
64
|
|
|
@@ -72,10 +76,8 @@ module Julewire
|
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
def clear
|
|
75
|
-
@
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
@health.clear_degraded!
|
|
79
|
+
@state_ref.update { State.new(it.sequence, EMPTY_ENTRIES) }
|
|
80
|
+
@health.clear_failures!
|
|
79
81
|
self
|
|
80
82
|
end
|
|
81
83
|
|
|
@@ -89,17 +91,14 @@ module Julewire
|
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
def health
|
|
92
|
-
|
|
93
|
-
@health.snapshot(capacity: @capacity, size: size)
|
|
94
|
+
@health.snapshot(capacity: @capacity, size: entries.length)
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
private
|
|
97
98
|
|
|
98
99
|
def initialize_state
|
|
99
|
-
@
|
|
100
|
-
@entries = []
|
|
100
|
+
@state_ref = Concurrent::AtomicReference.new(State.new(0, EMPTY_ENTRIES))
|
|
101
101
|
@health = Integration::DestinationHealth.new(counter_keys: COUNTER_KEYS)
|
|
102
|
-
@sequence = 0
|
|
103
102
|
@serializer_mutex = Mutex.new
|
|
104
103
|
end
|
|
105
104
|
|
|
@@ -123,7 +122,7 @@ module Julewire
|
|
|
123
122
|
message = Records::DisplayMessage.call(record)
|
|
124
123
|
return payload if blank?(message)
|
|
125
124
|
|
|
126
|
-
payload = payload.dup
|
|
125
|
+
payload = payload.dup
|
|
127
126
|
payload["message"] = message
|
|
128
127
|
payload
|
|
129
128
|
end
|
|
@@ -131,10 +130,7 @@ module Julewire
|
|
|
131
130
|
def serialize_payload(payload)
|
|
132
131
|
return serialize_custom_payload(payload) if @serializer
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
return build_serializer.serialize(payload) if serializer.in_use?
|
|
136
|
-
|
|
137
|
-
serializer.serialize(payload)
|
|
133
|
+
Serialization::SerializerPool.serialize_with(cached_serializer, payload) { build_serializer }
|
|
138
134
|
end
|
|
139
135
|
|
|
140
136
|
def serialize_custom_payload(payload)
|
|
@@ -142,11 +138,11 @@ module Julewire
|
|
|
142
138
|
end
|
|
143
139
|
|
|
144
140
|
def cached_serializer
|
|
145
|
-
Serialization::SerializerPool.serializer(@serializer_pool_key,
|
|
141
|
+
Serialization::SerializerPool.serializer(@serializer_pool_key, nil) { build_serializer }
|
|
146
142
|
end
|
|
147
143
|
|
|
148
144
|
def build_serializer
|
|
149
|
-
|
|
145
|
+
Serializer.new(compact_empty: true)
|
|
150
146
|
end
|
|
151
147
|
|
|
152
148
|
def blank?(value)
|
|
@@ -74,15 +74,11 @@ module Julewire
|
|
|
74
74
|
|
|
75
75
|
EXECUTION_OPTION_DEFAULTS.merge(options).tap do |normalized|
|
|
76
76
|
normalized[:execution] = execution_fields(normalized.delete(:fields))
|
|
77
|
-
normalized[:attributes] ||= EMPTY_HASH
|
|
78
|
-
normalized[:neutral] ||= EMPTY_HASH
|
|
79
|
-
normalized[:labels] ||= EMPTY_HASH
|
|
80
77
|
end
|
|
81
78
|
end
|
|
82
79
|
|
|
83
80
|
def execution_fields(fields)
|
|
84
|
-
|
|
85
|
-
raise ArgumentError, "execution fields must be a Hash" unless fields.is_a?(Hash)
|
|
81
|
+
raise ArgumentError, "execution fields must be a Hash" unless fields.nil? || fields.is_a?(Hash)
|
|
86
82
|
|
|
87
83
|
fields
|
|
88
84
|
end
|
|
@@ -91,9 +87,7 @@ module Julewire
|
|
|
91
87
|
return unless emit_summary_enabled
|
|
92
88
|
|
|
93
89
|
@summary_finalizer ||= lambda do |scope|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@emit_summary_record.call(scope)
|
|
90
|
+
@emit_summary_record.call(scope) unless suppress_summary_for_non_standard_exception?(scope)
|
|
97
91
|
end
|
|
98
92
|
end
|
|
99
93
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
|
|
3
5
|
module Julewire
|
|
4
6
|
module Core
|
|
5
7
|
module Execution
|
|
@@ -8,12 +10,9 @@ module Julewire
|
|
|
8
10
|
@scope = scope
|
|
9
11
|
@on_finish = on_finish
|
|
10
12
|
@on_finish_failure = on_finish_failure
|
|
11
|
-
@
|
|
12
|
-
@finished = false
|
|
13
|
+
@finished = Concurrent::AtomicBoolean.new
|
|
13
14
|
end
|
|
14
15
|
|
|
15
|
-
attr_reader :scope
|
|
16
|
-
|
|
17
16
|
def snapshot = View.new(@scope)
|
|
18
17
|
|
|
19
18
|
def run
|
|
@@ -32,44 +31,40 @@ module Julewire
|
|
|
32
31
|
ContextStore.current.with_scope(@scope, &)
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
def finish(reason: :closed, fields:
|
|
34
|
+
def finish(reason: :closed, fields: nil, attributes: nil, error: nil, severity: nil)
|
|
36
35
|
# Finishing is one-shot: retrying after partial summary mutation can
|
|
37
36
|
# duplicate completion data, so failures are reported and stop here.
|
|
38
37
|
return false unless mark_finished
|
|
39
38
|
|
|
40
39
|
add_completion_attributes(reason)
|
|
41
|
-
@scope.add_summary(fields)
|
|
42
|
-
@scope.add_summary_attributes(attributes)
|
|
40
|
+
@scope.add_summary(fields)
|
|
41
|
+
@scope.add_summary_attributes(attributes)
|
|
43
42
|
@scope.finish_owned(error: error, severity: severity)
|
|
44
43
|
call_finish
|
|
45
44
|
true
|
|
46
45
|
rescue StandardError => e
|
|
47
|
-
report_finish_failure(e)
|
|
46
|
+
report_finish_failure(e, phase: :summary_finish)
|
|
48
47
|
false
|
|
49
48
|
end
|
|
50
49
|
|
|
51
50
|
private
|
|
52
51
|
|
|
53
52
|
def mark_finished
|
|
54
|
-
@
|
|
55
|
-
return false if @finished
|
|
56
|
-
|
|
57
|
-
@finished = true
|
|
58
|
-
end
|
|
53
|
+
@finished.make_true
|
|
59
54
|
end
|
|
60
55
|
|
|
61
56
|
def add_completion_attributes(reason)
|
|
62
|
-
@scope.add_summary_attributes({ "julewire.completion": reason.to_s }
|
|
57
|
+
@scope.add_summary_attributes({ "julewire.completion": reason.to_s })
|
|
63
58
|
end
|
|
64
59
|
|
|
65
60
|
def call_finish
|
|
66
61
|
@on_finish&.call(@scope)
|
|
67
62
|
rescue StandardError => e
|
|
68
|
-
report_finish_failure(e)
|
|
63
|
+
report_finish_failure(e, phase: :summary_emit)
|
|
69
64
|
end
|
|
70
65
|
|
|
71
|
-
def report_finish_failure(error)
|
|
72
|
-
@on_finish_failure
|
|
66
|
+
def report_finish_failure(error, phase:)
|
|
67
|
+
@on_finish_failure.call(error, phase: phase)
|
|
73
68
|
rescue StandardError
|
|
74
69
|
nil
|
|
75
70
|
end
|
|
@@ -24,7 +24,6 @@ module Julewire
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def from_execution_hash(execution)
|
|
27
|
-
execution = {} unless execution.is_a?(Hash)
|
|
28
27
|
new(
|
|
29
28
|
reference: execution_reference(execution),
|
|
30
29
|
root_reference: relationship_value(execution, :root),
|
|
@@ -68,7 +67,9 @@ module Julewire
|
|
|
68
67
|
id = reference_value(execution, :id)
|
|
69
68
|
reference[:type] = type unless type.equal?(MISSING)
|
|
70
69
|
reference[:id] = id unless id.equal?(MISSING)
|
|
71
|
-
reference.empty?
|
|
70
|
+
return if reference.empty?
|
|
71
|
+
|
|
72
|
+
reference
|
|
72
73
|
end
|
|
73
74
|
end
|
|
74
75
|
|
|
@@ -81,15 +82,18 @@ module Julewire
|
|
|
81
82
|
ancestors: nil,
|
|
82
83
|
ancestors_truncated: false
|
|
83
84
|
)
|
|
84
|
-
@parent_lineage = parent_lineage
|
|
85
85
|
@depth = depth_value(depth, parent_lineage)
|
|
86
|
-
@root_reference = freeze_reference(
|
|
86
|
+
@root_reference = freeze_reference(
|
|
87
|
+
root_reference || parent_lineage&.root_reference_for_child || reference
|
|
88
|
+
)
|
|
87
89
|
@parent_reference = freeze_reference(parent_reference)
|
|
88
90
|
@ancestor_references = freeze_ancestors(ancestors)
|
|
89
91
|
@ancestors_truncated = ancestors_truncated ? true : false
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
return if @ancestor_references
|
|
93
|
+
|
|
94
|
+
references = inherited_ancestor_references(parent_lineage)
|
|
95
|
+
@ancestor_references = references.last(MAX_ANCESTORS).freeze
|
|
96
|
+
@ancestors_truncated ||= references.length > MAX_ANCESTORS
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
def merge_into_frozen(execution)
|
|
@@ -100,34 +104,14 @@ module Julewire
|
|
|
100
104
|
hash.freeze
|
|
101
105
|
end
|
|
102
106
|
|
|
103
|
-
def ancestors
|
|
104
|
-
references = @ancestor_references_for_child
|
|
105
|
-
return references if references
|
|
106
|
-
|
|
107
|
-
materialize_ancestor_references_for_child
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def truncated?
|
|
111
|
-
return @truncated if @truncated_computed
|
|
107
|
+
def ancestors = @ancestor_references
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
@truncated_computed = true
|
|
115
|
-
@truncated
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def freeze
|
|
119
|
-
return self if frozen?
|
|
120
|
-
|
|
121
|
-
ancestors
|
|
122
|
-
truncated?
|
|
123
|
-
@parent_lineage = nil
|
|
124
|
-
super
|
|
125
|
-
end
|
|
109
|
+
def truncated? = @ancestors_truncated
|
|
126
110
|
|
|
127
111
|
protected
|
|
128
112
|
|
|
129
113
|
def ancestor_references_for_child
|
|
130
|
-
|
|
114
|
+
@ancestor_references
|
|
131
115
|
end
|
|
132
116
|
|
|
133
117
|
def root_reference_for_child
|
|
@@ -136,37 +120,14 @@ module Julewire
|
|
|
136
120
|
|
|
137
121
|
private
|
|
138
122
|
|
|
139
|
-
attr_reader :parent_lineage
|
|
140
|
-
|
|
141
|
-
def materialize_ancestor_references_for_child
|
|
142
|
-
if @ancestor_references
|
|
143
|
-
@ancestor_references_for_child = @ancestor_references.freeze
|
|
144
|
-
else
|
|
145
|
-
references = build_ancestor_references
|
|
146
|
-
@truncated = references.length > MAX_ANCESTORS
|
|
147
|
-
@truncated_computed = true
|
|
148
|
-
@ancestor_references_for_child = references.last(MAX_ANCESTORS).freeze
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def ancestor_count_exceeds_limit?
|
|
153
|
-
return false if @ancestor_references_for_child
|
|
154
|
-
|
|
155
|
-
build_ancestor_references.length > MAX_ANCESTORS
|
|
156
|
-
end
|
|
157
|
-
|
|
158
123
|
def depth_value(depth, parent_lineage)
|
|
159
|
-
return depth if depth.
|
|
124
|
+
return depth if depth.instance_of?(Integer) && depth.positive?
|
|
160
125
|
return parent_lineage.depth + 1 if parent_lineage
|
|
161
126
|
|
|
162
127
|
1
|
|
163
128
|
end
|
|
164
129
|
|
|
165
|
-
def
|
|
166
|
-
parent_lineage ? parent_lineage.root_reference_for_child : reference
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def build_ancestor_references
|
|
130
|
+
def inherited_ancestor_references(parent_lineage)
|
|
170
131
|
return [] unless parent_lineage && @parent_reference
|
|
171
132
|
|
|
172
133
|
parent_lineage.ancestor_references_for_child + [@parent_reference]
|
|
@@ -180,7 +141,6 @@ module Julewire
|
|
|
180
141
|
|
|
181
142
|
def freeze_reference(reference)
|
|
182
143
|
return unless reference
|
|
183
|
-
return reference if reference.frozen?
|
|
184
144
|
|
|
185
145
|
Serialization::ValueCopy.call(reference, freeze_values: true)
|
|
186
146
|
end
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
|
|
3
5
|
module Julewire
|
|
4
6
|
module Core
|
|
5
7
|
module Execution
|
|
6
8
|
class MeasurementHandle
|
|
7
9
|
def initialize(&finish)
|
|
8
10
|
@finish = finish
|
|
9
|
-
@finished =
|
|
10
|
-
@mutex = Mutex.new
|
|
11
|
+
@finished = Concurrent::AtomicBoolean.new
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def finish
|
|
14
|
-
@
|
|
15
|
-
return if @finished
|
|
15
|
+
return unless @finished.make_true
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
@finish.call
|
|
19
|
-
end
|
|
17
|
+
@finish.call
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
def finished?
|
|
23
|
-
@mutex.synchronize { @finished }
|
|
24
|
-
end
|
|
20
|
+
def finished? = @finished.true?
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
23
|
end
|