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
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Your First Fractor Application
|
|
4
|
+
nav_order: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
== Your First Fractor Application
|
|
8
|
+
:toc:
|
|
9
|
+
:toclevels: 3
|
|
10
|
+
|
|
11
|
+
This tutorial walks you through creating your first parallel processing application with Fractor from scratch. By the end, you'll understand the core components and how they work together.
|
|
12
|
+
|
|
13
|
+
=== What we'll build
|
|
14
|
+
|
|
15
|
+
We'll create a simple image processor that:
|
|
16
|
+
|
|
17
|
+
* Takes a list of image filenames
|
|
18
|
+
* Resizes each image in parallel
|
|
19
|
+
* Collects the results
|
|
20
|
+
* Handles errors gracefully
|
|
21
|
+
|
|
22
|
+
=== Prerequisites
|
|
23
|
+
|
|
24
|
+
* Ruby 3.0 or later installed
|
|
25
|
+
* Fractor gem installed (`gem install fractor`)
|
|
26
|
+
* Basic Ruby knowledge
|
|
27
|
+
|
|
28
|
+
=== Step 1: Project setup
|
|
29
|
+
|
|
30
|
+
Create a new directory for your project:
|
|
31
|
+
|
|
32
|
+
[source,sh]
|
|
33
|
+
----
|
|
34
|
+
mkdir fractor-tutorial
|
|
35
|
+
cd fractor-tutorial
|
|
36
|
+
----
|
|
37
|
+
|
|
38
|
+
Create a new file called `image_processor.rb`:
|
|
39
|
+
|
|
40
|
+
[source,sh]
|
|
41
|
+
----
|
|
42
|
+
touch image_processor.rb
|
|
43
|
+
----
|
|
44
|
+
|
|
45
|
+
=== Step 2: Define your Work class
|
|
46
|
+
|
|
47
|
+
The `Work` class represents a single unit of work. For our image processor, each work item contains information about one image to process.
|
|
48
|
+
|
|
49
|
+
Open `image_processor.rb` and add:
|
|
50
|
+
|
|
51
|
+
[source,ruby]
|
|
52
|
+
----
|
|
53
|
+
require 'fractor'
|
|
54
|
+
|
|
55
|
+
# Represents a single image to be processed
|
|
56
|
+
class ImageWork < Fractor::Work
|
|
57
|
+
def initialize(filename, width, height)
|
|
58
|
+
super({
|
|
59
|
+
filename: filename,
|
|
60
|
+
width: width,
|
|
61
|
+
height: height
|
|
62
|
+
})
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Convenience methods to access work data
|
|
66
|
+
def filename
|
|
67
|
+
input[:filename]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def width
|
|
71
|
+
input[:width]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def height
|
|
75
|
+
input[:height]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_s
|
|
79
|
+
"ImageWork(#{filename}, #{width}x#{height})"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
----
|
|
83
|
+
|
|
84
|
+
**Key points:**
|
|
85
|
+
|
|
86
|
+
* Inherit from `Fractor::Work`
|
|
87
|
+
* Call `super()` with a hash of input data
|
|
88
|
+
* Add convenience methods for accessing data
|
|
89
|
+
* The `input` hash is accessible across Ractor boundaries
|
|
90
|
+
|
|
91
|
+
=== Step 3: Define your Worker class
|
|
92
|
+
|
|
93
|
+
The `Worker` class contains the processing logic. Each worker runs in its own Ractor for true parallel execution.
|
|
94
|
+
|
|
95
|
+
Add this to `image_processor.rb`:
|
|
96
|
+
|
|
97
|
+
[source,ruby]
|
|
98
|
+
----
|
|
99
|
+
# Processes image resize operations
|
|
100
|
+
class ImageWorker < Fractor::Worker
|
|
101
|
+
def process(work)
|
|
102
|
+
puts "Processing #{work.filename}..."
|
|
103
|
+
|
|
104
|
+
# Simulate image processing
|
|
105
|
+
# In real code, you'd use ImageMagick, mini_magick, etc.
|
|
106
|
+
sleep(rand(0.1..0.5)) # Simulate work
|
|
107
|
+
|
|
108
|
+
result_filename = "resized_#{work.filename}"
|
|
109
|
+
|
|
110
|
+
# Return successful result
|
|
111
|
+
Fractor::WorkResult.new(
|
|
112
|
+
result: {
|
|
113
|
+
original: work.filename,
|
|
114
|
+
resized: result_filename,
|
|
115
|
+
dimensions: "#{work.width}x#{work.height}"
|
|
116
|
+
},
|
|
117
|
+
work: work
|
|
118
|
+
)
|
|
119
|
+
rescue => e
|
|
120
|
+
# Return error result
|
|
121
|
+
Fractor::WorkResult.new(
|
|
122
|
+
error: e,
|
|
123
|
+
work: work,
|
|
124
|
+
error_code: :image_processing_failed,
|
|
125
|
+
error_context: {
|
|
126
|
+
filename: work.filename,
|
|
127
|
+
dimensions: "#{work.width}x#{work.height}"
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
----
|
|
133
|
+
|
|
134
|
+
**Key points:**
|
|
135
|
+
|
|
136
|
+
* Inherit from `Fractor::Worker`
|
|
137
|
+
* Implement the `process(work)` method
|
|
138
|
+
* Always return a `WorkResult` (success or error)
|
|
139
|
+
* Use `rescue` to handle errors gracefully
|
|
140
|
+
* Add error context for debugging
|
|
141
|
+
|
|
142
|
+
=== Step 4: Create the Supervisor
|
|
143
|
+
|
|
144
|
+
The Supervisor manages the worker pool and coordinates processing:
|
|
145
|
+
|
|
146
|
+
[source,ruby]
|
|
147
|
+
----
|
|
148
|
+
# Create sample work items
|
|
149
|
+
work_items = [
|
|
150
|
+
ImageWork.new("photo1.jpg", 800, 600),
|
|
151
|
+
ImageWork.new("photo2.jpg", 1024, 768),
|
|
152
|
+
ImageWork.new("photo3.jpg", 1920, 1080),
|
|
153
|
+
ImageWork.new("photo4.jpg", 640, 480),
|
|
154
|
+
ImageWork.new("photo5.jpg", 1280, 720)
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
# Create supervisor with ImageWorker pool
|
|
158
|
+
supervisor = Fractor::Supervisor.new(
|
|
159
|
+
worker_pools: [
|
|
160
|
+
{
|
|
161
|
+
worker_class: ImageWorker
|
|
162
|
+
# num_workers is auto-detected based on CPU cores
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
puts "Starting image processor with #{supervisor.worker_pools.first[:num_workers]} workers"
|
|
168
|
+
|
|
169
|
+
# Add work items to process
|
|
170
|
+
supervisor.add_work_items(work_items)
|
|
171
|
+
|
|
172
|
+
# Run processing (blocks until complete)
|
|
173
|
+
supervisor.run
|
|
174
|
+
|
|
175
|
+
puts "\nProcessing complete!"
|
|
176
|
+
----
|
|
177
|
+
|
|
178
|
+
=== Step 5: Access results
|
|
179
|
+
|
|
180
|
+
After processing completes, access the results:
|
|
181
|
+
|
|
182
|
+
[source,ruby]
|
|
183
|
+
----
|
|
184
|
+
# Get successful results
|
|
185
|
+
results = supervisor.results.results
|
|
186
|
+
puts "\nSuccessfully processed #{results.size} images:"
|
|
187
|
+
results.each do |work_result|
|
|
188
|
+
data = work_result.result
|
|
189
|
+
puts " #{data[:original]} -> #{data[:resized]} (#{data[:dimensions]})"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Get errors
|
|
193
|
+
errors = supervisor.results.errors
|
|
194
|
+
if errors.any?
|
|
195
|
+
puts "\nErrors encountered: #{errors.size}"
|
|
196
|
+
errors.each do |error_result|
|
|
197
|
+
puts " #{error_result.work.filename}: #{error_result.error_info[:error_message]}"
|
|
198
|
+
end
|
|
199
|
+
else
|
|
200
|
+
puts "\nNo errors!"
|
|
201
|
+
end
|
|
202
|
+
----
|
|
203
|
+
|
|
204
|
+
=== Step 6: Run your application
|
|
205
|
+
|
|
206
|
+
Save the file and run it:
|
|
207
|
+
|
|
208
|
+
[source,sh]
|
|
209
|
+
----
|
|
210
|
+
ruby image_processor.rb
|
|
211
|
+
----
|
|
212
|
+
|
|
213
|
+
You should see output like:
|
|
214
|
+
|
|
215
|
+
[source]
|
|
216
|
+
----
|
|
217
|
+
Starting image processor with 8 workers
|
|
218
|
+
Processing photo1.jpg...
|
|
219
|
+
Processing photo2.jpg...
|
|
220
|
+
Processing photo3.jpg...
|
|
221
|
+
Processing photo4.jpg...
|
|
222
|
+
Processing photo5.jpg...
|
|
223
|
+
|
|
224
|
+
Processing complete!
|
|
225
|
+
|
|
226
|
+
Successfully processed 5 images:
|
|
227
|
+
photo1.jpg -> resized_photo1.jpg (800x600)
|
|
228
|
+
photo2.jpg -> resized_photo2.jpg (1024x768)
|
|
229
|
+
photo3.jpg -> resized_photo3.jpg (1920x1080)
|
|
230
|
+
photo4.jpg -> resized_photo4.jpg (640x480)
|
|
231
|
+
photo5.jpg -> resized_photo5.jpg (1280x720)
|
|
232
|
+
|
|
233
|
+
No errors!
|
|
234
|
+
----
|
|
235
|
+
|
|
236
|
+
=== Complete code
|
|
237
|
+
|
|
238
|
+
Here's the complete `image_processor.rb`:
|
|
239
|
+
|
|
240
|
+
[source,ruby]
|
|
241
|
+
----
|
|
242
|
+
require 'fractor'
|
|
243
|
+
|
|
244
|
+
class ImageWork < Fractor::Work
|
|
245
|
+
def initialize(filename, width, height)
|
|
246
|
+
super({
|
|
247
|
+
filename: filename,
|
|
248
|
+
width: width,
|
|
249
|
+
height: height
|
|
250
|
+
})
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def filename
|
|
254
|
+
input[:filename]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def width
|
|
258
|
+
input[:width]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def height
|
|
262
|
+
input[:height]
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def to_s
|
|
266
|
+
"ImageWork(#{filename}, #{width}x#{height})"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
class ImageWorker < Fractor::Worker
|
|
271
|
+
def process(work)
|
|
272
|
+
puts "Processing #{work.filename}..."
|
|
273
|
+
|
|
274
|
+
sleep(rand(0.1..0.5))
|
|
275
|
+
|
|
276
|
+
result_filename = "resized_#{work.filename}"
|
|
277
|
+
|
|
278
|
+
Fractor::WorkResult.new(
|
|
279
|
+
result: {
|
|
280
|
+
original: work.filename,
|
|
281
|
+
resized: result_filename,
|
|
282
|
+
dimensions: "#{work.width}x#{work.height}"
|
|
283
|
+
},
|
|
284
|
+
work: work
|
|
285
|
+
)
|
|
286
|
+
rescue => e
|
|
287
|
+
Fractor::WorkResult.new(
|
|
288
|
+
error: e,
|
|
289
|
+
work: work,
|
|
290
|
+
error_code: :image_processing_failed,
|
|
291
|
+
error_context: {
|
|
292
|
+
filename: work.filename,
|
|
293
|
+
dimensions: "#{work.width}x#{work.height}"
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Create work items
|
|
300
|
+
work_items = [
|
|
301
|
+
ImageWork.new("photo1.jpg", 800, 600),
|
|
302
|
+
ImageWork.new("photo2.jpg", 1024, 768),
|
|
303
|
+
ImageWork.new("photo3.jpg", 1920, 1080),
|
|
304
|
+
ImageWork.new("photo4.jpg", 640, 480),
|
|
305
|
+
ImageWork.new("photo5.jpg", 1280, 720)
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
# Create supervisor
|
|
309
|
+
supervisor = Fractor::Supervisor.new(
|
|
310
|
+
worker_pools: [{ worker_class: ImageWorker }]
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
puts "Starting image processor with #{supervisor.worker_pools.first[:num_workers]} workers"
|
|
314
|
+
|
|
315
|
+
# Process
|
|
316
|
+
supervisor.add_work_items(work_items)
|
|
317
|
+
supervisor.run
|
|
318
|
+
|
|
319
|
+
puts "\nProcessing complete!"
|
|
320
|
+
|
|
321
|
+
# Results
|
|
322
|
+
results = supervisor.results.results
|
|
323
|
+
puts "\nSuccessfully processed #{results.size} images:"
|
|
324
|
+
results.each do |work_result|
|
|
325
|
+
data = work_result.result
|
|
326
|
+
puts " #{data[:original]} -> #{data[:resized]} (#{data[:dimensions]})"
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
errors = supervisor.results.errors
|
|
330
|
+
if errors.any?
|
|
331
|
+
puts "\nErrors encountered: #{errors.size}"
|
|
332
|
+
errors.each do |error_result|
|
|
333
|
+
puts " #{error_result.work.filename}: #{error_result.error_info[:error_message]}"
|
|
334
|
+
end
|
|
335
|
+
else
|
|
336
|
+
puts "\nNo errors!"
|
|
337
|
+
end
|
|
338
|
+
----
|
|
339
|
+
|
|
340
|
+
=== What you learned
|
|
341
|
+
|
|
342
|
+
Congratulations! You've built your first Fractor application. You now understand:
|
|
343
|
+
|
|
344
|
+
* How to create `Work` classes to encapsulate input data
|
|
345
|
+
* How to create `Worker` classes with processing logic
|
|
346
|
+
* How to use the `Supervisor` to manage parallel execution
|
|
347
|
+
* How to handle both successful results and errors
|
|
348
|
+
* How Fractor automatically detects optimal worker count
|
|
349
|
+
|
|
350
|
+
=== Next steps
|
|
351
|
+
|
|
352
|
+
Now that you understand the basics:
|
|
353
|
+
|
|
354
|
+
* Learn about link:../getting-started/[different processing modes] (Pipeline vs Continuous)
|
|
355
|
+
* Explore link:../../guides/core-concepts/[core concepts] in depth
|
|
356
|
+
* Try link:../../guides/workflows/[workflows] for complex processing graphs
|
|
357
|
+
* See link:../../reference/examples/[real-world examples]
|
|
358
|
+
|
|
359
|
+
=== Troubleshooting
|
|
360
|
+
|
|
361
|
+
==== "Cannot load Ractor"
|
|
362
|
+
|
|
363
|
+
Ensure you're using Ruby 3.0 or later:
|
|
364
|
+
|
|
365
|
+
[source,sh]
|
|
366
|
+
----
|
|
367
|
+
ruby --version
|
|
368
|
+
----
|
|
369
|
+
|
|
370
|
+
==== Workers not running in parallel
|
|
371
|
+
|
|
372
|
+
Check your CPU core count. Fractor creates one worker per core by default:
|
|
373
|
+
|
|
374
|
+
[source,ruby]
|
|
375
|
+
----
|
|
376
|
+
require 'etc'
|
|
377
|
+
puts "Available processors: #{Etc.nprocessors}"
|
|
378
|
+
----
|
|
379
|
+
|
|
380
|
+
==== Need more help?
|
|
381
|
+
|
|
382
|
+
* Check the link:../../guides/core-concepts/[Core Concepts guide]
|
|
383
|
+
* Browse link:../../reference/api/[API Reference]
|
|
384
|
+
* Open an issue on https://github.com/metanorma/fractor/issues[GitHub]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Overview
|
|
4
|
+
nav_order: 1
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
== Overview
|
|
8
|
+
|
|
9
|
+
Step-by-step guides to help you get started with Fractor.
|
|
10
|
+
|
|
11
|
+
These tutorials are designed to be followed in order:
|
|
12
|
+
|
|
13
|
+
. *link:../installation/[Installation]* - Set up Fractor in your environment
|
|
14
|
+
. *link:first-application/[Your First Application]* - Build a simple parallel processor
|
|
15
|
+
. *link:getting-started/[Getting Started]* - Understand modes and patterns
|
|
16
|
+
|
|
17
|
+
=== What You'll Learn
|
|
18
|
+
|
|
19
|
+
By completing these tutorials, you'll:
|
|
20
|
+
|
|
21
|
+
* Install and verify Fractor on your system
|
|
22
|
+
* Understand the core components (Work, Worker, Supervisor)
|
|
23
|
+
* Create your first parallel processing application
|
|
24
|
+
* Learn when to use Pipeline vs Continuous mode
|
|
25
|
+
* Handle errors gracefully
|
|
26
|
+
* Access and use results
|
|
27
|
+
|
|
28
|
+
=== Prerequisites
|
|
29
|
+
|
|
30
|
+
* Ruby 3.0 or later
|
|
31
|
+
* Basic Ruby programming knowledge
|
|
32
|
+
* Understanding of parallel processing concepts (helpful but not required)
|
|
33
|
+
|
|
34
|
+
=== Time Investment
|
|
35
|
+
|
|
36
|
+
* *Installation*: 5-10 minutes
|
|
37
|
+
* *Your First Application*: 20-30 minutes
|
|
38
|
+
* *Getting Started*: 15-20 minutes
|
|
39
|
+
|
|
40
|
+
*Total*: About 1 hour to complete all tutorials
|
|
41
|
+
|
|
42
|
+
=== Next Steps
|
|
43
|
+
|
|
44
|
+
After completing the tutorials, continue to:
|
|
45
|
+
|
|
46
|
+
* link:../guides/[Guides] for in-depth feature documentation
|
|
47
|
+
* link:../reference/[Reference] for API details and patterns
|
|
48
|
+
* link:../reference/examples/[Examples] for real-world use cases
|