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
|
@@ -12,12 +12,6 @@ module Julewire
|
|
|
12
12
|
Validation.validate_integer_limit!(value, name: name)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def record_hash_truncation(fields, key, truncated)
|
|
16
|
-
return fields unless truncated
|
|
17
|
-
|
|
18
|
-
append_truncation_field(fields, key.to_s)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
15
|
def finish_hash(result, fields)
|
|
22
16
|
add_truncation_metadata!(result, fields)
|
|
23
17
|
finish_container(result, fields)
|
|
@@ -38,7 +32,7 @@ module Julewire
|
|
|
38
32
|
|
|
39
33
|
def finish_container(result, fields)
|
|
40
34
|
value = freeze_container(result)
|
|
41
|
-
fields ? mark_truncated(value) :
|
|
35
|
+
fields ? mark_truncated(value) : value
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
def truncation_metadata(fields)
|
|
@@ -59,11 +53,6 @@ module Julewire
|
|
|
59
53
|
value
|
|
60
54
|
end
|
|
61
55
|
|
|
62
|
-
def clear_truncated(value)
|
|
63
|
-
@last_truncated = false
|
|
64
|
-
value
|
|
65
|
-
end
|
|
66
|
-
|
|
67
56
|
def consume_truncated
|
|
68
57
|
truncated = @last_truncated
|
|
69
58
|
@last_truncated = false
|
|
@@ -76,97 +65,6 @@ module Julewire
|
|
|
76
65
|
end
|
|
77
66
|
private_constant :ValueCopyTruncation
|
|
78
67
|
|
|
79
|
-
module ValueCopyCache
|
|
80
|
-
POOL_KEY = :julewire_core_value_copy_pool
|
|
81
|
-
private_constant :POOL_KEY
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
|
|
85
|
-
def cached_copier(compact_empty:, freeze_values:, max_array_items:, max_depth:, max_hash_keys:,
|
|
86
|
-
max_string_bytes:, preserve_truncation_metadata:, symbolize_keys:)
|
|
87
|
-
options = copier_options(
|
|
88
|
-
compact_empty: compact_empty,
|
|
89
|
-
freeze_values: freeze_values,
|
|
90
|
-
max_array_items: max_array_items,
|
|
91
|
-
max_depth: max_depth,
|
|
92
|
-
max_hash_keys: max_hash_keys,
|
|
93
|
-
max_string_bytes: max_string_bytes,
|
|
94
|
-
preserve_truncation_metadata: preserve_truncation_metadata,
|
|
95
|
-
symbolize_keys: symbolize_keys
|
|
96
|
-
)
|
|
97
|
-
return new(**options) unless cacheable_options?(options)
|
|
98
|
-
|
|
99
|
-
reusable_copier(options)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def reusable_copier(options)
|
|
103
|
-
# One copier per thread/options avoids per-record walker allocation.
|
|
104
|
-
pool = Thread.current.thread_variable_get(POOL_KEY)
|
|
105
|
-
unless pool
|
|
106
|
-
pool = {}
|
|
107
|
-
Thread.current.thread_variable_set(POOL_KEY, pool)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
bucket = cache_bucket(
|
|
111
|
-
pool,
|
|
112
|
-
compact_empty: options.fetch(:compact_empty),
|
|
113
|
-
freeze_values: options.fetch(:freeze_values),
|
|
114
|
-
max_array_items: options.fetch(:max_array_items),
|
|
115
|
-
max_depth: options.fetch(:max_depth),
|
|
116
|
-
max_hash_keys: options.fetch(:max_hash_keys),
|
|
117
|
-
preserve_truncation_metadata: options.fetch(:preserve_truncation_metadata),
|
|
118
|
-
symbolize_keys: options.fetch(:symbolize_keys)
|
|
119
|
-
)
|
|
120
|
-
bucket[options.fetch(:max_string_bytes)] ||= new(**options)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def copier_options(compact_empty:, freeze_values:, max_array_items:, max_depth:, max_hash_keys:,
|
|
124
|
-
max_string_bytes:, preserve_truncation_metadata:, symbolize_keys:)
|
|
125
|
-
{
|
|
126
|
-
compact_empty: compact_empty,
|
|
127
|
-
freeze_values: freeze_values,
|
|
128
|
-
max_array_items: max_array_items,
|
|
129
|
-
max_depth: max_depth,
|
|
130
|
-
max_hash_keys: max_hash_keys,
|
|
131
|
-
max_string_bytes: max_string_bytes,
|
|
132
|
-
preserve_truncation_metadata: preserve_truncation_metadata,
|
|
133
|
-
symbolize_keys: symbolize_keys
|
|
134
|
-
}
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def cache_bucket(pool, compact_empty:, freeze_values:, max_array_items:, max_depth:, max_hash_keys:,
|
|
138
|
-
preserve_truncation_metadata:, symbolize_keys:)
|
|
139
|
-
flags = cache_flags(
|
|
140
|
-
compact_empty: compact_empty,
|
|
141
|
-
freeze_values: freeze_values,
|
|
142
|
-
preserve_truncation_metadata: preserve_truncation_metadata,
|
|
143
|
-
symbolize_keys: symbolize_keys
|
|
144
|
-
)
|
|
145
|
-
by_depth = (pool[flags] ||= {})
|
|
146
|
-
by_array = (by_depth[max_depth] ||= {})
|
|
147
|
-
by_hash = (by_array[max_array_items] ||= {})
|
|
148
|
-
by_hash[max_hash_keys] ||= {}
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def cache_flags(compact_empty:, freeze_values:, preserve_truncation_metadata:, symbolize_keys:)
|
|
152
|
-
flags = 0
|
|
153
|
-
flags |= 1 if compact_empty
|
|
154
|
-
flags |= 2 if freeze_values
|
|
155
|
-
flags |= 4 if symbolize_keys
|
|
156
|
-
flags |= 8 if preserve_truncation_metadata
|
|
157
|
-
flags
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def cacheable_options?(options)
|
|
161
|
-
# Only default ingress bounds use the thread-local pool; custom limits instantiate ad hoc.
|
|
162
|
-
options.fetch(:max_depth) == Core::NORMALIZATION_MAX_DEPTH &&
|
|
163
|
-
[nil, Serializer::DEFAULT_MAX_ARRAY_ITEMS].include?(options.fetch(:max_array_items)) &&
|
|
164
|
-
[nil, Serializer::DEFAULT_MAX_HASH_KEYS].include?(options.fetch(:max_hash_keys)) &&
|
|
165
|
-
[nil, Serializer::DEFAULT_MAX_STRING_BYTES].include?(options.fetch(:max_string_bytes))
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
private_constant :ValueCopyCache
|
|
169
|
-
|
|
170
68
|
class ValueCopy
|
|
171
69
|
include ValueTraversal
|
|
172
70
|
include ValueCopyTruncation
|
|
@@ -174,87 +72,37 @@ module Julewire
|
|
|
174
72
|
CIRCULAR_REFERENCE = Core::CIRCULAR_REFERENCE
|
|
175
73
|
EMPTY_ARRAY = [].freeze
|
|
176
74
|
EMPTY_HASH = {}.freeze
|
|
177
|
-
|
|
178
|
-
private_constant :EMPTY_ARRAY, :EMPTY_HASH, :RESERVED_KEYS
|
|
75
|
+
private_constant :EMPTY_ARRAY, :EMPTY_HASH
|
|
179
76
|
|
|
180
77
|
class << self
|
|
181
|
-
include ValueCopyCache
|
|
182
|
-
|
|
183
78
|
def call( # rubocop:disable Metrics/ParameterLists
|
|
184
79
|
value,
|
|
185
80
|
compact_empty: false,
|
|
186
81
|
freeze_values: false,
|
|
187
82
|
max_array_items: nil,
|
|
188
|
-
max_depth:
|
|
83
|
+
max_depth: NORMALIZATION_MAX_DEPTH,
|
|
189
84
|
max_hash_keys: nil,
|
|
190
85
|
max_string_bytes: nil,
|
|
191
86
|
preserve_truncation_metadata: false,
|
|
192
87
|
symbolize_keys: false
|
|
193
88
|
)
|
|
194
|
-
needs_string_limit = value.is_a?(String) && max_string_bytes
|
|
195
|
-
return copy_leaf(value, freeze_values: freeze_values) unless container?(value) || needs_string_limit
|
|
196
|
-
|
|
197
|
-
copy_with(
|
|
198
|
-
cached_copier(
|
|
199
|
-
compact_empty: compact_empty,
|
|
200
|
-
freeze_values: freeze_values,
|
|
201
|
-
max_array_items: max_array_items,
|
|
202
|
-
max_depth: max_depth,
|
|
203
|
-
max_hash_keys: max_hash_keys,
|
|
204
|
-
max_string_bytes: max_string_bytes,
|
|
205
|
-
preserve_truncation_metadata: preserve_truncation_metadata,
|
|
206
|
-
symbolize_keys: symbolize_keys
|
|
207
|
-
),
|
|
208
|
-
value
|
|
209
|
-
)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def omitted_empty?(value)
|
|
213
|
-
value.nil? || (value.is_a?(Hash) && value.empty?) || (value.is_a?(Array) && value.empty?)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
private
|
|
217
|
-
|
|
218
|
-
def container?(value) = value.is_a?(Hash) || value.is_a?(Array)
|
|
219
|
-
|
|
220
|
-
def copy_with(copier, value)
|
|
221
|
-
return copier.call_reusable(value) unless copier.in_use?
|
|
222
|
-
|
|
223
89
|
new(
|
|
224
|
-
compact_empty
|
|
225
|
-
freeze_values
|
|
226
|
-
max_array_items
|
|
227
|
-
max_depth
|
|
228
|
-
max_hash_keys
|
|
229
|
-
max_string_bytes
|
|
230
|
-
preserve_truncation_metadata
|
|
231
|
-
symbolize_keys:
|
|
90
|
+
compact_empty:,
|
|
91
|
+
freeze_values:,
|
|
92
|
+
max_array_items:,
|
|
93
|
+
max_depth:,
|
|
94
|
+
max_hash_keys:,
|
|
95
|
+
max_string_bytes:,
|
|
96
|
+
preserve_truncation_metadata:,
|
|
97
|
+
symbolize_keys:
|
|
232
98
|
).call(value)
|
|
233
99
|
end
|
|
234
100
|
|
|
235
|
-
def
|
|
236
|
-
|
|
237
|
-
return copy_time(value, freeze_values: freeze_values) if value.is_a?(Time)
|
|
238
|
-
|
|
239
|
-
value
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def copy_string(value, freeze_values:)
|
|
243
|
-
copy = value.frozen? ? value : value.dup
|
|
244
|
-
freeze_values ? copy.freeze : copy
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def copy_time(value, freeze_values:)
|
|
248
|
-
return value unless freeze_values
|
|
249
|
-
return value if value.frozen?
|
|
250
|
-
|
|
251
|
-
value.dup.freeze
|
|
101
|
+
def omitted_empty?(value)
|
|
102
|
+
value.nil? || (value.is_a?(Hash) && value.empty?) || (value.is_a?(Array) && value.empty?)
|
|
252
103
|
end
|
|
253
104
|
end
|
|
254
105
|
|
|
255
|
-
attr_reader :compact_empty, :freeze_values, :max_array_items, :max_depth, :max_hash_keys, :max_string_bytes,
|
|
256
|
-
:preserve_truncation_metadata, :symbolize_keys
|
|
257
|
-
|
|
258
106
|
def initialize(compact_empty:, freeze_values:, max_array_items:, max_depth:, max_hash_keys:, max_string_bytes:,
|
|
259
107
|
preserve_truncation_metadata:, symbolize_keys:)
|
|
260
108
|
@compact_empty = compact_empty
|
|
@@ -265,27 +113,13 @@ module Julewire
|
|
|
265
113
|
@max_string_bytes = validate_optional_limit(max_string_bytes, name: :max_string_bytes)
|
|
266
114
|
@preserve_truncation_metadata = preserve_truncation_metadata
|
|
267
115
|
@symbolize_keys = symbolize_keys
|
|
268
|
-
@
|
|
269
|
-
@last_truncated = false
|
|
270
|
-
@track_truncation = !!(@max_array_items || @max_hash_keys || @max_string_bytes)
|
|
116
|
+
@track_truncation = @max_array_items || @max_hash_keys || @max_string_bytes
|
|
271
117
|
end
|
|
272
118
|
|
|
273
119
|
def call(value)
|
|
274
|
-
@last_truncated = false
|
|
275
120
|
traverse(value) { |root, depth| copy_value(root, depth) }
|
|
276
|
-
ensure
|
|
277
|
-
@last_truncated = false
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def call_reusable(value)
|
|
281
|
-
@in_use = true
|
|
282
|
-
call(value)
|
|
283
|
-
ensure
|
|
284
|
-
@in_use = false
|
|
285
121
|
end
|
|
286
122
|
|
|
287
|
-
def in_use? = @in_use
|
|
288
|
-
|
|
289
123
|
private
|
|
290
124
|
|
|
291
125
|
def copy_value(value, depth)
|
|
@@ -297,7 +131,7 @@ module Julewire
|
|
|
297
131
|
end
|
|
298
132
|
|
|
299
133
|
def copy_container(value, depth)
|
|
300
|
-
return mark_truncated(
|
|
134
|
+
return mark_truncated(Serializer::MAX_DEPTH_VALUE) if depth == @max_depth
|
|
301
135
|
return frozen_empty_container(value) if @freeze_values && value.empty?
|
|
302
136
|
|
|
303
137
|
with_traversal_container(value, CIRCULAR_REFERENCE) do
|
|
@@ -305,10 +139,6 @@ module Julewire
|
|
|
305
139
|
end
|
|
306
140
|
end
|
|
307
141
|
|
|
308
|
-
def depth_limited?(depth)
|
|
309
|
-
@max_depth && depth >= @max_depth
|
|
310
|
-
end
|
|
311
|
-
|
|
312
142
|
def frozen_empty_container(value)
|
|
313
143
|
value.is_a?(Hash) ? EMPTY_HASH : EMPTY_ARRAY
|
|
314
144
|
end
|
|
@@ -318,13 +148,12 @@ module Julewire
|
|
|
318
148
|
result = {}
|
|
319
149
|
visited = 0
|
|
320
150
|
value.each do |key, item|
|
|
321
|
-
if
|
|
151
|
+
if visited == @max_hash_keys
|
|
322
152
|
fields = append_truncation_field(fields, "hash_keys")
|
|
323
153
|
break
|
|
324
154
|
end
|
|
325
155
|
|
|
326
156
|
visited += 1
|
|
327
|
-
# Raw-empty values still spend work budget. The limit protects traversal work, not output size.
|
|
328
157
|
next if @compact_empty && self.class.omitted_empty?(item)
|
|
329
158
|
|
|
330
159
|
fields = copy_hash_entry(result, fields, key, item, depth)
|
|
@@ -332,32 +161,31 @@ module Julewire
|
|
|
332
161
|
finish_hash(result, fields)
|
|
333
162
|
end
|
|
334
163
|
|
|
335
|
-
def hash_limit_reached?(visited)
|
|
336
|
-
@max_hash_keys && visited >= @max_hash_keys
|
|
337
|
-
end
|
|
338
|
-
|
|
339
164
|
def copy_hash_entry(result, fields, key, item, depth)
|
|
340
|
-
return copy_truncation_metadata_entry(result, fields, key, item
|
|
165
|
+
return copy_truncation_metadata_entry(result, fields, key, item) if reserved_truncation_key?(key)
|
|
341
166
|
|
|
167
|
+
validate_symbolized_key_shape!(key)
|
|
342
168
|
copied = copy_value(item, depth + 1)
|
|
343
|
-
child_truncated = consume_truncated
|
|
344
169
|
return fields if @compact_empty && self.class.omitted_empty?(copied)
|
|
345
170
|
|
|
346
|
-
copied_key =
|
|
347
|
-
|
|
171
|
+
copied_key = copied_key_value(key)
|
|
172
|
+
value_or_key_truncated = consume_truncated
|
|
348
173
|
result[copied_key] = copied
|
|
349
|
-
|
|
174
|
+
fields = append_truncation_field(fields, copied_key.to_s) if value_or_key_truncated
|
|
175
|
+
fields
|
|
350
176
|
end
|
|
351
177
|
|
|
352
|
-
def copy_truncation_metadata_entry(result, fields, key, item
|
|
178
|
+
def copy_truncation_metadata_entry(result, fields, key, item)
|
|
353
179
|
unless @preserve_truncation_metadata &&
|
|
354
180
|
allowed_truncation_metadata_key?(key) &&
|
|
355
181
|
TruncationMetadata.valid?(item, max_fields: truncation_metadata_field_limit)
|
|
356
|
-
raise_reserved_key!
|
|
182
|
+
raise_reserved_key!
|
|
357
183
|
end
|
|
358
184
|
|
|
359
|
-
result[copy_truncation_metadata_key(key)] =
|
|
360
|
-
|
|
185
|
+
result[copy_truncation_metadata_key(key)] = TruncationMetadata.copy(
|
|
186
|
+
item,
|
|
187
|
+
freeze_values: @freeze_values
|
|
188
|
+
)
|
|
361
189
|
fields
|
|
362
190
|
end
|
|
363
191
|
|
|
@@ -366,7 +194,7 @@ module Julewire
|
|
|
366
194
|
end
|
|
367
195
|
|
|
368
196
|
def allowed_truncation_metadata_key?(key)
|
|
369
|
-
key.
|
|
197
|
+
key.instance_of?(Symbol) || @symbolize_keys
|
|
370
198
|
end
|
|
371
199
|
|
|
372
200
|
def reserved_truncation_key?(key)
|
|
@@ -374,64 +202,69 @@ module Julewire
|
|
|
374
202
|
end
|
|
375
203
|
|
|
376
204
|
def copy_truncation_metadata_key(key)
|
|
377
|
-
|
|
205
|
+
key.to_sym
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def validate_symbolized_key_shape!(key)
|
|
209
|
+
return unless @symbolize_keys
|
|
210
|
+
return if key.is_a?(String) || key.instance_of?(Symbol)
|
|
211
|
+
|
|
212
|
+
raise TypeError, Fields::Internal::FIELD_KEY_ERROR
|
|
378
213
|
end
|
|
379
214
|
|
|
380
215
|
def copy_array(value, depth)
|
|
381
|
-
fields = nil
|
|
382
216
|
result = []
|
|
383
217
|
visited = 0
|
|
218
|
+
array_items_truncated = false
|
|
219
|
+
array_item_values_truncated = false
|
|
384
220
|
value.each do |item|
|
|
385
|
-
if
|
|
386
|
-
|
|
221
|
+
if visited == @max_array_items
|
|
222
|
+
array_items_truncated = true
|
|
387
223
|
break
|
|
388
224
|
end
|
|
389
225
|
|
|
390
226
|
visited += 1
|
|
391
227
|
next if @compact_empty && self.class.omitted_empty?(item)
|
|
392
228
|
|
|
393
|
-
|
|
229
|
+
copied = copy_value(item, depth + 1)
|
|
230
|
+
child_truncated = consume_truncated
|
|
231
|
+
next if @compact_empty && self.class.omitted_empty?(copied)
|
|
232
|
+
|
|
233
|
+
result << copied
|
|
234
|
+
array_item_values_truncated ||= child_truncated
|
|
394
235
|
end
|
|
395
|
-
finish_array(result, fields)
|
|
396
|
-
end
|
|
397
236
|
|
|
398
|
-
|
|
399
|
-
@max_array_items && visited >= @max_array_items
|
|
237
|
+
finish_array(result, array_truncation_fields(array_items_truncated, array_item_values_truncated))
|
|
400
238
|
end
|
|
401
239
|
|
|
402
|
-
def
|
|
403
|
-
|
|
404
|
-
child_truncated = consume_truncated
|
|
405
|
-
return fields if @compact_empty && self.class.omitted_empty?(copied)
|
|
240
|
+
def array_truncation_fields(array_items_truncated, array_item_values_truncated)
|
|
241
|
+
return unless array_items_truncated || array_item_values_truncated
|
|
406
242
|
|
|
407
|
-
|
|
408
|
-
|
|
243
|
+
fields = []
|
|
244
|
+
fields << "array_item_values" if array_item_values_truncated
|
|
245
|
+
fields << "array_items" if array_items_truncated
|
|
246
|
+
fields
|
|
409
247
|
end
|
|
410
248
|
|
|
411
|
-
def
|
|
412
|
-
|
|
413
|
-
copied = copied.to_sym if @symbolize_keys && copied.is_a?(String)
|
|
414
|
-
raise_reserved_key!(copied)
|
|
249
|
+
def copied_key_value(key)
|
|
250
|
+
return key unless key.is_a?(String)
|
|
415
251
|
|
|
416
|
-
|
|
252
|
+
copy = copy_string(key)
|
|
253
|
+
@symbolize_keys ? copy.to_sym : copy
|
|
417
254
|
end
|
|
418
255
|
|
|
419
|
-
def raise_reserved_key!
|
|
420
|
-
return unless RESERVED_KEYS.include?(key)
|
|
421
|
-
|
|
256
|
+
def raise_reserved_key!
|
|
422
257
|
raise ArgumentError, "#{Serializer::TRUNCATION_METADATA_KEY} is reserved for Julewire truncation metadata"
|
|
423
258
|
end
|
|
424
259
|
|
|
425
260
|
def copy_string(value)
|
|
426
|
-
return value unless value.is_a?(String)
|
|
427
|
-
|
|
428
261
|
if @max_string_bytes && value.bytesize > @max_string_bytes
|
|
429
262
|
copy = "#{value.byteslice(0, @max_string_bytes).scrub("?")}#{Serializer::TRUNCATED_SUFFIX}"
|
|
430
263
|
return mark_truncated(freeze_container(copy))
|
|
431
264
|
end
|
|
432
265
|
|
|
433
266
|
copy = value.frozen? ? value : value.dup
|
|
434
|
-
|
|
267
|
+
freeze_container(copy)
|
|
435
268
|
end
|
|
436
269
|
|
|
437
270
|
def copy_time(value)
|
|
@@ -5,142 +5,25 @@ module Julewire
|
|
|
5
5
|
module Serialization
|
|
6
6
|
module ValueTraversal
|
|
7
7
|
def traverse(value)
|
|
8
|
-
previous_seen = @traversal_seen
|
|
9
|
-
previous_first_seen = @traversal_first_seen
|
|
10
|
-
previous_second_seen = @traversal_second_seen
|
|
11
|
-
previous_third_seen = @traversal_third_seen
|
|
12
|
-
previous_fourth_seen = @traversal_fourth_seen
|
|
13
|
-
@traversal_seen = nil
|
|
14
|
-
@traversal_first_seen = nil
|
|
15
|
-
@traversal_second_seen = nil
|
|
16
|
-
@traversal_third_seen = nil
|
|
17
|
-
@traversal_fourth_seen = nil
|
|
18
8
|
yield(value, 0)
|
|
19
|
-
ensure
|
|
20
|
-
@traversal_seen = previous_seen
|
|
21
|
-
@traversal_first_seen = previous_first_seen
|
|
22
|
-
@traversal_second_seen = previous_second_seen
|
|
23
|
-
@traversal_third_seen = previous_third_seen
|
|
24
|
-
@traversal_fourth_seen = previous_fourth_seen
|
|
25
9
|
end
|
|
26
10
|
|
|
27
11
|
private
|
|
28
12
|
|
|
29
|
-
def with_traversal_container(value, circular_value)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
first_seen = @traversal_first_seen
|
|
33
|
-
second_seen = @traversal_second_seen
|
|
34
|
-
third_seen = @traversal_third_seen
|
|
35
|
-
fourth_seen = @traversal_fourth_seen
|
|
36
|
-
return circular_value if seen&.key?(value) || first_seen.equal?(value) || second_seen.equal?(value) ||
|
|
37
|
-
third_seen.equal?(value) || fourth_seen.equal?(value)
|
|
13
|
+
def with_traversal_container(value, circular_value, &)
|
|
14
|
+
seen = traversal_seen
|
|
15
|
+
return circular_value if seen.include?(value)
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def traversal_seen?(value)
|
|
46
|
-
@traversal_seen&.key?(value) || @traversal_first_seen.equal?(value) ||
|
|
47
|
-
@traversal_second_seen.equal?(value) || @traversal_third_seen.equal?(value) ||
|
|
48
|
-
@traversal_fourth_seen.equal?(value)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def with_marked_traversal_container(value)
|
|
52
|
-
added = mark_traversal_container(
|
|
53
|
-
value,
|
|
54
|
-
@traversal_seen,
|
|
55
|
-
@traversal_first_seen,
|
|
56
|
-
@traversal_second_seen,
|
|
57
|
-
@traversal_third_seen,
|
|
58
|
-
@traversal_fourth_seen
|
|
59
|
-
)
|
|
60
|
-
yield
|
|
61
|
-
ensure
|
|
62
|
-
unmark_traversal_container(value, added)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def mark_traversal_container(value, seen, first_seen, second_seen, third_seen, fourth_seen)
|
|
66
|
-
# Most records visit only a handful of live containers. Keep those in
|
|
67
|
-
# slots and allocate the identity hash only for genuinely deep walks.
|
|
68
|
-
if seen
|
|
69
|
-
seen[value] = true
|
|
70
|
-
:hash
|
|
71
|
-
elsif first_seen.nil?
|
|
72
|
-
mark_first_seen(value)
|
|
73
|
-
elsif second_seen.nil?
|
|
74
|
-
mark_second_seen(value)
|
|
75
|
-
elsif third_seen.nil?
|
|
76
|
-
mark_third_seen(value)
|
|
77
|
-
elsif fourth_seen.nil?
|
|
78
|
-
mark_fourth_seen(value)
|
|
79
|
-
else
|
|
80
|
-
promote_traversal_seen(value, first_seen, second_seen, third_seen, fourth_seen)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def unmark_traversal_container(value, added)
|
|
85
|
-
case added
|
|
86
|
-
when :hash
|
|
87
|
-
@traversal_seen.delete(value)
|
|
88
|
-
when :first, :second, :third, :fourth
|
|
89
|
-
unmark_traversal_slot(value, added)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def mark_first_seen(value)
|
|
94
|
-
@traversal_first_seen = value
|
|
95
|
-
:first
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def mark_second_seen(value)
|
|
99
|
-
@traversal_second_seen = value
|
|
100
|
-
:second
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def mark_third_seen(value)
|
|
104
|
-
@traversal_third_seen = value
|
|
105
|
-
:third
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def mark_fourth_seen(value)
|
|
109
|
-
@traversal_fourth_seen = value
|
|
110
|
-
:fourth
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def promote_traversal_seen(value, first_seen, second_seen, third_seen, fourth_seen)
|
|
114
|
-
@traversal_seen = {}.compare_by_identity
|
|
115
|
-
@traversal_seen[first_seen] = true
|
|
116
|
-
@traversal_seen[second_seen] = true
|
|
117
|
-
@traversal_seen[third_seen] = true
|
|
118
|
-
@traversal_seen[fourth_seen] = true
|
|
119
|
-
@traversal_seen[value] = true
|
|
120
|
-
clear_traversal_slots!
|
|
121
|
-
:hash
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def unmark_traversal_slot(value, added)
|
|
125
|
-
return @traversal_seen.delete(value) if @traversal_seen
|
|
126
|
-
|
|
127
|
-
clear_traversal_slot(added)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def clear_traversal_slot(slot)
|
|
131
|
-
case slot
|
|
132
|
-
when :first then @traversal_first_seen = nil
|
|
133
|
-
when :second then @traversal_second_seen = nil
|
|
134
|
-
when :third then @traversal_third_seen = nil
|
|
135
|
-
when :fourth then @traversal_fourth_seen = nil
|
|
17
|
+
seen.add(value)
|
|
18
|
+
begin
|
|
19
|
+
yield
|
|
20
|
+
ensure
|
|
21
|
+
seen.delete(value)
|
|
136
22
|
end
|
|
137
23
|
end
|
|
138
24
|
|
|
139
|
-
def
|
|
140
|
-
@
|
|
141
|
-
@traversal_second_seen = nil
|
|
142
|
-
@traversal_third_seen = nil
|
|
143
|
-
@traversal_fourth_seen = nil
|
|
25
|
+
def traversal_seen
|
|
26
|
+
@traversal_seen ||= Set.new.compare_by_identity
|
|
144
27
|
end
|
|
145
28
|
end
|
|
146
29
|
|
|
@@ -19,20 +19,15 @@ module Julewire
|
|
|
19
19
|
nil
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def flush(*)
|
|
23
|
-
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def close(*)
|
|
27
|
-
self
|
|
28
|
-
end
|
|
22
|
+
def flush(*) = self
|
|
23
|
+
def close(*) = self
|
|
29
24
|
|
|
30
25
|
def health
|
|
31
|
-
{ status: :ok, counts: { captured:
|
|
26
|
+
{ status: :ok, counts: { captured: records.size } }
|
|
32
27
|
end
|
|
33
28
|
|
|
34
29
|
def clear
|
|
35
|
-
|
|
30
|
+
records.clear
|
|
36
31
|
self
|
|
37
32
|
end
|
|
38
33
|
end
|
|
@@ -46,17 +41,16 @@ module Julewire
|
|
|
46
41
|
end
|
|
47
42
|
|
|
48
43
|
def write(value)
|
|
49
|
-
|
|
44
|
+
writes << value
|
|
50
45
|
value.bytesize
|
|
51
46
|
end
|
|
52
47
|
|
|
53
48
|
def flush = self
|
|
54
|
-
|
|
55
49
|
def close = self
|
|
56
50
|
end
|
|
57
51
|
|
|
58
52
|
class << self
|
|
59
|
-
def configure_capture_destination(runtime
|
|
53
|
+
def configure_capture_destination(runtime, **)
|
|
60
54
|
destination = CaptureDestination.new(**)
|
|
61
55
|
runtime.configure do |config|
|
|
62
56
|
config.destinations.clear
|
|
@@ -70,53 +64,9 @@ module Julewire
|
|
|
70
64
|
yield records if block_given?
|
|
71
65
|
records
|
|
72
66
|
end
|
|
73
|
-
|
|
74
|
-
def unregister_destination(kind)
|
|
75
|
-
Core::Destinations.__send__(:unregister, kind)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def reset_shared_scheduler
|
|
79
|
-
Core::Scheduling::SharedScheduler.__send__(:reset_for_test!)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def with_overridden_singleton_method(receiver, method_name, replacement)
|
|
83
|
-
singleton_class = class << receiver; self; end
|
|
84
|
-
method_exists =
|
|
85
|
-
singleton_class.method_defined?(method_name) || singleton_class.private_method_defined?(method_name)
|
|
86
|
-
original = singleton_class.instance_method(method_name) if method_exists
|
|
87
|
-
verbose = $VERBOSE
|
|
88
|
-
$VERBOSE = nil
|
|
89
|
-
singleton_class.define_method(method_name, replacement)
|
|
90
|
-
yield
|
|
91
|
-
ensure
|
|
92
|
-
$VERBOSE = nil
|
|
93
|
-
if original
|
|
94
|
-
singleton_class.define_method(method_name, original)
|
|
95
|
-
elsif singleton_class&.method_defined?(method_name) || singleton_class&.private_method_defined?(method_name)
|
|
96
|
-
singleton_class.remove_method(method_name)
|
|
97
|
-
end
|
|
98
|
-
$VERBOSE = verbose
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def nonblocking_queue_values(queue)
|
|
102
|
-
values = []
|
|
103
|
-
loop do
|
|
104
|
-
values << queue.pop(true)
|
|
105
|
-
rescue ThreadError
|
|
106
|
-
return values
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
67
|
end
|
|
110
68
|
end
|
|
111
69
|
end
|
|
112
70
|
|
|
113
71
|
Testing = Core::Testing unless const_defined?(:Testing, false)
|
|
114
72
|
end
|
|
115
|
-
|
|
116
|
-
require_relative "testing/chaos"
|
|
117
|
-
require_relative "testing/chaos/catalog"
|
|
118
|
-
require_relative "testing/chaos/core_runtime"
|
|
119
|
-
require_relative "testing/chaos/destination"
|
|
120
|
-
require_relative "testing/chaos/emitter"
|
|
121
|
-
require_relative "testing/chaos/raising_output"
|
|
122
|
-
require_relative "testing/contracts"
|