fractor 0.1.6 → 0.1.8
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/.rubocop_todo.yml +227 -102
- data/README.adoc +113 -1940
- data/docs/.lycheeignore +16 -0
- data/docs/Gemfile +24 -0
- data/docs/README.md +157 -0
- data/docs/_config.yml +151 -0
- data/docs/_features/error-handling.adoc +1192 -0
- data/docs/_features/index.adoc +80 -0
- data/docs/_features/monitoring.adoc +589 -0
- data/docs/_features/signal-handling.adoc +202 -0
- data/docs/_features/workflows.adoc +1235 -0
- data/docs/_guides/continuous-mode.adoc +736 -0
- data/docs/_guides/cookbook.adoc +1133 -0
- data/docs/_guides/index.adoc +55 -0
- data/docs/_guides/pipeline-mode.adoc +730 -0
- data/docs/_guides/troubleshooting.adoc +358 -0
- data/docs/_pages/architecture.adoc +1390 -0
- data/docs/_pages/core-concepts.adoc +1392 -0
- data/docs/_pages/design-principles.adoc +862 -0
- data/docs/_pages/getting-started.adoc +290 -0
- data/docs/_pages/installation.adoc +143 -0
- data/docs/_reference/api.adoc +1080 -0
- data/docs/_reference/error-reporting.adoc +670 -0
- data/docs/_reference/examples.adoc +181 -0
- data/docs/_reference/index.adoc +96 -0
- data/docs/_reference/troubleshooting.adoc +862 -0
- data/docs/_tutorials/complex-workflows.adoc +1022 -0
- data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
- data/docs/_tutorials/first-application.adoc +384 -0
- data/docs/_tutorials/index.adoc +48 -0
- data/docs/_tutorials/long-running-services.adoc +931 -0
- data/docs/assets/images/favicon-16.png +0 -0
- data/docs/assets/images/favicon-32.png +0 -0
- data/docs/assets/images/favicon-48.png +0 -0
- data/docs/assets/images/favicon.ico +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/assets/images/favicon.svg +45 -0
- data/docs/assets/images/fractor-icon.svg +49 -0
- data/docs/assets/images/fractor-logo.svg +61 -0
- data/docs/index.adoc +131 -0
- data/docs/lychee.toml +39 -0
- data/examples/api_aggregator/README.adoc +627 -0
- data/examples/api_aggregator/api_aggregator.rb +376 -0
- data/examples/auto_detection/README.adoc +407 -29
- data/examples/continuous_chat_common/message_protocol.rb +1 -1
- data/examples/error_reporting.rb +207 -0
- data/examples/file_processor/README.adoc +170 -0
- data/examples/file_processor/file_processor.rb +615 -0
- data/examples/file_processor/sample_files/invalid.csv +1 -0
- data/examples/file_processor/sample_files/orders.xml +24 -0
- data/examples/file_processor/sample_files/products.json +23 -0
- data/examples/file_processor/sample_files/users.csv +6 -0
- data/examples/hierarchical_hasher/README.adoc +629 -41
- data/examples/image_processor/README.adoc +610 -0
- data/examples/image_processor/image_processor.rb +349 -0
- data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
- data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
- data/examples/image_processor/test_images/sample_1.png +1 -0
- data/examples/image_processor/test_images/sample_10.png +1 -0
- data/examples/image_processor/test_images/sample_2.png +1 -0
- data/examples/image_processor/test_images/sample_3.png +1 -0
- data/examples/image_processor/test_images/sample_4.png +1 -0
- data/examples/image_processor/test_images/sample_5.png +1 -0
- data/examples/image_processor/test_images/sample_6.png +1 -0
- data/examples/image_processor/test_images/sample_7.png +1 -0
- data/examples/image_processor/test_images/sample_8.png +1 -0
- data/examples/image_processor/test_images/sample_9.png +1 -0
- data/examples/log_analyzer/README.adoc +662 -0
- data/examples/log_analyzer/log_analyzer.rb +579 -0
- data/examples/log_analyzer/sample_logs/apache.log +20 -0
- data/examples/log_analyzer/sample_logs/json.log +15 -0
- data/examples/log_analyzer/sample_logs/nginx.log +15 -0
- data/examples/log_analyzer/sample_logs/rails.log +29 -0
- data/examples/multi_work_type/README.adoc +576 -26
- data/examples/performance_monitoring.rb +120 -0
- data/examples/pipeline_processing/README.adoc +740 -26
- data/examples/pipeline_processing/pipeline_processing.rb +2 -2
- data/examples/priority_work_example.rb +155 -0
- data/examples/producer_subscriber/README.adoc +889 -46
- data/examples/scatter_gather/README.adoc +829 -27
- data/examples/simple/README.adoc +347 -0
- data/examples/specialized_workers/README.adoc +622 -26
- data/examples/specialized_workers/specialized_workers.rb +44 -8
- data/examples/stream_processor/README.adoc +206 -0
- data/examples/stream_processor/stream_processor.rb +284 -0
- data/examples/web_scraper/README.adoc +625 -0
- data/examples/web_scraper/web_scraper.rb +285 -0
- data/examples/workflow/README.adoc +406 -0
- data/examples/workflow/circuit_breaker/README.adoc +360 -0
- data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
- data/examples/workflow/conditional/README.adoc +483 -0
- data/examples/workflow/conditional/conditional_workflow.rb +215 -0
- data/examples/workflow/dead_letter_queue/README.adoc +374 -0
- data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
- data/examples/workflow/fan_out/README.adoc +381 -0
- data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
- data/examples/workflow/retry/README.adoc +248 -0
- data/examples/workflow/retry/retry_workflow.rb +195 -0
- data/examples/workflow/simple_linear/README.adoc +267 -0
- data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
- data/examples/workflow/simplified/README.adoc +329 -0
- data/examples/workflow/simplified/simplified_workflow.rb +222 -0
- data/exe/fractor +10 -0
- data/lib/fractor/cli.rb +288 -0
- data/lib/fractor/configuration.rb +307 -0
- data/lib/fractor/continuous_server.rb +60 -65
- data/lib/fractor/error_formatter.rb +72 -0
- data/lib/fractor/error_report_generator.rb +152 -0
- data/lib/fractor/error_reporter.rb +244 -0
- data/lib/fractor/error_statistics.rb +147 -0
- data/lib/fractor/execution_tracer.rb +162 -0
- data/lib/fractor/logger.rb +230 -0
- data/lib/fractor/main_loop_handler.rb +406 -0
- data/lib/fractor/main_loop_handler3.rb +135 -0
- data/lib/fractor/main_loop_handler4.rb +299 -0
- data/lib/fractor/performance_metrics_collector.rb +181 -0
- data/lib/fractor/performance_monitor.rb +215 -0
- data/lib/fractor/performance_report_generator.rb +202 -0
- data/lib/fractor/priority_work.rb +93 -0
- data/lib/fractor/priority_work_queue.rb +189 -0
- data/lib/fractor/result_aggregator.rb +32 -0
- data/lib/fractor/shutdown_handler.rb +168 -0
- data/lib/fractor/signal_handler.rb +80 -0
- data/lib/fractor/supervisor.rb +382 -269
- data/lib/fractor/supervisor_logger.rb +88 -0
- data/lib/fractor/version.rb +1 -1
- data/lib/fractor/work.rb +12 -0
- data/lib/fractor/work_distribution_manager.rb +151 -0
- data/lib/fractor/work_queue.rb +20 -0
- data/lib/fractor/work_result.rb +181 -9
- data/lib/fractor/worker.rb +73 -0
- data/lib/fractor/workflow/builder.rb +210 -0
- data/lib/fractor/workflow/chain_builder.rb +169 -0
- data/lib/fractor/workflow/circuit_breaker.rb +183 -0
- data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
- data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
- data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
- data/lib/fractor/workflow/execution_hooks.rb +39 -0
- data/lib/fractor/workflow/execution_strategy.rb +225 -0
- data/lib/fractor/workflow/execution_trace.rb +134 -0
- data/lib/fractor/workflow/helpers.rb +191 -0
- data/lib/fractor/workflow/job.rb +290 -0
- data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
- data/lib/fractor/workflow/logger.rb +110 -0
- data/lib/fractor/workflow/pre_execution_context.rb +193 -0
- data/lib/fractor/workflow/retry_config.rb +156 -0
- data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
- data/lib/fractor/workflow/retry_strategy.rb +93 -0
- data/lib/fractor/workflow/structured_logger.rb +30 -0
- data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
- data/lib/fractor/workflow/visualizer.rb +211 -0
- data/lib/fractor/workflow/workflow_context.rb +132 -0
- data/lib/fractor/workflow/workflow_executor.rb +669 -0
- data/lib/fractor/workflow/workflow_result.rb +55 -0
- data/lib/fractor/workflow/workflow_validator.rb +295 -0
- data/lib/fractor/workflow.rb +333 -0
- data/lib/fractor/wrapped_ractor.rb +66 -101
- data/lib/fractor/wrapped_ractor3.rb +161 -0
- data/lib/fractor/wrapped_ractor4.rb +242 -0
- data/lib/fractor.rb +92 -4
- metadata +179 -6
- data/tests/sample.rb.bak +0 -309
- data/tests/sample_working.rb.bak +0 -209
data/lib/fractor.rb
CHANGED
|
@@ -2,16 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
# Require all component files
|
|
4
4
|
require_relative "fractor/version"
|
|
5
|
-
require_relative "fractor/
|
|
5
|
+
require_relative "fractor/configuration"
|
|
6
|
+
require_relative "fractor/logger"
|
|
6
7
|
require_relative "fractor/work"
|
|
7
8
|
require_relative "fractor/work_result"
|
|
8
|
-
require_relative "fractor/
|
|
9
|
+
require_relative "fractor/work_queue"
|
|
10
|
+
require_relative "fractor/priority_work"
|
|
11
|
+
require_relative "fractor/priority_work_queue"
|
|
9
12
|
require_relative "fractor/wrapped_ractor"
|
|
13
|
+
require_relative "fractor/wrapped_ractor3"
|
|
14
|
+
require_relative "fractor/wrapped_ractor4"
|
|
15
|
+
require_relative "fractor/worker"
|
|
16
|
+
require_relative "fractor/work_distribution_manager"
|
|
17
|
+
require_relative "fractor/shutdown_handler"
|
|
18
|
+
require_relative "fractor/main_loop_handler"
|
|
19
|
+
require_relative "fractor/main_loop_handler3"
|
|
20
|
+
require_relative "fractor/main_loop_handler4"
|
|
21
|
+
require_relative "fractor/signal_handler"
|
|
22
|
+
require_relative "fractor/supervisor_logger"
|
|
10
23
|
require_relative "fractor/supervisor"
|
|
11
|
-
require_relative "fractor/
|
|
24
|
+
require_relative "fractor/result_aggregator"
|
|
12
25
|
require_relative "fractor/continuous_server"
|
|
26
|
+
require_relative "fractor/workflow"
|
|
27
|
+
require_relative "fractor/performance_monitor"
|
|
28
|
+
require_relative "fractor/performance_metrics_collector"
|
|
29
|
+
require_relative "fractor/performance_report_generator"
|
|
30
|
+
require_relative "fractor/error_reporter"
|
|
31
|
+
require_relative "fractor/error_statistics"
|
|
32
|
+
require_relative "fractor/error_report_generator"
|
|
33
|
+
require_relative "fractor/error_formatter"
|
|
34
|
+
require_relative "fractor/execution_tracer"
|
|
35
|
+
|
|
36
|
+
# Optional: CLI (only load if Thor is available)
|
|
37
|
+
begin
|
|
38
|
+
require "thor"
|
|
39
|
+
require_relative "fractor/cli"
|
|
40
|
+
rescue LoadError
|
|
41
|
+
# Thor not available, CLI commands will not be available
|
|
42
|
+
end
|
|
13
43
|
|
|
14
44
|
# Fractor: Function-driven Ractors framework
|
|
15
45
|
module Fractor
|
|
16
|
-
#
|
|
46
|
+
# Exception raised when trying to push to a closed queue
|
|
47
|
+
class ClosedQueueError < StandardError; end
|
|
48
|
+
|
|
49
|
+
# Configure Fractor with a block.
|
|
50
|
+
#
|
|
51
|
+
# @yield [Configuration] The configuration object
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# Fractor.configure do |config|
|
|
55
|
+
# config.debug = true
|
|
56
|
+
# config.logger = Logger.new(STDOUT)
|
|
57
|
+
# config.default_worker_timeout = 60
|
|
58
|
+
# end
|
|
59
|
+
def self.configure(&)
|
|
60
|
+
Configuration.configure(&)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Load configuration from a YAML file.
|
|
64
|
+
#
|
|
65
|
+
# @param file_path [String] Path to the YAML configuration file
|
|
66
|
+
def self.configure_from_file(file_path)
|
|
67
|
+
Configuration.configure_from_file(file_path)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Load configuration from environment variables.
|
|
71
|
+
# Environment variables should be prefixed with FRACTOR_.
|
|
72
|
+
#
|
|
73
|
+
# @example
|
|
74
|
+
# # Set environment variables
|
|
75
|
+
# # export FRACTOR_DEBUG=true
|
|
76
|
+
# # export FRACTOR_DEFAULT_WORKER_TIMEOUT=60
|
|
77
|
+
#
|
|
78
|
+
# Fractor.configure_from_env
|
|
79
|
+
def self.configure_from_env
|
|
80
|
+
Configuration.configure_from_env
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Access the global configuration instance.
|
|
84
|
+
#
|
|
85
|
+
# @return [Configuration] The configuration instance
|
|
86
|
+
def self.config
|
|
87
|
+
Configuration.config
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Reset all global state to ensure isolation between different uses of Fractor.
|
|
91
|
+
# This is important for testing and when multiple gems use Fractor together.
|
|
92
|
+
#
|
|
93
|
+
# @example Reset state between tests
|
|
94
|
+
# Fractor.reset! # Clears logger, tracer, and other global state
|
|
95
|
+
def self.reset!
|
|
96
|
+
Configuration.reset!
|
|
97
|
+
ExecutionTracer.reset!
|
|
98
|
+
reset_logger!
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Reset logger state (internal method, use reset! instead)
|
|
102
|
+
def self.reset_logger!
|
|
103
|
+
@logger = nil if defined?(@logger)
|
|
104
|
+
end
|
|
17
105
|
end
|
metadata
CHANGED
|
@@ -1,20 +1,49 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fractor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ronald Tse
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2026-01-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: logger
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: benchmark
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
13
41
|
description: Fractor is a lightweight Ruby framework designed to simplify the process
|
|
14
42
|
of distributing computational work across multiple Ractors.
|
|
15
43
|
email:
|
|
16
44
|
- ronald.tse@ribose.com
|
|
17
|
-
executables:
|
|
45
|
+
executables:
|
|
46
|
+
- fractor
|
|
18
47
|
extensions: []
|
|
19
48
|
extra_rdoc_files: []
|
|
20
49
|
files:
|
|
@@ -25,6 +54,47 @@ files:
|
|
|
25
54
|
- CODE_OF_CONDUCT.md
|
|
26
55
|
- README.adoc
|
|
27
56
|
- Rakefile
|
|
57
|
+
- docs/.lycheeignore
|
|
58
|
+
- docs/Gemfile
|
|
59
|
+
- docs/README.md
|
|
60
|
+
- docs/_config.yml
|
|
61
|
+
- docs/_features/error-handling.adoc
|
|
62
|
+
- docs/_features/index.adoc
|
|
63
|
+
- docs/_features/monitoring.adoc
|
|
64
|
+
- docs/_features/signal-handling.adoc
|
|
65
|
+
- docs/_features/workflows.adoc
|
|
66
|
+
- docs/_guides/continuous-mode.adoc
|
|
67
|
+
- docs/_guides/cookbook.adoc
|
|
68
|
+
- docs/_guides/index.adoc
|
|
69
|
+
- docs/_guides/pipeline-mode.adoc
|
|
70
|
+
- docs/_guides/troubleshooting.adoc
|
|
71
|
+
- docs/_pages/architecture.adoc
|
|
72
|
+
- docs/_pages/core-concepts.adoc
|
|
73
|
+
- docs/_pages/design-principles.adoc
|
|
74
|
+
- docs/_pages/getting-started.adoc
|
|
75
|
+
- docs/_pages/installation.adoc
|
|
76
|
+
- docs/_reference/api.adoc
|
|
77
|
+
- docs/_reference/error-reporting.adoc
|
|
78
|
+
- docs/_reference/examples.adoc
|
|
79
|
+
- docs/_reference/index.adoc
|
|
80
|
+
- docs/_reference/troubleshooting.adoc
|
|
81
|
+
- docs/_tutorials/complex-workflows.adoc
|
|
82
|
+
- docs/_tutorials/data-processing-pipeline.adoc
|
|
83
|
+
- docs/_tutorials/first-application.adoc
|
|
84
|
+
- docs/_tutorials/index.adoc
|
|
85
|
+
- docs/_tutorials/long-running-services.adoc
|
|
86
|
+
- docs/assets/images/favicon-16.png
|
|
87
|
+
- docs/assets/images/favicon-32.png
|
|
88
|
+
- docs/assets/images/favicon-48.png
|
|
89
|
+
- docs/assets/images/favicon.ico
|
|
90
|
+
- docs/assets/images/favicon.png
|
|
91
|
+
- docs/assets/images/favicon.svg
|
|
92
|
+
- docs/assets/images/fractor-icon.svg
|
|
93
|
+
- docs/assets/images/fractor-logo.svg
|
|
94
|
+
- docs/index.adoc
|
|
95
|
+
- docs/lychee.toml
|
|
96
|
+
- examples/api_aggregator/README.adoc
|
|
97
|
+
- examples/api_aggregator/api_aggregator.rb
|
|
28
98
|
- examples/auto_detection/README.adoc
|
|
29
99
|
- examples/auto_detection/auto_detection.rb
|
|
30
100
|
- examples/continuous_chat_common/message_protocol.rb
|
|
@@ -37,32 +107,135 @@ files:
|
|
|
37
107
|
- examples/continuous_chat_server/chat_client.rb
|
|
38
108
|
- examples/continuous_chat_server/chat_server.rb
|
|
39
109
|
- examples/continuous_chat_server/simulate.rb
|
|
110
|
+
- examples/error_reporting.rb
|
|
111
|
+
- examples/file_processor/README.adoc
|
|
112
|
+
- examples/file_processor/file_processor.rb
|
|
113
|
+
- examples/file_processor/sample_files/invalid.csv
|
|
114
|
+
- examples/file_processor/sample_files/orders.xml
|
|
115
|
+
- examples/file_processor/sample_files/products.json
|
|
116
|
+
- examples/file_processor/sample_files/users.csv
|
|
40
117
|
- examples/hierarchical_hasher/README.adoc
|
|
41
118
|
- examples/hierarchical_hasher/hierarchical_hasher.rb
|
|
119
|
+
- examples/image_processor/README.adoc
|
|
120
|
+
- examples/image_processor/image_processor.rb
|
|
121
|
+
- examples/image_processor/processed_images/sample_10_processed.jpg.json
|
|
122
|
+
- examples/image_processor/processed_images/sample_1_processed.jpg.json
|
|
123
|
+
- examples/image_processor/processed_images/sample_2_processed.jpg.json
|
|
124
|
+
- examples/image_processor/processed_images/sample_3_processed.jpg.json
|
|
125
|
+
- examples/image_processor/processed_images/sample_4_processed.jpg.json
|
|
126
|
+
- examples/image_processor/processed_images/sample_5_processed.jpg.json
|
|
127
|
+
- examples/image_processor/processed_images/sample_6_processed.jpg.json
|
|
128
|
+
- examples/image_processor/processed_images/sample_7_processed.jpg.json
|
|
129
|
+
- examples/image_processor/processed_images/sample_8_processed.jpg.json
|
|
130
|
+
- examples/image_processor/processed_images/sample_9_processed.jpg.json
|
|
131
|
+
- examples/image_processor/test_images/sample_1.png
|
|
132
|
+
- examples/image_processor/test_images/sample_10.png
|
|
133
|
+
- examples/image_processor/test_images/sample_2.png
|
|
134
|
+
- examples/image_processor/test_images/sample_3.png
|
|
135
|
+
- examples/image_processor/test_images/sample_4.png
|
|
136
|
+
- examples/image_processor/test_images/sample_5.png
|
|
137
|
+
- examples/image_processor/test_images/sample_6.png
|
|
138
|
+
- examples/image_processor/test_images/sample_7.png
|
|
139
|
+
- examples/image_processor/test_images/sample_8.png
|
|
140
|
+
- examples/image_processor/test_images/sample_9.png
|
|
141
|
+
- examples/log_analyzer/README.adoc
|
|
142
|
+
- examples/log_analyzer/log_analyzer.rb
|
|
143
|
+
- examples/log_analyzer/sample_logs/apache.log
|
|
144
|
+
- examples/log_analyzer/sample_logs/json.log
|
|
145
|
+
- examples/log_analyzer/sample_logs/nginx.log
|
|
146
|
+
- examples/log_analyzer/sample_logs/rails.log
|
|
42
147
|
- examples/multi_work_type/README.adoc
|
|
43
148
|
- examples/multi_work_type/multi_work_type.rb
|
|
149
|
+
- examples/performance_monitoring.rb
|
|
44
150
|
- examples/pipeline_processing/README.adoc
|
|
45
151
|
- examples/pipeline_processing/pipeline_processing.rb
|
|
152
|
+
- examples/priority_work_example.rb
|
|
46
153
|
- examples/producer_subscriber/README.adoc
|
|
47
154
|
- examples/producer_subscriber/producer_subscriber.rb
|
|
48
155
|
- examples/scatter_gather/README.adoc
|
|
49
156
|
- examples/scatter_gather/scatter_gather.rb
|
|
157
|
+
- examples/simple/README.adoc
|
|
50
158
|
- examples/simple/sample.rb
|
|
51
159
|
- examples/specialized_workers/README.adoc
|
|
52
160
|
- examples/specialized_workers/specialized_workers.rb
|
|
161
|
+
- examples/stream_processor/README.adoc
|
|
162
|
+
- examples/stream_processor/stream_processor.rb
|
|
163
|
+
- examples/web_scraper/README.adoc
|
|
164
|
+
- examples/web_scraper/web_scraper.rb
|
|
165
|
+
- examples/workflow/README.adoc
|
|
166
|
+
- examples/workflow/circuit_breaker/README.adoc
|
|
167
|
+
- examples/workflow/circuit_breaker/circuit_breaker_workflow.rb
|
|
168
|
+
- examples/workflow/conditional/README.adoc
|
|
169
|
+
- examples/workflow/conditional/conditional_workflow.rb
|
|
170
|
+
- examples/workflow/dead_letter_queue/README.adoc
|
|
171
|
+
- examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb
|
|
172
|
+
- examples/workflow/fan_out/README.adoc
|
|
173
|
+
- examples/workflow/fan_out/fan_out_workflow.rb
|
|
174
|
+
- examples/workflow/retry/README.adoc
|
|
175
|
+
- examples/workflow/retry/retry_workflow.rb
|
|
176
|
+
- examples/workflow/simple_linear/README.adoc
|
|
177
|
+
- examples/workflow/simple_linear/simple_linear_workflow.rb
|
|
178
|
+
- examples/workflow/simplified/README.adoc
|
|
179
|
+
- examples/workflow/simplified/simplified_workflow.rb
|
|
180
|
+
- exe/fractor
|
|
53
181
|
- lib/fractor.rb
|
|
182
|
+
- lib/fractor/cli.rb
|
|
183
|
+
- lib/fractor/configuration.rb
|
|
54
184
|
- lib/fractor/continuous_server.rb
|
|
185
|
+
- lib/fractor/error_formatter.rb
|
|
186
|
+
- lib/fractor/error_report_generator.rb
|
|
187
|
+
- lib/fractor/error_reporter.rb
|
|
188
|
+
- lib/fractor/error_statistics.rb
|
|
189
|
+
- lib/fractor/execution_tracer.rb
|
|
190
|
+
- lib/fractor/logger.rb
|
|
191
|
+
- lib/fractor/main_loop_handler.rb
|
|
192
|
+
- lib/fractor/main_loop_handler3.rb
|
|
193
|
+
- lib/fractor/main_loop_handler4.rb
|
|
194
|
+
- lib/fractor/performance_metrics_collector.rb
|
|
195
|
+
- lib/fractor/performance_monitor.rb
|
|
196
|
+
- lib/fractor/performance_report_generator.rb
|
|
197
|
+
- lib/fractor/priority_work.rb
|
|
198
|
+
- lib/fractor/priority_work_queue.rb
|
|
55
199
|
- lib/fractor/result_aggregator.rb
|
|
200
|
+
- lib/fractor/shutdown_handler.rb
|
|
201
|
+
- lib/fractor/signal_handler.rb
|
|
56
202
|
- lib/fractor/supervisor.rb
|
|
203
|
+
- lib/fractor/supervisor_logger.rb
|
|
57
204
|
- lib/fractor/version.rb
|
|
58
205
|
- lib/fractor/work.rb
|
|
206
|
+
- lib/fractor/work_distribution_manager.rb
|
|
59
207
|
- lib/fractor/work_queue.rb
|
|
60
208
|
- lib/fractor/work_result.rb
|
|
61
209
|
- lib/fractor/worker.rb
|
|
210
|
+
- lib/fractor/workflow.rb
|
|
211
|
+
- lib/fractor/workflow/builder.rb
|
|
212
|
+
- lib/fractor/workflow/chain_builder.rb
|
|
213
|
+
- lib/fractor/workflow/circuit_breaker.rb
|
|
214
|
+
- lib/fractor/workflow/circuit_breaker_orchestrator.rb
|
|
215
|
+
- lib/fractor/workflow/circuit_breaker_registry.rb
|
|
216
|
+
- lib/fractor/workflow/dead_letter_queue.rb
|
|
217
|
+
- lib/fractor/workflow/execution_hooks.rb
|
|
218
|
+
- lib/fractor/workflow/execution_strategy.rb
|
|
219
|
+
- lib/fractor/workflow/execution_trace.rb
|
|
220
|
+
- lib/fractor/workflow/helpers.rb
|
|
221
|
+
- lib/fractor/workflow/job.rb
|
|
222
|
+
- lib/fractor/workflow/job_dependency_validator.rb
|
|
223
|
+
- lib/fractor/workflow/logger.rb
|
|
224
|
+
- lib/fractor/workflow/pre_execution_context.rb
|
|
225
|
+
- lib/fractor/workflow/retry_config.rb
|
|
226
|
+
- lib/fractor/workflow/retry_orchestrator.rb
|
|
227
|
+
- lib/fractor/workflow/retry_strategy.rb
|
|
228
|
+
- lib/fractor/workflow/structured_logger.rb
|
|
229
|
+
- lib/fractor/workflow/type_compatibility_validator.rb
|
|
230
|
+
- lib/fractor/workflow/visualizer.rb
|
|
231
|
+
- lib/fractor/workflow/workflow_context.rb
|
|
232
|
+
- lib/fractor/workflow/workflow_executor.rb
|
|
233
|
+
- lib/fractor/workflow/workflow_result.rb
|
|
234
|
+
- lib/fractor/workflow/workflow_validator.rb
|
|
62
235
|
- lib/fractor/wrapped_ractor.rb
|
|
236
|
+
- lib/fractor/wrapped_ractor3.rb
|
|
237
|
+
- lib/fractor/wrapped_ractor4.rb
|
|
63
238
|
- sig/fractor.rbs
|
|
64
|
-
- tests/sample.rb.bak
|
|
65
|
-
- tests/sample_working.rb.bak
|
|
66
239
|
homepage: https://github.com/ribose/fractor
|
|
67
240
|
licenses: []
|
|
68
241
|
metadata:
|
data/tests/sample.rb.bak
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Worker
|
|
5
|
-
def process(work)
|
|
6
|
-
raise NotImplementedError, "This #{self.class} cannot respond to:"
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
class MyWorker < Worker
|
|
11
|
-
# This method is called by the Ractor to process the work
|
|
12
|
-
# It should return a WorkResult object
|
|
13
|
-
# If there is an error, it should raise an exception
|
|
14
|
-
# The Ractor will catch the exception and send it back to the main thread
|
|
15
|
-
def process(work)
|
|
16
|
-
puts "Working on '#{work.inspect}'"
|
|
17
|
-
|
|
18
|
-
if work.input == 5
|
|
19
|
-
return WorkResult.new(error: "Error processing work #{work.input}", work: work)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
calculated = work.input * 2
|
|
23
|
-
WorkResult.new(result: calculated, work: work)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class Work
|
|
28
|
-
attr_reader :input
|
|
29
|
-
def initialize(input)
|
|
30
|
-
@input = input
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def to_s
|
|
34
|
-
"Work: #{@input}"
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
class MyWork < Work
|
|
39
|
-
def initialize(input)
|
|
40
|
-
super
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def to_s
|
|
44
|
-
"MyWork: #{@input}"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
class WorkResult
|
|
49
|
-
attr_reader :result, :error, :work
|
|
50
|
-
def initialize(result: nil, error: nil, work: nil)
|
|
51
|
-
@result = result
|
|
52
|
-
@error = error
|
|
53
|
-
@work = work
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def success?
|
|
57
|
-
!@error
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def to_s
|
|
61
|
-
if success?
|
|
62
|
-
"Result: #{@result}"
|
|
63
|
-
else
|
|
64
|
-
"Error: #{@error}, Work: #{@work}"
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class ResultAggregator
|
|
71
|
-
attr_reader :results, :errors
|
|
72
|
-
|
|
73
|
-
# This class is used to aggregate the results and errors from the Ractors
|
|
74
|
-
# It will store the results and errors in separate arrays
|
|
75
|
-
# It will also provide a method to print the results and errors
|
|
76
|
-
def initialize
|
|
77
|
-
@results = []
|
|
78
|
-
@errors = []
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def add_result(result)
|
|
82
|
-
if result.success?
|
|
83
|
-
puts "Work completed successfully: #{result}"
|
|
84
|
-
@results << result
|
|
85
|
-
else
|
|
86
|
-
puts "Error processing work: #{result}"
|
|
87
|
-
@errors << result
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def to_s
|
|
92
|
-
"Results: #{@results.each(&:to_s).join(", ")}, Errors: #{@errors.each(&:to_s).join(", ")}"
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def inspect
|
|
96
|
-
{
|
|
97
|
-
results: @results.map(&:to_s),
|
|
98
|
-
errors: @errors.map(&:to_s)
|
|
99
|
-
}
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
class MyRactor
|
|
104
|
-
def initialize(name)
|
|
105
|
-
puts "Creating Ractor #{name}"
|
|
106
|
-
@name = name
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def start
|
|
110
|
-
puts "Starting Ractor #{@name}"
|
|
111
|
-
@ractor = Ractor.new(@name) do |name|
|
|
112
|
-
puts "Ractor #{name} started"
|
|
113
|
-
Ractor.yield({ type: :initialize, processor: name })
|
|
114
|
-
worker = MyWorker.new
|
|
115
|
-
|
|
116
|
-
loop do
|
|
117
|
-
puts "Waiting for work in #{name}"
|
|
118
|
-
work = Ractor.receive
|
|
119
|
-
puts "Received work #{work} in #{name}"
|
|
120
|
-
begin
|
|
121
|
-
result = worker.process(work)
|
|
122
|
-
puts "Sending result #{result} from Ractor #{name}"
|
|
123
|
-
Ractor.yield({ type: :result, result: result })
|
|
124
|
-
rescue StandardError => e
|
|
125
|
-
puts "Error processing work #{work} in Ractor #{name}: #{e.message}"
|
|
126
|
-
Ractor.yield({ type: :error, error: e.message, processor: name, work: work })
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def ractor
|
|
133
|
-
@ractor
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
class Supervisor
|
|
138
|
-
# Removed failed_queue from attr_reader
|
|
139
|
-
attr_reader :work_queue, :workers, :results
|
|
140
|
-
|
|
141
|
-
def initialize(num_workers = 2)
|
|
142
|
-
@work_queue = Queue.new
|
|
143
|
-
@results = ResultAggregator.new
|
|
144
|
-
# @failed_queue = Queue.new # Removed failed_queue
|
|
145
|
-
@num_workers = num_workers
|
|
146
|
-
@workers = []
|
|
147
|
-
@total_work_count = 0 # Track total items initially added
|
|
148
|
-
# @shutdown_requested = false # Removed shutdown flag
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def add_work(items)
|
|
152
|
-
items.each { |item| @work_queue << item }
|
|
153
|
-
@total_work_count += items.size # Increment initial work count
|
|
154
|
-
puts "Work added. Initial work count: #{@total_work_count}, Queue size: #{@work_queue.size}"
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def start_workers
|
|
158
|
-
@workers = (1..@num_workers).map do |i|
|
|
159
|
-
MyRactor.new("worker #{i}")
|
|
160
|
-
end
|
|
161
|
-
@workers.each(&:start)
|
|
162
|
-
puts "Workers started"
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def setup_signal_handler
|
|
166
|
-
# No need for Ractor.current here anymore
|
|
167
|
-
# Need access to @workers within the trap block
|
|
168
|
-
workers_ref = @workers
|
|
169
|
-
Signal.trap("INT") do
|
|
170
|
-
puts "\nCtrl+C received. Initiating immediate shutdown..."
|
|
171
|
-
# Attempt to close worker Ractors before exiting
|
|
172
|
-
puts "Attempting to close worker Ractors..."
|
|
173
|
-
workers_ref.each do |w|
|
|
174
|
-
begin
|
|
175
|
-
# Check if ractor exists and is not closed
|
|
176
|
-
if w && w.respond_to?(:ractor) && w.ractor && !w.ractor.closed?
|
|
177
|
-
w.ractor.close
|
|
178
|
-
puts "Closed Ractor: #{w.ractor}"
|
|
179
|
-
end
|
|
180
|
-
rescue => e # Catch potential errors during close
|
|
181
|
-
puts "Error closing Ractor #{w.ractor rescue 'unknown'}: #{e.message}"
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
puts "Exiting now."
|
|
185
|
-
exit(1) # Exit immediately
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def run
|
|
190
|
-
setup_signal_handler # Sets up the immediate exit trap
|
|
191
|
-
start_workers
|
|
192
|
-
|
|
193
|
-
# Removed the initial work distribution loop.
|
|
194
|
-
# The main loop will handle sending work upon receiving :initialize message.
|
|
195
|
-
|
|
196
|
-
# Main loop: Process events until the number of results equals the initial work count.
|
|
197
|
-
# The signal trap handles immediate exit.
|
|
198
|
-
while (@results.results.size + @results.errors.size) < @total_work_count
|
|
199
|
-
processed_count = @results.results.size + @results.errors.size
|
|
200
|
-
puts "Waiting for Ractor results. Processed: #{processed_count}/#{@total_work_count}, Queue size: #{@work_queue.size}"
|
|
201
|
-
|
|
202
|
-
# Only select from worker ractors now
|
|
203
|
-
ready_ractors = @workers.map(&:ractor).compact
|
|
204
|
-
# Safety break if all workers somehow finished/closed unexpectedly AND no work left
|
|
205
|
-
# This condition might need refinement depending on exact desired behavior if workers die.
|
|
206
|
-
break if ready_ractors.empty? && @work_queue.empty? && processed_count < @total_work_count
|
|
207
|
-
|
|
208
|
-
# Ractor.select will block until a worker sends a message
|
|
209
|
-
# If ready_ractors is empty but loop continues, select would raise error. Added break above.
|
|
210
|
-
next if ready_ractors.empty? # Skip iteration if no workers available but loop condition met (e.g., waiting for final results)
|
|
211
|
-
|
|
212
|
-
ractor, completed_work = Ractor.select(*ready_ractors)
|
|
213
|
-
|
|
214
|
-
puts "Selected Ractor returned: #{ractor}, completed work: #{completed_work}"
|
|
215
|
-
|
|
216
|
-
# Process the received message
|
|
217
|
-
case completed_work[:type]
|
|
218
|
-
when :initialize
|
|
219
|
-
puts "Initializing Ractor: #{completed_work[:processor]}"
|
|
220
|
-
# Send work if available
|
|
221
|
-
if !@work_queue.empty?
|
|
222
|
-
queued_work = @work_queue.pop # Pop before sending
|
|
223
|
-
puts "Sending initial work #{queued_work} to initialized Ractor: #{ractor}"
|
|
224
|
-
ractor.send(MyWork.new(queued_work))
|
|
225
|
-
puts "Initial work sent to #{completed_work[:processor]}."
|
|
226
|
-
else
|
|
227
|
-
puts "Work queue empty when Ractor #{completed_work[:processor]} initialized."
|
|
228
|
-
end
|
|
229
|
-
when :result
|
|
230
|
-
puts "Completed work: #{completed_work[:result]} in Ractor: #{completed_work[:processor]}"
|
|
231
|
-
@results.add_result(completed_work[:result])
|
|
232
|
-
# No need to decrement a counter here, loop condition checks total results
|
|
233
|
-
puts "Result processed. Total processed: #{@results.results.size + @results.errors.size}/#{@total_work_count}"
|
|
234
|
-
puts "Results: #{@results.inspect}"
|
|
235
|
-
# Call helper to send next work
|
|
236
|
-
send_next_work_if_available(ractor)
|
|
237
|
-
when :error
|
|
238
|
-
error_result = WorkResult.new(error: completed_work[:error], work: completed_work[:work])
|
|
239
|
-
puts "Error processing work #{error_result.work} in Ractor: #{completed_work[:processor]}: #{error_result.error}"
|
|
240
|
-
# Removed adding to failed_queue
|
|
241
|
-
@results.add_result(error_result) # This adds it to the errors array in the aggregator
|
|
242
|
-
# No need to decrement a counter here, loop condition checks total results
|
|
243
|
-
puts "Error handled. Total processed: #{@results.results.size + @results.errors.size}/#{@total_work_count}"
|
|
244
|
-
# Removed Failed Queue size log
|
|
245
|
-
puts "Results (including errors): #{@results.inspect}"
|
|
246
|
-
# Call helper to send next work
|
|
247
|
-
send_next_work_if_available(ractor)
|
|
248
|
-
else
|
|
249
|
-
# Log unknown message types from workers
|
|
250
|
-
puts "Unknown message type received: #{completed_work[:type]} from #{ractor}"
|
|
251
|
-
end
|
|
252
|
-
# Loop continues based on the while condition at the top
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
# Removed DEBUG LOG for failed_queue
|
|
256
|
-
|
|
257
|
-
# This part might not be reached if exit(1) is called in the trap
|
|
258
|
-
puts "Main loop finished."
|
|
259
|
-
puts "Final Results: #{@results.inspect}"
|
|
260
|
-
# Removed Failed Work Queue size log
|
|
261
|
-
# Optionally print failed items
|
|
262
|
-
# until @failed_queue.empty?
|
|
263
|
-
# puts "Failed: #{@failed_queue.pop.inspect}"
|
|
264
|
-
# end
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
private
|
|
268
|
-
|
|
269
|
-
# Helper method to send next work item if available
|
|
270
|
-
def send_next_work_if_available(ractor)
|
|
271
|
-
# Ensure the ractor is valid before attempting to send
|
|
272
|
-
# Ractor.select should only return active ractors, so closed? check is removed.
|
|
273
|
-
unless ractor.nil?
|
|
274
|
-
if !@work_queue.empty?
|
|
275
|
-
queued_work = @work_queue.pop # Pop before sending
|
|
276
|
-
puts "Sending next work #{queued_work} to Ractor: #{ractor}"
|
|
277
|
-
ractor.send(MyWork.new(queued_work))
|
|
278
|
-
puts "Work sent."
|
|
279
|
-
else
|
|
280
|
-
puts "Work queue empty. Not sending new work to Ractor #{ractor}."
|
|
281
|
-
end
|
|
282
|
-
else
|
|
283
|
-
puts "Attempted to send work to an invalid or closed Ractor."
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# --- Main Execution ---
|
|
289
|
-
if __FILE__ == $0
|
|
290
|
-
supervisor = Supervisor.new(2) # Create supervisor with 2 workers
|
|
291
|
-
|
|
292
|
-
# Add work items
|
|
293
|
-
work_items = (1..10).to_a
|
|
294
|
-
supervisor.add_work(work_items)
|
|
295
|
-
|
|
296
|
-
# Run the supervisor
|
|
297
|
-
supervisor.run
|
|
298
|
-
|
|
299
|
-
puts "Processing complete."
|
|
300
|
-
puts "Final Aggregated Results:"
|
|
301
|
-
puts supervisor.results.inspect
|
|
302
|
-
|
|
303
|
-
# Print failed items directly from the ResultAggregator's errors array
|
|
304
|
-
failed_items = supervisor.results.errors # Access the errors array
|
|
305
|
-
puts "\nFailed Work Items (#{failed_items.size}):"
|
|
306
|
-
# Inspect each item individually for better readability if they are objects
|
|
307
|
-
# The items are already WorkResult objects
|
|
308
|
-
puts failed_items.map(&:inspect).inspect
|
|
309
|
-
end
|