fractor 0.1.3 → 0.1.6
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-https---raw-githubusercontent-com-riboseinc-oss-guides-main-ci-rubocop-yml +552 -0
- data/.rubocop.yml +14 -8
- data/.rubocop_todo.yml +154 -48
- data/README.adoc +1371 -317
- data/examples/auto_detection/README.adoc +52 -0
- data/examples/auto_detection/auto_detection.rb +170 -0
- data/examples/continuous_chat_common/message_protocol.rb +53 -0
- data/examples/continuous_chat_fractor/README.adoc +217 -0
- data/examples/continuous_chat_fractor/chat_client.rb +303 -0
- data/examples/continuous_chat_fractor/chat_common.rb +83 -0
- data/examples/continuous_chat_fractor/chat_server.rb +167 -0
- data/examples/continuous_chat_fractor/simulate.rb +345 -0
- data/examples/continuous_chat_server/README.adoc +135 -0
- data/examples/continuous_chat_server/chat_client.rb +303 -0
- data/examples/continuous_chat_server/chat_server.rb +359 -0
- data/examples/continuous_chat_server/simulate.rb +343 -0
- data/examples/hierarchical_hasher/hierarchical_hasher.rb +12 -8
- data/examples/multi_work_type/multi_work_type.rb +30 -29
- data/examples/pipeline_processing/pipeline_processing.rb +15 -15
- data/examples/producer_subscriber/producer_subscriber.rb +20 -16
- data/examples/scatter_gather/scatter_gather.rb +29 -28
- data/examples/simple/sample.rb +38 -6
- data/examples/specialized_workers/specialized_workers.rb +44 -37
- data/lib/fractor/continuous_server.rb +188 -0
- data/lib/fractor/result_aggregator.rb +1 -1
- data/lib/fractor/supervisor.rb +291 -108
- data/lib/fractor/version.rb +1 -1
- data/lib/fractor/work_queue.rb +68 -0
- data/lib/fractor/work_result.rb +1 -1
- data/lib/fractor/worker.rb +2 -1
- data/lib/fractor/wrapped_ractor.rb +12 -2
- data/lib/fractor.rb +2 -0
- metadata +17 -2
|
@@ -8,7 +8,7 @@ module ProducerSubscriber
|
|
|
8
8
|
def initialize(data, depth = 0)
|
|
9
9
|
super({
|
|
10
10
|
data: data,
|
|
11
|
-
depth: depth
|
|
11
|
+
depth: depth,
|
|
12
12
|
})
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -31,7 +31,7 @@ module ProducerSubscriber
|
|
|
31
31
|
super({
|
|
32
32
|
data: data,
|
|
33
33
|
parent_id: parent_id,
|
|
34
|
-
depth: depth
|
|
34
|
+
depth: depth,
|
|
35
35
|
})
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -63,7 +63,7 @@ module ProducerSubscriber
|
|
|
63
63
|
else
|
|
64
64
|
Fractor::WorkResult.new(
|
|
65
65
|
error: "Unknown work type: #{work.class}",
|
|
66
|
-
work: work
|
|
66
|
+
work: work,
|
|
67
67
|
)
|
|
68
68
|
end
|
|
69
69
|
end
|
|
@@ -80,13 +80,13 @@ module ProducerSubscriber
|
|
|
80
80
|
# Return the result with metadata about sub-works
|
|
81
81
|
result = {
|
|
82
82
|
processed_data: processed_data,
|
|
83
|
-
sub_works: [] # Will be populated by the supervisor
|
|
83
|
+
sub_works: [], # Will be populated by the supervisor
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
# Return a successful result
|
|
87
87
|
Fractor::WorkResult.new(
|
|
88
88
|
result: result,
|
|
89
|
-
work: work
|
|
89
|
+
work: work,
|
|
90
90
|
)
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -101,9 +101,9 @@ module ProducerSubscriber
|
|
|
101
101
|
Fractor::WorkResult.new(
|
|
102
102
|
result: {
|
|
103
103
|
processed_data: processed_data,
|
|
104
|
-
parent_id: work.parent_id
|
|
104
|
+
parent_id: work.parent_id,
|
|
105
105
|
},
|
|
106
|
-
work: work
|
|
106
|
+
work: work,
|
|
107
107
|
)
|
|
108
108
|
end
|
|
109
109
|
end
|
|
@@ -122,8 +122,8 @@ module ProducerSubscriber
|
|
|
122
122
|
# Create the supervisor
|
|
123
123
|
supervisor = Fractor::Supervisor.new(
|
|
124
124
|
worker_pools: [
|
|
125
|
-
{ worker_class: MultiWorker, num_workers: @worker_count }
|
|
126
|
-
]
|
|
125
|
+
{ worker_class: MultiWorker, num_workers: @worker_count },
|
|
126
|
+
],
|
|
127
127
|
)
|
|
128
128
|
|
|
129
129
|
# Create and add initial work items
|
|
@@ -144,12 +144,14 @@ module ProducerSubscriber
|
|
|
144
144
|
# Create a new supervisor for sub-works
|
|
145
145
|
sub_supervisor = Fractor::Supervisor.new(
|
|
146
146
|
worker_pools: [
|
|
147
|
-
{ worker_class: MultiWorker, num_workers: @worker_count }
|
|
148
|
-
]
|
|
147
|
+
{ worker_class: MultiWorker, num_workers: @worker_count },
|
|
148
|
+
],
|
|
149
149
|
)
|
|
150
150
|
|
|
151
151
|
# Create and add the sub-work items
|
|
152
|
-
sub_work_items = sub_works.map
|
|
152
|
+
sub_work_items = sub_works.map do |sw|
|
|
153
|
+
SubWork.new(sw[:data], sw[:parent_id], sw[:depth])
|
|
154
|
+
end
|
|
153
155
|
sub_supervisor.add_work_items(sub_work_items)
|
|
154
156
|
sub_supervisor.run
|
|
155
157
|
|
|
@@ -179,12 +181,14 @@ module ProducerSubscriber
|
|
|
179
181
|
sub_works << {
|
|
180
182
|
data: sub_data,
|
|
181
183
|
parent_id: work.object_id,
|
|
182
|
-
depth: work.depth + 1
|
|
184
|
+
depth: work.depth + 1,
|
|
183
185
|
}
|
|
184
186
|
end
|
|
185
187
|
|
|
186
188
|
# Store the sub-work IDs in the result for reference
|
|
187
|
-
result.result[:sub_works] = sub_works.last(3).map
|
|
189
|
+
result.result[:sub_works] = sub_works.last(3).map do |sw|
|
|
190
|
+
sw[:parent_id]
|
|
191
|
+
end
|
|
188
192
|
end
|
|
189
193
|
|
|
190
194
|
sub_works
|
|
@@ -195,7 +199,7 @@ module ProducerSubscriber
|
|
|
195
199
|
initial_results.results.each do |result|
|
|
196
200
|
@result_tree[result.work.object_id] = {
|
|
197
201
|
data: result.result[:processed_data],
|
|
198
|
-
children: []
|
|
202
|
+
children: [],
|
|
199
203
|
}
|
|
200
204
|
end
|
|
201
205
|
|
|
@@ -236,7 +240,7 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
236
240
|
documents = [
|
|
237
241
|
"Annual Report 2025",
|
|
238
242
|
"Technical Documentation",
|
|
239
|
-
"Research Paper"
|
|
243
|
+
"Research Paper",
|
|
240
244
|
]
|
|
241
245
|
|
|
242
246
|
worker_count = 4
|
|
@@ -42,7 +42,7 @@ module ScatterGather
|
|
|
42
42
|
error = ArgumentError.new("Unknown source: #{work.source}")
|
|
43
43
|
return Fractor::WorkResult.new(
|
|
44
44
|
error: error,
|
|
45
|
-
work: work
|
|
45
|
+
work: work,
|
|
46
46
|
)
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -53,9 +53,9 @@ module ScatterGather
|
|
|
53
53
|
query: work.query,
|
|
54
54
|
hits: result[:hits],
|
|
55
55
|
metadata: result[:metadata],
|
|
56
|
-
timing: result[:timing]
|
|
56
|
+
timing: result[:timing],
|
|
57
57
|
},
|
|
58
|
-
work: work
|
|
58
|
+
work: work,
|
|
59
59
|
)
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -72,12 +72,12 @@ module ScatterGather
|
|
|
72
72
|
|
|
73
73
|
# Generate simulated records
|
|
74
74
|
record_count = rand(3..10)
|
|
75
|
-
hits = record_count
|
|
75
|
+
hits = Array.new(record_count) do |i|
|
|
76
76
|
{
|
|
77
77
|
id: "db-#{i + 1}",
|
|
78
78
|
title: "Database Result #{i + 1} for '#{work.query}'",
|
|
79
79
|
content: "This is database content for #{work.query}",
|
|
80
|
-
relevance: rand(0.1..1.0).round(2)
|
|
80
|
+
relevance: rand(0.1..1.0).round(2),
|
|
81
81
|
}
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -86,9 +86,9 @@ module ScatterGather
|
|
|
86
86
|
metadata: {
|
|
87
87
|
source_type: "PostgreSQL Database",
|
|
88
88
|
total_available: record_count + rand(10..50),
|
|
89
|
-
query_type: "Full-text search"
|
|
89
|
+
query_type: "Full-text search",
|
|
90
90
|
},
|
|
91
|
-
timing: rand(0.01..0.3).round(3)
|
|
91
|
+
timing: rand(0.01..0.3).round(3),
|
|
92
92
|
}
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -98,12 +98,12 @@ module ScatterGather
|
|
|
98
98
|
|
|
99
99
|
# Generate simulated API results
|
|
100
100
|
record_count = rand(2..8)
|
|
101
|
-
hits = record_count
|
|
101
|
+
hits = Array.new(record_count) do |i|
|
|
102
102
|
{
|
|
103
103
|
id: "api-#{i + 1}",
|
|
104
104
|
title: "API Result #{i + 1} for '#{work.query}'",
|
|
105
105
|
content: "This is API content for #{work.query}",
|
|
106
|
-
relevance: rand(0.1..1.0).round(2)
|
|
106
|
+
relevance: rand(0.1..1.0).round(2),
|
|
107
107
|
}
|
|
108
108
|
end
|
|
109
109
|
|
|
@@ -112,9 +112,9 @@ module ScatterGather
|
|
|
112
112
|
metadata: {
|
|
113
113
|
source_type: "External REST API",
|
|
114
114
|
provider: %w[Google Bing DuckDuckGo].sample,
|
|
115
|
-
response_code: 200
|
|
115
|
+
response_code: 200,
|
|
116
116
|
},
|
|
117
|
-
timing: rand(0.1..0.5).round(3)
|
|
117
|
+
timing: rand(0.1..0.5).round(3),
|
|
118
118
|
}
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -128,12 +128,12 @@ module ScatterGather
|
|
|
128
128
|
if cache_hit
|
|
129
129
|
# Cache hit - return cached results
|
|
130
130
|
record_count = rand(1..5)
|
|
131
|
-
hits = record_count
|
|
131
|
+
hits = Array.new(record_count) do |i|
|
|
132
132
|
{
|
|
133
133
|
id: "cache-#{i + 1}",
|
|
134
134
|
title: "Cached Result #{i + 1} for '#{work.query}'",
|
|
135
135
|
content: "This is cached content for #{work.query}",
|
|
136
|
-
relevance: rand(0.1..1.0).round(2)
|
|
136
|
+
relevance: rand(0.1..1.0).round(2),
|
|
137
137
|
}
|
|
138
138
|
end
|
|
139
139
|
|
|
@@ -142,9 +142,9 @@ module ScatterGather
|
|
|
142
142
|
metadata: {
|
|
143
143
|
source_type: "In-memory Cache",
|
|
144
144
|
cache_hit: true,
|
|
145
|
-
age: rand(1..3600)
|
|
145
|
+
age: rand(1..3600),
|
|
146
146
|
},
|
|
147
|
-
timing: rand(0.001..0.05).round(3)
|
|
147
|
+
timing: rand(0.001..0.05).round(3),
|
|
148
148
|
}
|
|
149
149
|
else
|
|
150
150
|
# Cache miss
|
|
@@ -152,9 +152,9 @@ module ScatterGather
|
|
|
152
152
|
hits: [],
|
|
153
153
|
metadata: {
|
|
154
154
|
source_type: "In-memory Cache",
|
|
155
|
-
cache_hit: false
|
|
155
|
+
cache_hit: false,
|
|
156
156
|
},
|
|
157
|
-
timing: rand(0.001..0.01).round(3)
|
|
157
|
+
timing: rand(0.001..0.01).round(3),
|
|
158
158
|
}
|
|
159
159
|
end
|
|
160
160
|
end
|
|
@@ -165,13 +165,13 @@ module ScatterGather
|
|
|
165
165
|
|
|
166
166
|
# Generate simulated file results
|
|
167
167
|
record_count = rand(1..12)
|
|
168
|
-
hits = record_count
|
|
168
|
+
hits = Array.new(record_count) do |i|
|
|
169
169
|
{
|
|
170
170
|
id: "file-#{i + 1}",
|
|
171
171
|
title: "File Result #{i + 1} for '#{work.query}'",
|
|
172
172
|
path: "/path/to/file_#{i + 1}.txt",
|
|
173
173
|
content: "This is file content matching #{work.query}",
|
|
174
|
-
relevance: rand(0.1..1.0).round(2)
|
|
174
|
+
relevance: rand(0.1..1.0).round(2),
|
|
175
175
|
}
|
|
176
176
|
end
|
|
177
177
|
|
|
@@ -180,9 +180,9 @@ module ScatterGather
|
|
|
180
180
|
metadata: {
|
|
181
181
|
source_type: "File System",
|
|
182
182
|
directories_searched: rand(5..20),
|
|
183
|
-
files_scanned: rand(50..500)
|
|
183
|
+
files_scanned: rand(50..500),
|
|
184
184
|
},
|
|
185
|
-
timing: rand(0.01..0.2).round(3)
|
|
185
|
+
timing: rand(0.01..0.2).round(3),
|
|
186
186
|
}
|
|
187
187
|
end
|
|
188
188
|
end
|
|
@@ -194,8 +194,8 @@ module ScatterGather
|
|
|
194
194
|
def initialize(worker_count = 4)
|
|
195
195
|
@supervisor = Fractor::Supervisor.new(
|
|
196
196
|
worker_pools: [
|
|
197
|
-
{ worker_class: SearchWorker, num_workers: worker_count }
|
|
198
|
-
]
|
|
197
|
+
{ worker_class: SearchWorker, num_workers: worker_count },
|
|
198
|
+
],
|
|
199
199
|
)
|
|
200
200
|
|
|
201
201
|
@merged_results = nil
|
|
@@ -204,10 +204,11 @@ module ScatterGather
|
|
|
204
204
|
def search(query, sources = nil)
|
|
205
205
|
# Define search sources with their parameters
|
|
206
206
|
sources ||= [
|
|
207
|
-
{ source: :database,
|
|
207
|
+
{ source: :database,
|
|
208
|
+
params: { max_results: 50, include_archived: false } },
|
|
208
209
|
{ source: :api, params: { format: "json", timeout: 5 } },
|
|
209
210
|
{ source: :cache, params: { max_age: 3600 } },
|
|
210
|
-
{ source: :filesystem, params: { extensions: %w[txt md pdf] } }
|
|
211
|
+
{ source: :filesystem, params: { extensions: %w[txt md pdf] } },
|
|
211
212
|
]
|
|
212
213
|
|
|
213
214
|
start_time = Time.now
|
|
@@ -262,7 +263,7 @@ module ScatterGather
|
|
|
262
263
|
content: hit[:content],
|
|
263
264
|
source: source,
|
|
264
265
|
original_relevance: hit[:relevance],
|
|
265
|
-
weighted_relevance: hit[:relevance] * source_weight
|
|
266
|
+
weighted_relevance: hit[:relevance] * source_weight,
|
|
266
267
|
}
|
|
267
268
|
end
|
|
268
269
|
end
|
|
@@ -277,7 +278,7 @@ module ScatterGather
|
|
|
277
278
|
execution_time: total_time,
|
|
278
279
|
sources: results_by_source.keys,
|
|
279
280
|
ranked_results: ranked_hits,
|
|
280
|
-
source_details: results_by_source
|
|
281
|
+
source_details: results_by_source,
|
|
281
282
|
}
|
|
282
283
|
end
|
|
283
284
|
end
|
|
@@ -309,7 +310,7 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
309
310
|
puts "Query: #{results[:query]}"
|
|
310
311
|
puts "Total hits: #{results[:total_hits]}"
|
|
311
312
|
puts "Total execution time: #{results[:execution_time].round(3)} seconds"
|
|
312
|
-
puts "Sources searched: #{results[:sources].join(
|
|
313
|
+
puts "Sources searched: #{results[:sources].join(', ')}"
|
|
313
314
|
puts
|
|
314
315
|
|
|
315
316
|
puts "Top 5 Results (by relevance):"
|
data/examples/simple/sample.rb
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Simple Example - Getting Started with Fractor
|
|
6
|
+
# =============================================================================
|
|
7
|
+
#
|
|
8
|
+
# This example demonstrates the basic usage of the Fractor framework.
|
|
9
|
+
#
|
|
10
|
+
# WHAT THIS DEMONSTRATES:
|
|
11
|
+
# - How to create a Work class (MyWork) to encapsulate work items
|
|
12
|
+
# - How to create a Worker class (MyWorker) to process work
|
|
13
|
+
# - How to set up a Supervisor to manage parallel processing
|
|
14
|
+
# - Basic error handling in workers
|
|
15
|
+
# - How to access and display results after processing
|
|
16
|
+
# - Auto-detection of available processors (num_workers not specified)
|
|
17
|
+
#
|
|
18
|
+
# KEY CONCEPTS:
|
|
19
|
+
# 1. Work Class: Inherits from Fractor::Work, stores input data
|
|
20
|
+
# 2. Worker Class: Inherits from Fractor::Worker, implements process() method
|
|
21
|
+
# 3. Supervisor: Manages worker Ractors and distributes work
|
|
22
|
+
# 4. WorkResult: Contains either successful results or errors
|
|
23
|
+
#
|
|
24
|
+
# HOW TO RUN:
|
|
25
|
+
# ruby examples/simple/sample.rb
|
|
26
|
+
#
|
|
27
|
+
# WHAT TO EXPECT:
|
|
28
|
+
# - Creates work items with values 1-10
|
|
29
|
+
# - Processes them in parallel using auto-detected number of workers
|
|
30
|
+
# - Value 5 intentionally produces an error for demonstration
|
|
31
|
+
# - Displays successful results and error information
|
|
32
|
+
#
|
|
33
|
+
# =============================================================================
|
|
34
|
+
|
|
35
|
+
require_relative '../../lib/fractor'
|
|
5
36
|
|
|
6
37
|
# Client-specific work item implementation inheriting from Fractor::Work
|
|
7
38
|
class MyWork < Fractor::Work
|
|
@@ -41,14 +72,14 @@ class MyWorker < Fractor::Worker
|
|
|
41
72
|
# It should return a Fractor::WorkResult object
|
|
42
73
|
def process(work)
|
|
43
74
|
# Only print debug information if FRACTOR_DEBUG is enabled
|
|
44
|
-
puts "Working on '#{work.inspect}'" if ENV[
|
|
75
|
+
puts "Working on '#{work.inspect}'" if ENV['FRACTOR_DEBUG']
|
|
45
76
|
|
|
46
77
|
# Check work type and handle accordingly
|
|
47
78
|
if work.is_a?(MyWork)
|
|
48
79
|
if work.value == 5
|
|
49
80
|
# Return a Fractor::WorkResult for errors
|
|
50
81
|
# Store the error object, not just the string
|
|
51
|
-
error = StandardError.new(
|
|
82
|
+
error = StandardError.new('Cannot process value 5')
|
|
52
83
|
return Fractor::WorkResult.new(error: error, work: work)
|
|
53
84
|
end
|
|
54
85
|
|
|
@@ -71,9 +102,10 @@ end
|
|
|
71
102
|
# MyWorker and MyWork classes.
|
|
72
103
|
if __FILE__ == $PROGRAM_NAME
|
|
73
104
|
# Create supervisor, passing the client-specific worker class in a worker pool
|
|
105
|
+
# Note: num_workers is not specified, so it will auto-detect the number of available processors
|
|
74
106
|
supervisor = Fractor::Supervisor.new(
|
|
75
107
|
worker_pools: [
|
|
76
|
-
{ worker_class: MyWorker
|
|
108
|
+
{ worker_class: MyWorker } # Worker class without explicit num_workers uses auto-detection
|
|
77
109
|
]
|
|
78
110
|
)
|
|
79
111
|
|
|
@@ -84,8 +116,8 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
84
116
|
# Run the supervisor to start processing work
|
|
85
117
|
supervisor.run
|
|
86
118
|
|
|
87
|
-
puts
|
|
88
|
-
puts
|
|
119
|
+
puts 'Processing complete.'
|
|
120
|
+
puts 'Final Aggregated Results:'
|
|
89
121
|
# Access the results aggregator from the supervisor
|
|
90
122
|
puts supervisor.results.inspect
|
|
91
123
|
|
|
@@ -9,7 +9,7 @@ module SpecializedWorkers
|
|
|
9
9
|
super({
|
|
10
10
|
data: data,
|
|
11
11
|
operation: operation,
|
|
12
|
-
parameters: parameters
|
|
12
|
+
parameters: parameters,
|
|
13
13
|
})
|
|
14
14
|
end
|
|
15
15
|
|
|
@@ -32,12 +32,13 @@ module SpecializedWorkers
|
|
|
32
32
|
|
|
33
33
|
# Second work type: Database operations
|
|
34
34
|
class DatabaseWork < Fractor::Work
|
|
35
|
-
def initialize(data = "", query_type = :select, table = "unknown",
|
|
35
|
+
def initialize(data = "", query_type = :select, table = "unknown",
|
|
36
|
+
conditions = {})
|
|
36
37
|
super({
|
|
37
38
|
data: data,
|
|
38
39
|
query_type: query_type,
|
|
39
40
|
table: table,
|
|
40
|
-
conditions: conditions
|
|
41
|
+
conditions: conditions,
|
|
41
42
|
})
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -74,14 +75,16 @@ module SpecializedWorkers
|
|
|
74
75
|
unless work.is_a?(ComputeWork)
|
|
75
76
|
return Fractor::WorkResult.new(
|
|
76
77
|
error: "ComputeWorker can only process ComputeWork, got: #{work.class}",
|
|
77
|
-
work: work
|
|
78
|
+
work: work,
|
|
78
79
|
)
|
|
79
80
|
end
|
|
80
81
|
|
|
81
82
|
# Process based on the requested operation
|
|
82
83
|
result = case work.operation
|
|
83
|
-
when :matrix_multiply then matrix_multiply(work.data,
|
|
84
|
-
|
|
84
|
+
when :matrix_multiply then matrix_multiply(work.data,
|
|
85
|
+
work.parameters)
|
|
86
|
+
when :image_transform then image_transform(work.data,
|
|
87
|
+
work.parameters)
|
|
85
88
|
when :path_finding then path_finding(work.data, work.parameters)
|
|
86
89
|
else default_computation(work.data, work.parameters)
|
|
87
90
|
end
|
|
@@ -90,9 +93,9 @@ module SpecializedWorkers
|
|
|
90
93
|
result: {
|
|
91
94
|
operation: work.operation,
|
|
92
95
|
computation_result: result,
|
|
93
|
-
resources_used: @compute_resources
|
|
96
|
+
resources_used: @compute_resources,
|
|
94
97
|
},
|
|
95
|
-
work: work
|
|
98
|
+
work: work,
|
|
96
99
|
)
|
|
97
100
|
end
|
|
98
101
|
|
|
@@ -110,7 +113,7 @@ module SpecializedWorkers
|
|
|
110
113
|
# Simulate image transformation
|
|
111
114
|
sleep(rand(0.1..0.3))
|
|
112
115
|
transforms = params[:transforms] || %i[rotate scale]
|
|
113
|
-
"Image transformation applied: #{transforms.join(
|
|
116
|
+
"Image transformation applied: #{transforms.join(', ')} with parameters #{params}"
|
|
114
117
|
end
|
|
115
118
|
|
|
116
119
|
def path_finding(_data, params)
|
|
@@ -140,7 +143,7 @@ module SpecializedWorkers
|
|
|
140
143
|
unless work.is_a?(DatabaseWork)
|
|
141
144
|
return Fractor::WorkResult.new(
|
|
142
145
|
error: "DatabaseWorker can only process DatabaseWork, got: #{work.class}",
|
|
143
|
-
work: work
|
|
146
|
+
work: work,
|
|
144
147
|
)
|
|
145
148
|
end
|
|
146
149
|
|
|
@@ -148,7 +151,8 @@ module SpecializedWorkers
|
|
|
148
151
|
result = case work.query_type
|
|
149
152
|
when :select then perform_select(work.table, work.conditions)
|
|
150
153
|
when :insert then perform_insert(work.table, work.data)
|
|
151
|
-
when :update then perform_update(work.table, work.data,
|
|
154
|
+
when :update then perform_update(work.table, work.data,
|
|
155
|
+
work.conditions)
|
|
152
156
|
when :delete then perform_delete(work.table, work.conditions)
|
|
153
157
|
else default_query(work.query_type, work.table, work.conditions)
|
|
154
158
|
end
|
|
@@ -159,9 +163,9 @@ module SpecializedWorkers
|
|
|
159
163
|
table: work.table,
|
|
160
164
|
rows_affected: result[:rows_affected],
|
|
161
165
|
data: result[:data],
|
|
162
|
-
execution_time: result[:time]
|
|
166
|
+
execution_time: result[:time],
|
|
163
167
|
},
|
|
164
|
-
work: work
|
|
168
|
+
work: work,
|
|
165
169
|
)
|
|
166
170
|
end
|
|
167
171
|
|
|
@@ -174,8 +178,10 @@ module SpecializedWorkers
|
|
|
174
178
|
record_count = rand(0..20)
|
|
175
179
|
{
|
|
176
180
|
rows_affected: record_count,
|
|
177
|
-
data: record_count
|
|
178
|
-
|
|
181
|
+
data: Array.new(record_count) do |i|
|
|
182
|
+
{ id: i + 1, name: "Record #{i + 1}" }
|
|
183
|
+
end,
|
|
184
|
+
time: rand(0.01..0.05),
|
|
179
185
|
}
|
|
180
186
|
end
|
|
181
187
|
|
|
@@ -185,7 +191,7 @@ module SpecializedWorkers
|
|
|
185
191
|
{
|
|
186
192
|
rows_affected: 1,
|
|
187
193
|
data: { id: rand(1000..9999) },
|
|
188
|
-
time: rand(0.01..0.03)
|
|
194
|
+
time: rand(0.01..0.03),
|
|
189
195
|
}
|
|
190
196
|
end
|
|
191
197
|
|
|
@@ -196,7 +202,7 @@ module SpecializedWorkers
|
|
|
196
202
|
{
|
|
197
203
|
rows_affected: affected,
|
|
198
204
|
data: nil,
|
|
199
|
-
time: rand(0.01..0.05)
|
|
205
|
+
time: rand(0.01..0.05),
|
|
200
206
|
}
|
|
201
207
|
end
|
|
202
208
|
|
|
@@ -207,7 +213,7 @@ module SpecializedWorkers
|
|
|
207
213
|
{
|
|
208
214
|
rows_affected: affected,
|
|
209
215
|
data: nil,
|
|
210
|
-
time: rand(0.01..0.03)
|
|
216
|
+
time: rand(0.01..0.03),
|
|
211
217
|
}
|
|
212
218
|
end
|
|
213
219
|
|
|
@@ -217,7 +223,7 @@ module SpecializedWorkers
|
|
|
217
223
|
{
|
|
218
224
|
rows_affected: 0,
|
|
219
225
|
data: nil,
|
|
220
|
-
time: rand(0.005..0.01)
|
|
226
|
+
time: rand(0.005..0.01),
|
|
221
227
|
}
|
|
222
228
|
end
|
|
223
229
|
end
|
|
@@ -230,14 +236,14 @@ module SpecializedWorkers
|
|
|
230
236
|
# Create separate supervisors for each worker type
|
|
231
237
|
@compute_supervisor = Fractor::Supervisor.new(
|
|
232
238
|
worker_pools: [
|
|
233
|
-
{ worker_class: ComputeWorker, num_workers: compute_workers }
|
|
234
|
-
]
|
|
239
|
+
{ worker_class: ComputeWorker, num_workers: compute_workers },
|
|
240
|
+
],
|
|
235
241
|
)
|
|
236
242
|
|
|
237
243
|
@db_supervisor = Fractor::Supervisor.new(
|
|
238
244
|
worker_pools: [
|
|
239
|
-
{ worker_class: DatabaseWorker, num_workers: db_workers }
|
|
240
|
-
]
|
|
245
|
+
{ worker_class: DatabaseWorker, num_workers: db_workers },
|
|
246
|
+
],
|
|
241
247
|
)
|
|
242
248
|
|
|
243
249
|
@compute_results = []
|
|
@@ -253,7 +259,8 @@ module SpecializedWorkers
|
|
|
253
259
|
|
|
254
260
|
# Create and add database work items
|
|
255
261
|
db_work_items = db_tasks.map do |task|
|
|
256
|
-
DatabaseWork.new(task[:data], task[:query_type], task[:table],
|
|
262
|
+
DatabaseWork.new(task[:data], task[:query_type], task[:table],
|
|
263
|
+
task[:conditions])
|
|
257
264
|
end
|
|
258
265
|
@db_supervisor.add_work_items(db_work_items)
|
|
259
266
|
|
|
@@ -277,13 +284,13 @@ module SpecializedWorkers
|
|
|
277
284
|
computation: {
|
|
278
285
|
tasks: compute_tasks.size,
|
|
279
286
|
completed: @compute_results.size,
|
|
280
|
-
results: @compute_results
|
|
287
|
+
results: @compute_results,
|
|
281
288
|
},
|
|
282
289
|
database: {
|
|
283
290
|
tasks: db_tasks.size,
|
|
284
291
|
completed: @db_results.size,
|
|
285
|
-
results: @db_results
|
|
286
|
-
}
|
|
292
|
+
results: @db_results,
|
|
293
|
+
},
|
|
287
294
|
}
|
|
288
295
|
end
|
|
289
296
|
|
|
@@ -316,18 +323,18 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
316
323
|
{
|
|
317
324
|
operation: :matrix_multiply,
|
|
318
325
|
data: "Matrix data...",
|
|
319
|
-
parameters: { size: [10, 10] }
|
|
326
|
+
parameters: { size: [10, 10] },
|
|
320
327
|
},
|
|
321
328
|
{
|
|
322
329
|
operation: :image_transform,
|
|
323
330
|
data: "Image data...",
|
|
324
|
-
parameters: { transforms: %i[rotate scale blur], angle: 45, scale: 1.5 }
|
|
331
|
+
parameters: { transforms: %i[rotate scale blur], angle: 45, scale: 1.5 },
|
|
325
332
|
},
|
|
326
333
|
{
|
|
327
334
|
operation: :path_finding,
|
|
328
335
|
data: "Graph data...",
|
|
329
|
-
parameters: { algorithm: :dijkstra, nodes: 20, start: 1, end: 15 }
|
|
330
|
-
}
|
|
336
|
+
parameters: { algorithm: :dijkstra, nodes: 20, start: 1, end: 15 },
|
|
337
|
+
},
|
|
331
338
|
]
|
|
332
339
|
|
|
333
340
|
# Prepare database tasks
|
|
@@ -335,24 +342,24 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
335
342
|
{
|
|
336
343
|
query_type: :select,
|
|
337
344
|
table: "users",
|
|
338
|
-
conditions: { active: true, role: "admin" }
|
|
345
|
+
conditions: { active: true, role: "admin" },
|
|
339
346
|
},
|
|
340
347
|
{
|
|
341
348
|
query_type: :insert,
|
|
342
349
|
table: "orders",
|
|
343
|
-
data: "Order data..."
|
|
350
|
+
data: "Order data...",
|
|
344
351
|
},
|
|
345
352
|
{
|
|
346
353
|
query_type: :update,
|
|
347
354
|
table: "products",
|
|
348
355
|
data: "Product data...",
|
|
349
|
-
conditions: { category: "electronics" }
|
|
356
|
+
conditions: { category: "electronics" },
|
|
350
357
|
},
|
|
351
358
|
{
|
|
352
359
|
query_type: :delete,
|
|
353
360
|
table: "sessions",
|
|
354
|
-
conditions: { expired: true }
|
|
355
|
-
}
|
|
361
|
+
conditions: { expired: true },
|
|
362
|
+
},
|
|
356
363
|
]
|
|
357
364
|
|
|
358
365
|
compute_workers = 2
|
|
@@ -363,7 +370,7 @@ if __FILE__ == $PROGRAM_NAME
|
|
|
363
370
|
start_time = Time.now
|
|
364
371
|
system = SpecializedWorkers::HybridSystem.new(
|
|
365
372
|
compute_workers: compute_workers,
|
|
366
|
-
db_workers: db_workers
|
|
373
|
+
db_workers: db_workers,
|
|
367
374
|
)
|
|
368
375
|
result = system.process_mixed_workload(compute_tasks, db_tasks)
|
|
369
376
|
end_time = Time.now
|