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
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'json'
|
|
7
|
+
|
|
8
|
+
module ContinuousChat
|
|
9
|
+
# Simulation controller that manages the server and clients
|
|
10
|
+
class Simulation
|
|
11
|
+
attr_reader :server_port, :log_dir
|
|
12
|
+
|
|
13
|
+
def initialize(server_port = 3000, duration = 10, log_dir = 'logs')
|
|
14
|
+
@server_port = server_port
|
|
15
|
+
@duration = duration
|
|
16
|
+
@log_dir = log_dir
|
|
17
|
+
@server_pid = nil
|
|
18
|
+
@client_pids = {}
|
|
19
|
+
@running = false
|
|
20
|
+
|
|
21
|
+
# Create log directory if it doesn't exist
|
|
22
|
+
FileUtils.mkdir_p(@log_dir)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Start the simulation
|
|
26
|
+
def start
|
|
27
|
+
puts "Starting chat simulation on port #{@server_port}"
|
|
28
|
+
puts "Logs will be saved to #{@log_dir}"
|
|
29
|
+
|
|
30
|
+
# Start the server
|
|
31
|
+
start_server
|
|
32
|
+
|
|
33
|
+
# Give the server time to initialize
|
|
34
|
+
puts 'Waiting for server to initialize...'
|
|
35
|
+
sleep(2)
|
|
36
|
+
|
|
37
|
+
# Start the clients
|
|
38
|
+
start_clients
|
|
39
|
+
|
|
40
|
+
@running = true
|
|
41
|
+
puts 'Chat simulation started'
|
|
42
|
+
|
|
43
|
+
# Wait for the specified duration
|
|
44
|
+
puts "Simulation will run for #{@duration} seconds"
|
|
45
|
+
|
|
46
|
+
# Give clients time to connect
|
|
47
|
+
sleep(2)
|
|
48
|
+
puts 'Clients should be connecting now...'
|
|
49
|
+
|
|
50
|
+
# Wait for messages to be processed
|
|
51
|
+
remaining_time = @duration - 4
|
|
52
|
+
if remaining_time.positive?
|
|
53
|
+
puts "Waiting #{remaining_time} more seconds for processing..."
|
|
54
|
+
sleep(remaining_time)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
puts 'Simulation time complete, stopping...'
|
|
58
|
+
|
|
59
|
+
# Stop the simulation
|
|
60
|
+
stop
|
|
61
|
+
|
|
62
|
+
# Analyze the logs
|
|
63
|
+
analyze_logs
|
|
64
|
+
|
|
65
|
+
true
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
puts "Failed to start simulation: #{e.message}"
|
|
68
|
+
stop
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Stop the simulation
|
|
73
|
+
def stop
|
|
74
|
+
puts 'Stopping chat simulation...'
|
|
75
|
+
|
|
76
|
+
# Stop all clients
|
|
77
|
+
stop_clients
|
|
78
|
+
|
|
79
|
+
# Stop the server
|
|
80
|
+
stop_server
|
|
81
|
+
|
|
82
|
+
@running = false
|
|
83
|
+
puts 'Chat simulation stopped'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Start the server process
|
|
89
|
+
def start_server
|
|
90
|
+
server_log_file = File.join(@log_dir, 'server_messages.log')
|
|
91
|
+
|
|
92
|
+
# Get the directory where this script is located
|
|
93
|
+
script_dir = File.dirname(__FILE__)
|
|
94
|
+
server_script = File.join(script_dir, 'chat_server.rb')
|
|
95
|
+
|
|
96
|
+
server_cmd = "ruby #{server_script} #{@server_port} #{server_log_file}"
|
|
97
|
+
|
|
98
|
+
puts "Starting server: #{server_cmd}"
|
|
99
|
+
|
|
100
|
+
# Start the server process as a fork
|
|
101
|
+
@server_pid = fork do
|
|
102
|
+
exec(server_cmd)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
puts "Server started with PID #{@server_pid}"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Stop the server process
|
|
109
|
+
def stop_server
|
|
110
|
+
return unless @server_pid
|
|
111
|
+
|
|
112
|
+
puts "Stopping server (PID #{@server_pid})..."
|
|
113
|
+
|
|
114
|
+
# Send SIGINT to the server process
|
|
115
|
+
begin
|
|
116
|
+
Process.kill('INT', @server_pid)
|
|
117
|
+
# Give it a moment to shut down gracefully
|
|
118
|
+
sleep(1)
|
|
119
|
+
|
|
120
|
+
# Force kill if still running
|
|
121
|
+
Process.kill('KILL', @server_pid) if process_running?(@server_pid)
|
|
122
|
+
rescue Errno::ESRCH
|
|
123
|
+
# Process already gone
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
@server_pid = nil
|
|
127
|
+
puts 'Server stopped'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Start client processes
|
|
131
|
+
def start_clients
|
|
132
|
+
# Define the client usernames and their messages
|
|
133
|
+
clients = {
|
|
134
|
+
'alice' => [
|
|
135
|
+
{ content: 'Hello everyone!', recipient: 'all' },
|
|
136
|
+
{ content: "I'm working on a Ruby project using sockets",
|
|
137
|
+
recipient: 'all' },
|
|
138
|
+
{ content: "It's a simple chat server and client", recipient: 'all' }
|
|
139
|
+
],
|
|
140
|
+
'bob' => [
|
|
141
|
+
{ content: 'Hi Alice!', recipient: 'alice' },
|
|
142
|
+
{ content: 'That sounds interesting. What kind of project?',
|
|
143
|
+
recipient: 'alice' },
|
|
144
|
+
{ content: "Cool! I love Ruby's socket features",
|
|
145
|
+
recipient: 'alice' }
|
|
146
|
+
],
|
|
147
|
+
'charlie' => [
|
|
148
|
+
{ content: "How's everyone doing today?", recipient: 'all' },
|
|
149
|
+
{ content: 'Are you using any specific libraries?',
|
|
150
|
+
recipient: 'alice' },
|
|
151
|
+
{ content: 'Non-blocking IO in chat clients is efficient',
|
|
152
|
+
recipient: 'all' }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
puts "Starting #{clients.size} clients: #{clients.keys.join(', ')}"
|
|
157
|
+
|
|
158
|
+
# Start each client in a separate process
|
|
159
|
+
clients.each do |username, messages|
|
|
160
|
+
start_client(username, messages)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Start a single client process
|
|
165
|
+
def start_client(username, messages)
|
|
166
|
+
client_log_file = File.join(@log_dir, "client_#{username}_messages.log")
|
|
167
|
+
messages_file = File.join(@log_dir,
|
|
168
|
+
"client_#{username}_send_messages.json")
|
|
169
|
+
|
|
170
|
+
# Write the messages to a JSON file
|
|
171
|
+
File.write(messages_file, JSON.generate(messages))
|
|
172
|
+
|
|
173
|
+
# Get the directory where this script is located
|
|
174
|
+
script_dir = File.dirname(__FILE__)
|
|
175
|
+
client_script = File.join(script_dir, 'chat_client.rb')
|
|
176
|
+
|
|
177
|
+
# Build the client command
|
|
178
|
+
client_cmd = "ruby #{client_script} #{username} #{@server_port} #{client_log_file}"
|
|
179
|
+
|
|
180
|
+
puts "Starting client #{username}"
|
|
181
|
+
|
|
182
|
+
# Start the client process as a fork
|
|
183
|
+
@client_pids[username] = fork do
|
|
184
|
+
exec(client_cmd)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
puts "Client #{username} started with PID #{@client_pids[username]}"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Stop all client processes
|
|
191
|
+
def stop_clients
|
|
192
|
+
return if @client_pids.empty?
|
|
193
|
+
|
|
194
|
+
puts "Stopping #{@client_pids.size} clients..."
|
|
195
|
+
|
|
196
|
+
@client_pids.each do |username, pid|
|
|
197
|
+
# Try to gracefully terminate the process
|
|
198
|
+
begin
|
|
199
|
+
Process.kill('INT', pid)
|
|
200
|
+
# Give it a moment to shut down
|
|
201
|
+
sleep(0.5)
|
|
202
|
+
|
|
203
|
+
# Force kill if still running
|
|
204
|
+
Process.kill('KILL', pid) if process_running?(pid)
|
|
205
|
+
rescue Errno::ESRCH
|
|
206
|
+
# Process already gone
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
puts "Client #{username} stopped"
|
|
210
|
+
rescue StandardError => e
|
|
211
|
+
puts "Error stopping client #{username}: #{e.message}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
@client_pids.clear
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Check if a process is still running
|
|
218
|
+
def process_running?(pid)
|
|
219
|
+
Process.getpgid(pid)
|
|
220
|
+
true
|
|
221
|
+
rescue Errno::ESRCH
|
|
222
|
+
false
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Analyze the log files after the simulation
|
|
226
|
+
def analyze_logs
|
|
227
|
+
puts "\nSimulation Results"
|
|
228
|
+
puts '================='
|
|
229
|
+
|
|
230
|
+
# Analyze server log
|
|
231
|
+
server_log_file = File.join(@log_dir, 'server_messages.log')
|
|
232
|
+
if File.exist?(server_log_file)
|
|
233
|
+
server_log = File.readlines(server_log_file)
|
|
234
|
+
puts "Server processed #{server_log.size} log entries"
|
|
235
|
+
|
|
236
|
+
# Count message types
|
|
237
|
+
message_count = server_log.count do |line|
|
|
238
|
+
line.include?('Received from')
|
|
239
|
+
end
|
|
240
|
+
broadcast_count = server_log.count do |line|
|
|
241
|
+
line.include?('Fractor: Broadcasting message from') ||
|
|
242
|
+
line.include?('Fractor processed: broadcast')
|
|
243
|
+
end
|
|
244
|
+
direct_count = server_log.count do |line|
|
|
245
|
+
line.include?('Fractor: Direct message from') ||
|
|
246
|
+
line.include?('Fractor processed: direct_message')
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
puts " - #{message_count} messages received from clients"
|
|
250
|
+
puts " - #{broadcast_count} broadcast messages processed by Fractor"
|
|
251
|
+
puts " - #{direct_count} direct messages processed by Fractor"
|
|
252
|
+
else
|
|
253
|
+
puts 'Server log file not found'
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
puts "\nClient Activity:"
|
|
257
|
+
# Analyze each client log
|
|
258
|
+
@client_pids.each_key do |username|
|
|
259
|
+
client_log_file = File.join(@log_dir, "client_#{username}_messages.log")
|
|
260
|
+
if File.exist?(client_log_file)
|
|
261
|
+
client_log = File.readlines(client_log_file)
|
|
262
|
+
sent_count = client_log.count { |line| line.include?('Sent message') }
|
|
263
|
+
received_count = client_log.count do |line|
|
|
264
|
+
line.include?('Received:')
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
puts " #{username}: Sent #{sent_count} messages, Received #{received_count} messages"
|
|
268
|
+
else
|
|
269
|
+
puts " #{username}: Log file not found"
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
puts "\nLog files are available in the #{@log_dir} directory for detailed analysis."
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# When run directly, start the simulation
|
|
279
|
+
if __FILE__ == $PROGRAM_NAME
|
|
280
|
+
options = {
|
|
281
|
+
port: 3000,
|
|
282
|
+
duration: 10,
|
|
283
|
+
log_dir: 'logs'
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# Parse command line options
|
|
287
|
+
OptionParser.new do |opts|
|
|
288
|
+
opts.banner = 'Usage: ruby simulate.rb [options]'
|
|
289
|
+
|
|
290
|
+
opts.on('-p', '--port PORT', Integer,
|
|
291
|
+
'Server port (default: 3000)') do |port|
|
|
292
|
+
options[:port] = port
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
opts.on('-d', '--duration SECONDS', Integer,
|
|
296
|
+
'Simulation duration in seconds (default: 10)') do |duration|
|
|
297
|
+
options[:duration] = duration
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
opts.on('-l', '--log-dir DIR',
|
|
301
|
+
'Directory for log files (default: logs)') do |dir|
|
|
302
|
+
options[:log_dir] = dir
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
opts.on('-h', '--help', 'Show this help message') do
|
|
306
|
+
puts opts
|
|
307
|
+
exit
|
|
308
|
+
end
|
|
309
|
+
end.parse!
|
|
310
|
+
|
|
311
|
+
puts 'Starting Chat Simulation'
|
|
312
|
+
puts '======================'
|
|
313
|
+
puts 'This simulation runs a chat server and multiple clients as separate processes'
|
|
314
|
+
puts 'to demonstrate a basic chat application with socket communication.'
|
|
315
|
+
puts
|
|
316
|
+
|
|
317
|
+
# Create and run the simulation
|
|
318
|
+
simulation = ContinuousChat::Simulation.new(
|
|
319
|
+
options[:port],
|
|
320
|
+
options[:duration],
|
|
321
|
+
options[:log_dir]
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Set up signal handlers to properly clean up child processes
|
|
325
|
+
Signal.trap('INT') do
|
|
326
|
+
puts "\nSimulation interrupted"
|
|
327
|
+
simulation.stop
|
|
328
|
+
exit
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
Signal.trap('TERM') do
|
|
332
|
+
puts "\nSimulation terminated"
|
|
333
|
+
simulation.stop
|
|
334
|
+
exit
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
begin
|
|
338
|
+
simulation.start
|
|
339
|
+
rescue Interrupt
|
|
340
|
+
puts "\nSimulation interrupted"
|
|
341
|
+
simulation.stop
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
puts 'Simulation completed'
|
|
345
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
= Continuous Chat Server Example
|
|
2
|
+
|
|
3
|
+
== Overview
|
|
4
|
+
|
|
5
|
+
This example demonstrates Fractor's continuous mode feature with a chat server implementation. In continuous mode, a Fractor supervisor can run indefinitely, processing work items as they arrive without stopping after the initial work queue is empty.
|
|
6
|
+
|
|
7
|
+
The example is structured as a real client-server application with socket communication, allowing you to test it with multiple terminals.
|
|
8
|
+
|
|
9
|
+
== Key Concepts
|
|
10
|
+
|
|
11
|
+
* *Continuous Mode*: Supervisors run indefinitely, waiting for new work rather than terminating
|
|
12
|
+
* *Work Sources*: Callback functions that provide new work to the supervisor as needed
|
|
13
|
+
* *Asynchronous Processing*: Workers process messages concurrently as they arrive
|
|
14
|
+
* *Graceful Shutdown*: Proper handling of resources when the system terminates
|
|
15
|
+
* *Thread Coordination*: Multiple threads working together with Ractors
|
|
16
|
+
* *Parallel Components*: Server and client both use multiple Fractors for different responsibilities
|
|
17
|
+
|
|
18
|
+
== Example Components
|
|
19
|
+
|
|
20
|
+
This example is split into multiple files for better organization:
|
|
21
|
+
|
|
22
|
+
1. *chat_common.rb*: Contains shared code used by both server and client
|
|
23
|
+
* `ChatMessage` class extending `Fractor::Work`
|
|
24
|
+
* `ChatWorker` class extending `Fractor::Worker`
|
|
25
|
+
* Message protocol utilities for serialization
|
|
26
|
+
|
|
27
|
+
2. *chat_server.rb*: Implements the chat server with two parallel components
|
|
28
|
+
* Socket handler Fractor that manages client connections using IO.select
|
|
29
|
+
* Logger Fractor that writes messages to log sequentially
|
|
30
|
+
* Thread-safe queue for message passing between components
|
|
31
|
+
* `ChatServer` class that manages the supervisor and coordinates components
|
|
32
|
+
|
|
33
|
+
3. *chat_client.rb*: Implements the chat client with two parallel components
|
|
34
|
+
* User I/O handler Fractor for STDIN (user input) and STDOUT (display)
|
|
35
|
+
* Server I/O handler Fractor for socket communication with the server
|
|
36
|
+
* Separate queues for messages received and to be sent
|
|
37
|
+
* Interactive command-line interface
|
|
38
|
+
|
|
39
|
+
4. *simulate.rb*: Automated simulation with multiple clients
|
|
40
|
+
* Creates a server and multiple clients as separate processes
|
|
41
|
+
* Runs a predefined message schedule
|
|
42
|
+
* Demonstrates the system at scale
|
|
43
|
+
* Provides analysis of communication logs after completion
|
|
44
|
+
|
|
45
|
+
== Running the Example
|
|
46
|
+
|
|
47
|
+
You can run the example in several ways:
|
|
48
|
+
|
|
49
|
+
=== 1. Running the Simulation
|
|
50
|
+
|
|
51
|
+
To run the complete automated simulation:
|
|
52
|
+
|
|
53
|
+
[source,sh]
|
|
54
|
+
----
|
|
55
|
+
ruby examples/continuous_chat_server/simulate.rb
|
|
56
|
+
----
|
|
57
|
+
|
|
58
|
+
Optional parameters:
|
|
59
|
+
* `-p, --port PORT` - Specify server port (default: 3000)
|
|
60
|
+
* `-w, --workers NUM` - Number of worker Ractors (default: 2)
|
|
61
|
+
* `-d, --duration SECONDS` - Duration of simulation in seconds (default: 10)
|
|
62
|
+
* `-l, --log-dir DIR` - Directory for log files (default: logs)
|
|
63
|
+
* `-h, --help` - Show help message
|
|
64
|
+
|
|
65
|
+
=== 2. Running Server and Clients Separately
|
|
66
|
+
|
|
67
|
+
For a more interactive experience, you can run the server in one terminal:
|
|
68
|
+
|
|
69
|
+
[source,sh]
|
|
70
|
+
----
|
|
71
|
+
ruby examples/continuous_chat_server/chat_server.rb [PORT] [NUM_WORKERS] [LOG_FILE]
|
|
72
|
+
----
|
|
73
|
+
|
|
74
|
+
And then run multiple clients in different terminals:
|
|
75
|
+
|
|
76
|
+
[source,sh]
|
|
77
|
+
----
|
|
78
|
+
ruby examples/continuous_chat_server/chat_client.rb [USERNAME] [PORT] [LOG_FILE]
|
|
79
|
+
----
|
|
80
|
+
|
|
81
|
+
== Client Commands
|
|
82
|
+
|
|
83
|
+
When running the interactive client, you can use the following commands:
|
|
84
|
+
|
|
85
|
+
* `/help` - Show help message
|
|
86
|
+
* `/quit` - Disconnect and exit
|
|
87
|
+
* `/list` - List connected users
|
|
88
|
+
* `/msg USERNAME MESSAGE` - Send a private message
|
|
89
|
+
|
|
90
|
+
== Architecture
|
|
91
|
+
|
|
92
|
+
=== Server Architecture
|
|
93
|
+
The chat server has two parallel components:
|
|
94
|
+
1. A Fractor that handles IO.select on a TCPSocket (waits for incoming connections and messages)
|
|
95
|
+
* This component only processes work when IO.select has something to provide
|
|
96
|
+
* It puts received messages into a thread-safe Queue
|
|
97
|
+
2. A Fractor that writes messages to log sequentially
|
|
98
|
+
* Ensures that log entries are written in order without race conditions
|
|
99
|
+
|
|
100
|
+
The server runs as a single process with these concurrent components communicating via message passing.
|
|
101
|
+
|
|
102
|
+
=== Client Architecture
|
|
103
|
+
The chat client also has two parallel components:
|
|
104
|
+
1. A Fractor that handles IO.select for STDIN and STDOUT
|
|
105
|
+
* Manages user input without blocking
|
|
106
|
+
* Ensures sequential printing to the user's screen
|
|
107
|
+
* Uses separate queues for:
|
|
108
|
+
* Messages received from server (to be displayed to user)
|
|
109
|
+
* Messages from user (to be sent to server)
|
|
110
|
+
2. A Fractor that handles IO.select on the TCPSocket
|
|
111
|
+
* Manages the connection to the server
|
|
112
|
+
* Sends and receives messages concurrently
|
|
113
|
+
|
|
114
|
+
== Features Demonstrated
|
|
115
|
+
|
|
116
|
+
* Setting up a supervisor in continuous mode
|
|
117
|
+
* Registering a work source callback that provides new work on demand
|
|
118
|
+
* Running the supervisor in a non-blocking manner
|
|
119
|
+
* Socket-based client-server communication
|
|
120
|
+
* Coordinating between Ruby threads and Ractor workers
|
|
121
|
+
* Managing the lifecycle of a long-running application
|
|
122
|
+
* Proper resource cleanup on shutdown
|
|
123
|
+
* Using Ractors for handling separate concerns (IO, logging, etc.)
|
|
124
|
+
* Thread-safe message passing between components
|
|
125
|
+
|
|
126
|
+
== Expected Output
|
|
127
|
+
|
|
128
|
+
The example will show:
|
|
129
|
+
* The chat server starting up with multiple workers
|
|
130
|
+
* Clients connecting to the server
|
|
131
|
+
* Messages being sent between clients
|
|
132
|
+
* Workers processing the messages concurrently
|
|
133
|
+
* Results being delivered to recipients
|
|
134
|
+
* The system gracefully shutting down after all messages are processed
|
|
135
|
+
* A summary of message activity from the logs
|