cdc-parallel 0.2.0 → 0.2.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 +10 -0
- data/lib/cdc/parallel/configuration.rb +2 -0
- data/lib/cdc/parallel/processor_pool.rb +44 -4
- data/lib/cdc/parallel/version.rb +1 -1
- data/lib/cdc/parallel.rb +1 -0
- data/sig/cdc/parallel/processor_pool.rbs +25 -10
- data/sig/cdc/parallel/result_collector.rbs +6 -0
- data/sig/cdc/parallel/version.rbs +1 -1
- data/sig/shims/timeout.rbs +3 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72b3b33568a37fa04b270edad8511a446703f115d78d86c2347c6af2e4be76a0
|
|
4
|
+
data.tar.gz: e044e42d90f11b6b75b946ef0f1b1725f2810056be353a56342a908c139eb91c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3780fa1a616b33e824cff4f6c7f0ec8402085e5a742abbaa45082d1dc8ce6c7be04bd462aa78e4afcb61f7f9a3b0adab54ed4e47f972633c12348d57e65a632f
|
|
7
|
+
data.tar.gz: 01ae9a18f4d8a6bab6c5f7e71333c38475a1930bdce6326e41bc9376100e74800cb850b22b8fda1de9638a2e78be3094a27bfd1b6592a6e868554ce933b471af
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
|
|
6
6
|
|
|
7
|
+
## [0.2.1] - 2026-06-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
v0.2.1 - Correctness and reliability patch
|
|
12
|
+
|
|
13
|
+
- Enforced processor timeout handling.
|
|
14
|
+
- Fixed transaction partial-failure behavior.
|
|
15
|
+
- Added regression coverage for hung processors and transaction failure cases.
|
|
16
|
+
|
|
7
17
|
## [0.2.0] - 2026-06-03
|
|
8
18
|
|
|
9
19
|
### Added
|
|
@@ -12,6 +12,8 @@ module CDC
|
|
|
12
12
|
def initialize(size: Etc.nprocessors, timeout: nil)
|
|
13
13
|
raise ArgumentError, "size must be an Integer" unless size.is_a?(Integer)
|
|
14
14
|
raise ArgumentError, "size must be greater than zero" unless size.positive?
|
|
15
|
+
raise ArgumentError, "timeout must be numeric" unless timeout.nil? || timeout.is_a?(Numeric)
|
|
16
|
+
raise ArgumentError, "timeout must be greater than zero" if timeout && !timeout.positive?
|
|
15
17
|
|
|
16
18
|
super
|
|
17
19
|
::Ractor.make_shareable(self)
|
|
@@ -8,7 +8,7 @@ module CDC
|
|
|
8
8
|
# This pays Ractor startup cost once, keeps workers alive after processor
|
|
9
9
|
# failures, and provides both synchronous single-item processing and batched
|
|
10
10
|
# dispatch for throughput-oriented benchmarks and runtimes.
|
|
11
|
-
class ProcessorPool
|
|
11
|
+
class ProcessorPool # rubocop:disable Metrics/ClassLength
|
|
12
12
|
# @param processor [CDC::Core::Processor]
|
|
13
13
|
# @param size [Integer]
|
|
14
14
|
# @param timeout [Float, nil]
|
|
@@ -80,7 +80,7 @@ module CDC
|
|
|
80
80
|
"#{processor.class} must declare ractor_safe!"
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def build_worker(processor)
|
|
83
|
+
def build_worker(processor) # rubocop:disable Metrics/MethodLength
|
|
84
84
|
::Ractor.new(processor) do |safe_processor|
|
|
85
85
|
loop do
|
|
86
86
|
message = ::Ractor.receive
|
|
@@ -96,7 +96,11 @@ module CDC
|
|
|
96
96
|
CDC::Parallel::ResultCollector.worker_failure(e)
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
begin
|
|
100
|
+
reply_port << [index, response]
|
|
101
|
+
rescue Ractor::ClosedError
|
|
102
|
+
# The caller may have timed out and closed the reply port.
|
|
103
|
+
end
|
|
100
104
|
end
|
|
101
105
|
end
|
|
102
106
|
end
|
|
@@ -112,14 +116,50 @@ module CDC
|
|
|
112
116
|
|
|
113
117
|
def collect_results(reply_port, count)
|
|
114
118
|
results = Array.new(count)
|
|
119
|
+
return results.freeze if count.zero?
|
|
120
|
+
|
|
121
|
+
if @configuration.timeout
|
|
122
|
+
collect_results_with_timeout(reply_port, results)
|
|
123
|
+
else
|
|
124
|
+
collect_results_without_timeout(reply_port, results)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
def collect_results_without_timeout(reply_port, results)
|
|
129
|
+
results.length.times do
|
|
117
130
|
index, response = reply_port.receive
|
|
118
131
|
results[index] = ResultCollector.normalize(response)
|
|
119
132
|
end
|
|
120
133
|
|
|
121
134
|
results.freeze
|
|
122
135
|
end
|
|
136
|
+
|
|
137
|
+
def collect_results_with_timeout(reply_port, results)
|
|
138
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @configuration.timeout
|
|
139
|
+
|
|
140
|
+
results.length.times do
|
|
141
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
142
|
+
return timeout_results(results) unless remaining.positive?
|
|
143
|
+
|
|
144
|
+
index, response = ::Timeout.timeout(remaining, TimeoutError) { reply_port.receive }
|
|
145
|
+
results[index] = ResultCollector.normalize(response)
|
|
146
|
+
rescue TimeoutError
|
|
147
|
+
return timeout_results(results)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
results.freeze
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def timeout_results(results)
|
|
154
|
+
missing = results.count(&:nil?)
|
|
155
|
+
timeout_error = TimeoutError.new(
|
|
156
|
+
"processor pool timed out after #{@configuration.timeout} seconds waiting for #{missing} result(s)"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
results.map do |result|
|
|
160
|
+
result || CDC::Core::ProcessorResult.failure(timeout_error)
|
|
161
|
+
end.freeze
|
|
162
|
+
end
|
|
123
163
|
end
|
|
124
164
|
end
|
|
125
165
|
end
|
data/lib/cdc/parallel/version.rb
CHANGED
data/lib/cdc/parallel.rb
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
module CDC
|
|
2
2
|
module Parallel
|
|
3
|
-
# Executes one Ractor-safe processor in
|
|
4
|
-
#
|
|
5
|
-
# This v0.1 implementation intentionally uses one-shot worker Ractors for
|
|
6
|
-
# deterministic synchronous semantics while preserving the public pool API.
|
|
7
|
-
# The parallel-pool dependency is kept as the runtime foundation for later
|
|
8
|
-
# async/throughput-focused versions.
|
|
3
|
+
# Executes one Ractor-safe processor in pre-warmed persistent Ractor workers.
|
|
9
4
|
class ProcessorPool
|
|
10
5
|
@processor: untyped
|
|
11
6
|
|
|
12
7
|
@configuration: untyped
|
|
13
8
|
|
|
9
|
+
@workers: untyped
|
|
10
|
+
|
|
11
|
+
@next_worker: Integer
|
|
12
|
+
|
|
14
13
|
@shutdown: untyped
|
|
15
14
|
|
|
16
15
|
# @param processor [CDC::Core::Processor]
|
|
@@ -19,11 +18,17 @@ module CDC
|
|
|
19
18
|
# @return [void]
|
|
20
19
|
def initialize: (processor: untyped, ?size: untyped, ?timeout: untyped?) -> void
|
|
21
20
|
|
|
22
|
-
# Process one
|
|
21
|
+
# Process one work item synchronously.
|
|
23
22
|
#
|
|
24
|
-
# @param
|
|
23
|
+
# @param item [Object]
|
|
25
24
|
# @return [CDC::Core::ProcessorResult]
|
|
26
|
-
def process: (untyped
|
|
25
|
+
def process: (untyped item) -> untyped
|
|
26
|
+
|
|
27
|
+
# Process many work items using the pre-warmed worker pool.
|
|
28
|
+
#
|
|
29
|
+
# @param items [Array<Object>]
|
|
30
|
+
# @return [Array<CDC::Core::ProcessorResult>]
|
|
31
|
+
def process_many: (untyped items) -> untyped
|
|
27
32
|
|
|
28
33
|
# Shut down the pool.
|
|
29
34
|
#
|
|
@@ -34,7 +39,17 @@ module CDC
|
|
|
34
39
|
|
|
35
40
|
def validate_processor!: (untyped processor) -> (nil | untyped)
|
|
36
41
|
|
|
37
|
-
def
|
|
42
|
+
def build_worker: (untyped processor) -> untyped
|
|
43
|
+
|
|
44
|
+
def next_worker: () -> untyped
|
|
45
|
+
|
|
46
|
+
def collect_results: (untyped reply_port, Integer count) -> untyped
|
|
47
|
+
|
|
48
|
+
def collect_results_without_timeout: (untyped reply_port, untyped results) -> untyped
|
|
49
|
+
|
|
50
|
+
def collect_results_with_timeout: (untyped reply_port, untyped results) -> untyped
|
|
51
|
+
|
|
52
|
+
def timeout_results: (untyped results) -> untyped
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
end
|
|
@@ -4,6 +4,12 @@ module CDC
|
|
|
4
4
|
class ResultCollector
|
|
5
5
|
FAILURE_MARKER: :__cdc_parallel_failure__
|
|
6
6
|
|
|
7
|
+
# Build a shareable success payload that can safely cross a Ractor boundary.
|
|
8
|
+
#
|
|
9
|
+
# @param value [Object]
|
|
10
|
+
# @return [Object]
|
|
11
|
+
def self.worker_success: (untyped value) -> untyped
|
|
12
|
+
|
|
7
13
|
# Build a shareable failure payload that can safely cross a Ractor boundary.
|
|
8
14
|
#
|
|
9
15
|
# @param error [Exception]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cdc-parallel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ken C. Demanawa
|
|
@@ -73,6 +73,7 @@ files:
|
|
|
73
73
|
- sig/shims/cdc_core.rbs
|
|
74
74
|
- sig/shims/data_define.rbs
|
|
75
75
|
- sig/shims/etc.rbs
|
|
76
|
+
- sig/shims/timeout.rbs
|
|
76
77
|
homepage: https://kanutocd.github.io/cdc-parallel/
|
|
77
78
|
licenses:
|
|
78
79
|
- MIT
|