julewire-ractor 1.0.0 → 1.0.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 +8 -0
- data/docs/bridge.md +3 -2
- data/julewire-ractor.gemspec +1 -1
- data/lib/julewire/ractor/bridge.rb +2 -2
- data/lib/julewire/ractor/destination.rb +36 -10
- data/lib/julewire/ractor/remote_payload.rb +5 -2
- data/lib/julewire/ractor/remote_summary_record.rb +1 -1
- data/lib/julewire/ractor/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3d42c3786d7b68451c4353ca8305bc41e5620b5003236ddc89c3707cf2727971
|
|
4
|
+
data.tar.gz: 0225ef575874b54b8bd457f82bf6531821dfe137228dc32de2888d360d1b154b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94b40f17f489ae6749d27f2ed459317eca174c35d7c952d9b7bd5b207ce6d26ac63220d0f8fb0cc56600eca80d48a69278b159d1d625455ff5703e6afffc5d28
|
|
7
|
+
data.tar.gz: 31a6e2729f9db688b42a61c3cdb859777ca70d625a1096bd259de32f8da8f793b75774207d0e3ac16e5efd874cdfb94f9ca461b43099fea657e6b2f4e3ad4d09
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## 1.0.1 - 2026-06-25
|
|
4
|
+
|
|
5
|
+
- Reserve ractor destination queue slots before send and roll them back on
|
|
6
|
+
send failure.
|
|
7
|
+
- Count impossible queue-slot over-release events for destination health
|
|
8
|
+
debugging.
|
|
9
|
+
- Require julewire-core 1.0.1.
|
|
10
|
+
|
|
3
11
|
## 1.0.0 - 2026-06-21
|
|
4
12
|
|
|
5
13
|
- Initial release: Ruby 4 ractor bridge, child-runtime forwarding, remote
|
data/docs/bridge.md
CHANGED
|
@@ -32,8 +32,9 @@ identity or class.
|
|
|
32
32
|
The serialized envelope is a ractor concern. Core keeps only the narrow
|
|
33
33
|
`emit_envelope` hook that accepts already-extracted input, context, carry, and
|
|
34
34
|
a scope snapshot. Payload parsing and scope reconstruction stay in this gem.
|
|
35
|
-
That hook is parent-runtime SPI; the
|
|
36
|
-
facade emit methods instead of a
|
|
35
|
+
That hook is parent-runtime SPI; the bridge marks ractor wire data as owned, and
|
|
36
|
+
the child `RemoteRuntime` exposes the normal facade emit methods instead of a
|
|
37
|
+
detached-envelope API.
|
|
37
38
|
|
|
38
39
|
## Available in Child
|
|
39
40
|
|
data/julewire-ractor.gemspec
CHANGED
|
@@ -70,7 +70,7 @@ module Julewire
|
|
|
70
70
|
Julewire::Core::RuntimeLocator.current = Julewire::Ractor::RemoteRuntime.new(
|
|
71
71
|
port: bridge_port, emit_non_standard_exception_summaries: emit_non_standard_summaries
|
|
72
72
|
)
|
|
73
|
-
Julewire::Core::Propagation.restore(captured_envelope) do
|
|
73
|
+
Julewire::Core::Propagation.restore(captured_envelope, owned: true) do
|
|
74
74
|
callable.call(*call_args)
|
|
75
75
|
end
|
|
76
76
|
ensure
|
|
@@ -113,7 +113,7 @@ module Julewire
|
|
|
113
113
|
payload = RemotePayload.hash_value(message, :payload)
|
|
114
114
|
arguments = RemotePayload.extract(payload)
|
|
115
115
|
arguments[:enforce_level] = false unless enforce_level
|
|
116
|
-
runtime.emit_envelope(**arguments)
|
|
116
|
+
runtime.emit_envelope(**arguments, owned: true)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def reply_to(message, response)
|
|
@@ -12,6 +12,7 @@ module Julewire
|
|
|
12
12
|
queued
|
|
13
13
|
received
|
|
14
14
|
send_error
|
|
15
|
+
slot_underflow_ignored
|
|
15
16
|
worker_accepted
|
|
16
17
|
worker_dropped
|
|
17
18
|
].freeze
|
|
@@ -58,15 +59,17 @@ module Julewire
|
|
|
58
59
|
def emit(record)
|
|
59
60
|
increment(:received)
|
|
60
61
|
return drop(:closed_dropped, record) if closed?
|
|
61
|
-
return drop(:queue_full_dropped, record)
|
|
62
|
+
return drop(:queue_full_dropped, record) unless reserve_slot
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
begin
|
|
65
|
+
@port.send({ command: :emit, record: record })
|
|
66
|
+
increment(:queued)
|
|
67
|
+
rescue StandardError => e
|
|
68
|
+
release_slot
|
|
69
|
+
record_failure(e, phase: :ractor_send)
|
|
70
|
+
drop(:send_error, record)
|
|
71
|
+
end
|
|
66
72
|
nil
|
|
67
|
-
rescue StandardError => e
|
|
68
|
-
record_failure(e, phase: :ractor_send)
|
|
69
|
-
drop(:send_error, record)
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
def flush(timeout: nil)
|
|
@@ -227,8 +230,16 @@ module Julewire
|
|
|
227
230
|
PortLifecycle.close(timeout_port) if defined?(timeout_port) && timeout_port
|
|
228
231
|
end
|
|
229
232
|
|
|
230
|
-
def
|
|
231
|
-
@max_queue.positive?
|
|
233
|
+
def reserve_slot
|
|
234
|
+
return true unless @max_queue.positive?
|
|
235
|
+
|
|
236
|
+
# MRI normally settles this in one pass under the GVL; the CAS loop keeps
|
|
237
|
+
# the reservation honest when multiple emitters race.
|
|
238
|
+
loop do
|
|
239
|
+
current = @in_flight.value
|
|
240
|
+
return false if current >= @max_queue
|
|
241
|
+
return true if @in_flight.compare_and_set(current, current + 1)
|
|
242
|
+
end
|
|
232
243
|
end
|
|
233
244
|
|
|
234
245
|
def closed? = @closed.get
|
|
@@ -269,8 +280,23 @@ module Julewire
|
|
|
269
280
|
nil
|
|
270
281
|
end
|
|
271
282
|
|
|
283
|
+
def release_slot
|
|
284
|
+
return unless @max_queue.positive?
|
|
285
|
+
|
|
286
|
+
loop do
|
|
287
|
+
current = @in_flight.value
|
|
288
|
+
if current <= 0
|
|
289
|
+
# Late or duplicate ACKs can arrive after teardown/reset; keep them visible
|
|
290
|
+
# without treating the ignored underflow as an operator-facing defect.
|
|
291
|
+
increment(:slot_underflow_ignored)
|
|
292
|
+
return
|
|
293
|
+
end
|
|
294
|
+
return if @in_flight.compare_and_set(current, current - 1)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
272
298
|
def decrement_in_flight
|
|
273
|
-
|
|
299
|
+
release_slot
|
|
274
300
|
end
|
|
275
301
|
|
|
276
302
|
def increment(key)
|
|
@@ -20,7 +20,10 @@ module Julewire
|
|
|
20
20
|
|
|
21
21
|
def input_value(payload)
|
|
22
22
|
value = Core::Integration::Values::Read.hash_value(payload, :input, default: MISSING)
|
|
23
|
-
value.equal?(MISSING)
|
|
23
|
+
return {} if value.equal?(MISSING)
|
|
24
|
+
return Core::Fields::FieldSet.deep_symbolize_owned_keys(value) if value.is_a?(Hash)
|
|
25
|
+
|
|
26
|
+
value
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def scope_snapshot(scope_payload)
|
|
@@ -35,7 +38,7 @@ module Julewire
|
|
|
35
38
|
|
|
36
39
|
def hash_value(hash, key)
|
|
37
40
|
value = Core::Integration::Values::Read.hash_value(hash, key)
|
|
38
|
-
value.is_a?(Hash) ? Core::Fields::FieldSet.
|
|
41
|
+
value.is_a?(Hash) ? Core::Fields::FieldSet.deep_symbolize_owned_keys(value) : {}
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: julewire-ractor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Grebennik
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 1.0.1
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 1.0.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: zeitwerk
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|