tasker-rb 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/DEVELOPMENT.md +548 -0
- data/README.md +87 -0
- data/ext/tasker_core/Cargo.lock +4720 -0
- data/ext/tasker_core/Cargo.toml +76 -0
- data/ext/tasker_core/extconf.rb +38 -0
- data/ext/tasker_core/src/CLAUDE.md +7 -0
- data/ext/tasker_core/src/bootstrap.rs +320 -0
- data/ext/tasker_core/src/bridge.rs +400 -0
- data/ext/tasker_core/src/client_ffi.rs +173 -0
- data/ext/tasker_core/src/conversions.rs +131 -0
- data/ext/tasker_core/src/diagnostics.rs +57 -0
- data/ext/tasker_core/src/event_handler.rs +179 -0
- data/ext/tasker_core/src/event_publisher_ffi.rs +239 -0
- data/ext/tasker_core/src/ffi_logging.rs +245 -0
- data/ext/tasker_core/src/global_event_system.rs +16 -0
- data/ext/tasker_core/src/in_process_event_ffi.rs +319 -0
- data/ext/tasker_core/src/lib.rs +41 -0
- data/ext/tasker_core/src/observability_ffi.rs +339 -0
- data/lib/tasker_core/batch_processing/batch_aggregation_scenario.rb +85 -0
- data/lib/tasker_core/batch_processing/batch_worker_context.rb +238 -0
- data/lib/tasker_core/bootstrap.rb +394 -0
- data/lib/tasker_core/domain_events/base_publisher.rb +220 -0
- data/lib/tasker_core/domain_events/base_subscriber.rb +178 -0
- data/lib/tasker_core/domain_events/publisher_registry.rb +253 -0
- data/lib/tasker_core/domain_events/subscriber_registry.rb +152 -0
- data/lib/tasker_core/domain_events.rb +43 -0
- data/lib/tasker_core/errors/CLAUDE.md +7 -0
- data/lib/tasker_core/errors/common.rb +305 -0
- data/lib/tasker_core/errors/error_classifier.rb +61 -0
- data/lib/tasker_core/errors.rb +4 -0
- data/lib/tasker_core/event_bridge.rb +330 -0
- data/lib/tasker_core/handlers.rb +159 -0
- data/lib/tasker_core/internal.rb +31 -0
- data/lib/tasker_core/logger.rb +234 -0
- data/lib/tasker_core/models.rb +337 -0
- data/lib/tasker_core/observability/types.rb +158 -0
- data/lib/tasker_core/observability.rb +292 -0
- data/lib/tasker_core/registry/handler_registry.rb +453 -0
- data/lib/tasker_core/registry/resolver_chain.rb +258 -0
- data/lib/tasker_core/registry/resolvers/base_resolver.rb +90 -0
- data/lib/tasker_core/registry/resolvers/class_constant_resolver.rb +156 -0
- data/lib/tasker_core/registry/resolvers/explicit_mapping_resolver.rb +146 -0
- data/lib/tasker_core/registry/resolvers/method_dispatch_wrapper.rb +144 -0
- data/lib/tasker_core/registry/resolvers/registry_resolver.rb +229 -0
- data/lib/tasker_core/registry/resolvers.rb +42 -0
- data/lib/tasker_core/registry.rb +12 -0
- data/lib/tasker_core/step_handler/api.rb +48 -0
- data/lib/tasker_core/step_handler/base.rb +354 -0
- data/lib/tasker_core/step_handler/batchable.rb +50 -0
- data/lib/tasker_core/step_handler/decision.rb +53 -0
- data/lib/tasker_core/step_handler/mixins/api.rb +452 -0
- data/lib/tasker_core/step_handler/mixins/batchable.rb +465 -0
- data/lib/tasker_core/step_handler/mixins/decision.rb +252 -0
- data/lib/tasker_core/step_handler/mixins.rb +66 -0
- data/lib/tasker_core/subscriber.rb +212 -0
- data/lib/tasker_core/task_handler/base.rb +254 -0
- data/lib/tasker_core/tasker_rb.so +0 -0
- data/lib/tasker_core/template_discovery.rb +181 -0
- data/lib/tasker_core/tracing.rb +166 -0
- data/lib/tasker_core/types/batch_processing_outcome.rb +301 -0
- data/lib/tasker_core/types/client_types.rb +145 -0
- data/lib/tasker_core/types/decision_point_outcome.rb +177 -0
- data/lib/tasker_core/types/error_types.rb +72 -0
- data/lib/tasker_core/types/simple_message.rb +151 -0
- data/lib/tasker_core/types/step_context.rb +328 -0
- data/lib/tasker_core/types/step_handler_call_result.rb +307 -0
- data/lib/tasker_core/types/step_message.rb +112 -0
- data/lib/tasker_core/types/step_types.rb +207 -0
- data/lib/tasker_core/types/task_template.rb +240 -0
- data/lib/tasker_core/types/task_types.rb +148 -0
- data/lib/tasker_core/types.rb +132 -0
- data/lib/tasker_core/version.rb +13 -0
- data/lib/tasker_core/worker/CLAUDE.md +7 -0
- data/lib/tasker_core/worker/event_poller.rb +224 -0
- data/lib/tasker_core/worker/in_process_domain_event_poller.rb +271 -0
- data/lib/tasker_core.rb +160 -0
- metadata +322 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "tasker-rb"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
description = "Ruby bindings for tasker-core: High-performance workflow orchestration"
|
|
6
|
+
readme = "../../../../README.md"
|
|
7
|
+
repository = "https://github.com/tasker-systems/tasker-core"
|
|
8
|
+
license = "MIT"
|
|
9
|
+
keywords = ["bindings", "ffi", "magnus", "ruby", "worker"]
|
|
10
|
+
categories = ["api-bindings", "asynchronous"]
|
|
11
|
+
|
|
12
|
+
# TAS-145: Ignore false positives from cargo-machete
|
|
13
|
+
[package.metadata.cargo-machete]
|
|
14
|
+
ignored = [
|
|
15
|
+
# Dev-dependencies
|
|
16
|
+
"tasker-core",
|
|
17
|
+
# Used in FFI layer but not detected by analysis
|
|
18
|
+
"anyhow",
|
|
19
|
+
"async-trait",
|
|
20
|
+
"dotenvy",
|
|
21
|
+
"once_cell",
|
|
22
|
+
"serde_yaml",
|
|
23
|
+
"sqlx",
|
|
24
|
+
"thiserror",
|
|
25
|
+
"workspace_tools",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[lib]
|
|
29
|
+
crate-type = ["cdylib"]
|
|
30
|
+
name = "tasker_rb"
|
|
31
|
+
|
|
32
|
+
[dependencies]
|
|
33
|
+
anyhow = { workspace = true }
|
|
34
|
+
# Async trait support for FrameworkIntegration
|
|
35
|
+
async-trait = { workspace = true }
|
|
36
|
+
# Time handling for Ruby Time ↔ Rust DateTime conversion
|
|
37
|
+
chrono = { workspace = true }
|
|
38
|
+
# Environment variable loading
|
|
39
|
+
dotenvy = { workspace = true }
|
|
40
|
+
# Ruby FFI
|
|
41
|
+
# NOTE: "embed" feature removed for M4 Pro compatibility (TAS-XXX)
|
|
42
|
+
# Static linking with embed causes segfaults on Apple M4 Pro due to
|
|
43
|
+
# ARM64 FEAT_LSE2 atomic instruction incompatibility
|
|
44
|
+
magnus = { version = "0.8" }
|
|
45
|
+
# Once Cell
|
|
46
|
+
once_cell = { workspace = true }
|
|
47
|
+
# Serde for serialization/deserialization
|
|
48
|
+
serde = { workspace = true }
|
|
49
|
+
# Serialization for Ruby ↔ Rust data conversion
|
|
50
|
+
serde_json = { workspace = true }
|
|
51
|
+
serde_magnus = "0.10"
|
|
52
|
+
serde_yaml = { workspace = true }
|
|
53
|
+
# Database (needed for PgPool)
|
|
54
|
+
sqlx = { workspace = true }
|
|
55
|
+
# Core library dependency (relative path to parent)
|
|
56
|
+
tasker-shared = { path = "../../../../tasker-shared" }
|
|
57
|
+
tasker-worker = { path = "../../../../tasker-worker" }
|
|
58
|
+
# Error handling
|
|
59
|
+
thiserror = { workspace = true }
|
|
60
|
+
# Async runtime for blocking on futures in FFI
|
|
61
|
+
tokio = { workspace = true, features = ["rt-multi-thread"] }
|
|
62
|
+
# Logging
|
|
63
|
+
tracing = { workspace = true }
|
|
64
|
+
# UUID generation for event bridge
|
|
65
|
+
uuid = { workspace = true }
|
|
66
|
+
# Project root detection
|
|
67
|
+
workspace_tools = { workspace = true }
|
|
68
|
+
|
|
69
|
+
[dev-dependencies]
|
|
70
|
+
tasker-core = { package = "tasker-core", path = "../../../../" }
|
|
71
|
+
|
|
72
|
+
[features]
|
|
73
|
+
default = []
|
|
74
|
+
|
|
75
|
+
[lints]
|
|
76
|
+
workspace = true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'mkmf'
|
|
5
|
+
require 'rb_sys/mkmf'
|
|
6
|
+
|
|
7
|
+
# Ensure we have the required tools
|
|
8
|
+
unless find_executable('cargo')
|
|
9
|
+
abort <<~MSG
|
|
10
|
+
|
|
11
|
+
❌ Rust toolchain not found!
|
|
12
|
+
|
|
13
|
+
tasker-rb requires Rust to compile the native extension.
|
|
14
|
+
|
|
15
|
+
Please install Rust:
|
|
16
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
17
|
+
source $HOME/.cargo/env
|
|
18
|
+
|
|
19
|
+
Or visit: https://rustup.rs/
|
|
20
|
+
|
|
21
|
+
MSG
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Check Rust version
|
|
25
|
+
rust_version = `cargo --version`.strip
|
|
26
|
+
puts "🦀 Using Rust: #{rust_version}"
|
|
27
|
+
|
|
28
|
+
# Enable test-helpers feature when running tests
|
|
29
|
+
if ENV['ENABLE_TEST_HELPERS'] || ENV['TASKER_ENV'] == 'test'
|
|
30
|
+
ENV['CARGO_FEATURE_TEST_HELPERS'] = '1'
|
|
31
|
+
puts '🧪 Enabling test helpers for development/test environment'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Create the Rust makefile for the extension
|
|
35
|
+
# This will compile the Rust code into a Ruby-loadable shared library
|
|
36
|
+
create_rust_makefile('tasker_rb')
|
|
37
|
+
|
|
38
|
+
puts "✅ Configuration complete! Run 'make' to compile the extension."
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
//! # Ruby Worker Bootstrap
|
|
2
|
+
//!
|
|
3
|
+
//! TAS-67: Follows the same patterns as workers/rust/src/bootstrap.rs but adapted
|
|
4
|
+
//! for Ruby FFI integration with magnus. Uses FfiDispatchChannel for step event
|
|
5
|
+
//! dispatch instead of the legacy RubyEventHandler.
|
|
6
|
+
//!
|
|
7
|
+
//! ## Architecture
|
|
8
|
+
//!
|
|
9
|
+
//! ```text
|
|
10
|
+
//! WorkerBootstrap → WorkerSystemHandle → take_dispatch_handles()
|
|
11
|
+
//! │
|
|
12
|
+
//! ▼
|
|
13
|
+
//! FfiDispatchChannel
|
|
14
|
+
//! │
|
|
15
|
+
//! ┌────────────────┴────────────────┐
|
|
16
|
+
//! ▼ ▼
|
|
17
|
+
//! poll_step_events() complete_step_event()
|
|
18
|
+
//! (Ruby FFI) (Ruby FFI)
|
|
19
|
+
//! ```
|
|
20
|
+
|
|
21
|
+
use crate::bridge::{RubyBridgeHandle, WORKER_SYSTEM};
|
|
22
|
+
use crate::global_event_system::get_global_event_system;
|
|
23
|
+
use magnus::{value::ReprValue, Error, ExceptionClass, Ruby, Value};
|
|
24
|
+
use std::sync::Arc;
|
|
25
|
+
|
|
26
|
+
/// Helper to get RuntimeError exception class
|
|
27
|
+
/// Uses the new magnus 0.8 API pattern
|
|
28
|
+
fn runtime_error_class() -> ExceptionClass {
|
|
29
|
+
Ruby::get()
|
|
30
|
+
.expect("Ruby runtime should be available")
|
|
31
|
+
.exception_runtime_error()
|
|
32
|
+
}
|
|
33
|
+
use tasker_worker::worker::{
|
|
34
|
+
services::CheckpointService, DomainEventCallback, FfiDispatchChannel, FfiDispatchChannelConfig,
|
|
35
|
+
StepEventPublisherRegistry,
|
|
36
|
+
};
|
|
37
|
+
use tasker_worker::WorkerBootstrap;
|
|
38
|
+
use tokio::sync::RwLock;
|
|
39
|
+
use tracing::{error, info};
|
|
40
|
+
use uuid::Uuid;
|
|
41
|
+
|
|
42
|
+
/// Bootstrap the worker system for Ruby
|
|
43
|
+
///
|
|
44
|
+
/// TAS-67: This now uses FfiDispatchChannel for step event dispatch,
|
|
45
|
+
/// matching the architecture of the Rust worker but adapted for FFI.
|
|
46
|
+
///
|
|
47
|
+
/// Returns a handle ID that Ruby can use to reference the worker system.
|
|
48
|
+
pub fn bootstrap_worker() -> Result<Value, Error> {
|
|
49
|
+
let worker_id = Uuid::new_v4();
|
|
50
|
+
let worker_id_str = format!("ruby-worker-{}", worker_id);
|
|
51
|
+
|
|
52
|
+
// Check if already running
|
|
53
|
+
let mut handle_guard = WORKER_SYSTEM.lock().map_err(|e| {
|
|
54
|
+
error!("Failed to acquire worker system lock: {}", e);
|
|
55
|
+
Error::new(runtime_error_class(), "Lock acquisition failed")
|
|
56
|
+
})?;
|
|
57
|
+
|
|
58
|
+
if handle_guard.is_some() {
|
|
59
|
+
// Return existing handle info
|
|
60
|
+
let ruby = magnus::Ruby::get().map_err(|err| {
|
|
61
|
+
Error::new(
|
|
62
|
+
runtime_error_class(),
|
|
63
|
+
format!("Failed to get ruby system: {}", err),
|
|
64
|
+
)
|
|
65
|
+
})?;
|
|
66
|
+
let hash = ruby.hash_new();
|
|
67
|
+
hash.aset("handle_id", worker_id.to_string())?;
|
|
68
|
+
hash.aset("status", "already_running")?;
|
|
69
|
+
hash.aset("message", "Worker system already running")?;
|
|
70
|
+
return Ok(hash.as_value());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create tokio runtime with explicit thread pool sizing
|
|
74
|
+
// NOTE: Explicit worker_threads=8 for M2/M4 Pro compatibility (TAS-XXX)
|
|
75
|
+
// M4 Pro has 12 cores (10 P + 2 E), M2 Pro has 8 cores (6 P + 2 E)
|
|
76
|
+
// Using 8 threads ensures consistent behavior across architectures
|
|
77
|
+
let runtime = tokio::runtime::Builder::new_multi_thread()
|
|
78
|
+
.worker_threads(8)
|
|
79
|
+
.thread_name("ruby-ffi-runtime")
|
|
80
|
+
.enable_all()
|
|
81
|
+
.build()
|
|
82
|
+
.map_err(|e| {
|
|
83
|
+
error!("Failed to create tokio runtime: {}", e);
|
|
84
|
+
Error::new(runtime_error_class(), "Runtime creation failed")
|
|
85
|
+
})?;
|
|
86
|
+
|
|
87
|
+
// TAS-65 Phase 2: Initialize telemetry in Tokio runtime context
|
|
88
|
+
runtime.block_on(async {
|
|
89
|
+
tasker_shared::logging::init_tracing();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Get global event system (shared singleton)
|
|
93
|
+
let event_system = get_global_event_system();
|
|
94
|
+
|
|
95
|
+
// Bootstrap the worker using tasker-worker foundation
|
|
96
|
+
let mut system_handle = runtime.block_on(async {
|
|
97
|
+
WorkerBootstrap::bootstrap_with_event_system(Some(event_system))
|
|
98
|
+
.await
|
|
99
|
+
.map_err(|e| {
|
|
100
|
+
error!("Failed to bootstrap worker system: {}", e);
|
|
101
|
+
Error::new(
|
|
102
|
+
runtime_error_class(),
|
|
103
|
+
format!("Worker bootstrap failed: {}", e),
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
})?;
|
|
107
|
+
|
|
108
|
+
info!("✅ Worker system bootstrapped successfully");
|
|
109
|
+
|
|
110
|
+
// TAS-67: Create domain event callback for step completion
|
|
111
|
+
// This MUST be done BEFORE taking dispatch handles
|
|
112
|
+
info!("🔔 Setting up step event publisher registry for domain events...");
|
|
113
|
+
let (domain_event_publisher, domain_event_callback) = runtime.block_on(async {
|
|
114
|
+
let worker_core = system_handle.worker_core.lock().await;
|
|
115
|
+
|
|
116
|
+
// Get the message client for durable events
|
|
117
|
+
let message_client = worker_core.context.message_client.clone();
|
|
118
|
+
let publisher = Arc::new(
|
|
119
|
+
tasker_shared::events::domain_events::DomainEventPublisher::new(message_client),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// TAS-67: Get EventRouter from WorkerCore for stats tracking
|
|
123
|
+
// TAS-173: Use ok_or_else instead of expect to prevent panic at FFI boundary
|
|
124
|
+
let event_router = worker_core.event_router().ok_or_else(|| {
|
|
125
|
+
error!("EventRouter not available from WorkerCore after bootstrap");
|
|
126
|
+
Error::new(
|
|
127
|
+
runtime_error_class(),
|
|
128
|
+
"EventRouter not available from WorkerCore",
|
|
129
|
+
)
|
|
130
|
+
})?;
|
|
131
|
+
|
|
132
|
+
// Create registry with EventRouter for dual-path delivery (durable + fast)
|
|
133
|
+
let step_event_registry =
|
|
134
|
+
StepEventPublisherRegistry::with_event_router(publisher.clone(), event_router);
|
|
135
|
+
|
|
136
|
+
let registry = Arc::new(RwLock::new(step_event_registry));
|
|
137
|
+
let callback = Arc::new(DomainEventCallback::new(registry));
|
|
138
|
+
|
|
139
|
+
Ok::<_, Error>((publisher, callback))
|
|
140
|
+
})?;
|
|
141
|
+
info!("✅ Domain event callback created with EventRouter for stats tracking");
|
|
142
|
+
|
|
143
|
+
// TAS-67: Take dispatch handles and create FfiDispatchChannel with callback
|
|
144
|
+
let ffi_dispatch_channel = if let Some(dispatch_handles) = system_handle.take_dispatch_handles()
|
|
145
|
+
{
|
|
146
|
+
info!("🔗 Creating FfiDispatchChannel from dispatch handles...");
|
|
147
|
+
|
|
148
|
+
// Create config with runtime handle for executing async callbacks from FFI threads
|
|
149
|
+
let config = FfiDispatchChannelConfig::new(runtime.handle().clone())
|
|
150
|
+
.with_service_id(worker_id_str.clone())
|
|
151
|
+
.with_completion_timeout(std::time::Duration::from_secs(30));
|
|
152
|
+
|
|
153
|
+
// TAS-125: Get database pool for checkpoint service
|
|
154
|
+
// TAS-78: database_pool() returns tasker pool (backward compatible)
|
|
155
|
+
let db_pool = runtime.block_on(async {
|
|
156
|
+
let worker_core = system_handle.worker_core.lock().await;
|
|
157
|
+
worker_core.context.database_pool().clone()
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// TAS-125: Create checkpoint service for batch processing handlers
|
|
161
|
+
let checkpoint_service = CheckpointService::new(db_pool);
|
|
162
|
+
|
|
163
|
+
let channel = FfiDispatchChannel::new(
|
|
164
|
+
dispatch_handles.dispatch_receiver,
|
|
165
|
+
dispatch_handles.completion_sender,
|
|
166
|
+
config,
|
|
167
|
+
domain_event_callback,
|
|
168
|
+
)
|
|
169
|
+
// TAS-125: Enable checkpoint support for batch processing
|
|
170
|
+
.with_checkpoint_support(checkpoint_service, dispatch_handles.dispatch_sender);
|
|
171
|
+
|
|
172
|
+
info!("✅ FfiDispatchChannel created with domain event callback and checkpoint support for Ruby step dispatch");
|
|
173
|
+
Arc::new(channel)
|
|
174
|
+
} else {
|
|
175
|
+
error!("Failed to get dispatch handles from WorkerSystemHandle");
|
|
176
|
+
return Err(Error::new(
|
|
177
|
+
runtime_error_class(),
|
|
178
|
+
"Dispatch handles not available",
|
|
179
|
+
));
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// TAS-65 Phase 4.1: Get in-process event receiver from WorkerCore's event bus
|
|
183
|
+
// IMPORTANT: We must use WorkerCore's bus, not create a new one, otherwise the
|
|
184
|
+
// sender is dropped when the local bus goes out of scope.
|
|
185
|
+
info!("⚡ Subscribing to WorkerCore's in-process event bus for fast domain events...");
|
|
186
|
+
let in_process_event_receiver = runtime.block_on(async {
|
|
187
|
+
let worker_core = system_handle.worker_core.lock().await;
|
|
188
|
+
// Get the in-process bus from WorkerCore and subscribe for FFI
|
|
189
|
+
let bus = worker_core.in_process_event_bus();
|
|
190
|
+
let bus_guard = bus.write().await;
|
|
191
|
+
bus_guard.subscribe_ffi()
|
|
192
|
+
});
|
|
193
|
+
info!("✅ Subscribed to WorkerCore's in-process event bus for FFI domain events");
|
|
194
|
+
|
|
195
|
+
// TAS-231: Create FFI client bridge for orchestration API access
|
|
196
|
+
info!("Creating FFI client bridge for orchestration API access...");
|
|
197
|
+
let ffi_client = runtime.block_on(async {
|
|
198
|
+
let worker_core = system_handle.worker_core.lock().await;
|
|
199
|
+
tasker_worker::create_ffi_client_bridge(&worker_core, runtime.handle().clone()).await
|
|
200
|
+
});
|
|
201
|
+
if ffi_client.is_some() {
|
|
202
|
+
info!("✅ FFI client bridge created successfully");
|
|
203
|
+
} else {
|
|
204
|
+
info!("FFI client bridge not available (orchestration client not configured)");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Store the bridge handle with FfiDispatchChannel
|
|
208
|
+
*handle_guard = Some(RubyBridgeHandle::new(
|
|
209
|
+
system_handle,
|
|
210
|
+
ffi_dispatch_channel,
|
|
211
|
+
domain_event_publisher,
|
|
212
|
+
Some(in_process_event_receiver),
|
|
213
|
+
ffi_client,
|
|
214
|
+
runtime,
|
|
215
|
+
));
|
|
216
|
+
|
|
217
|
+
// Return handle info to Ruby
|
|
218
|
+
let ruby = magnus::Ruby::get().map_err(|err| {
|
|
219
|
+
Error::new(
|
|
220
|
+
runtime_error_class(),
|
|
221
|
+
format!("Failed to get ruby system: {}", err),
|
|
222
|
+
)
|
|
223
|
+
})?;
|
|
224
|
+
let hash = ruby.hash_new();
|
|
225
|
+
hash.aset("handle_id", worker_id.to_string())?;
|
|
226
|
+
hash.aset("status", "started")?;
|
|
227
|
+
hash.aset("message", "Ruby worker system started successfully")?;
|
|
228
|
+
hash.aset("worker_id", worker_id_str)?;
|
|
229
|
+
|
|
230
|
+
Ok(hash.as_value())
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// Stop the worker system
|
|
234
|
+
pub fn stop_worker() -> Result<String, Error> {
|
|
235
|
+
let mut handle_guard = WORKER_SYSTEM.lock().map_err(|e| {
|
|
236
|
+
error!("Failed to acquire worker system lock: {}", e);
|
|
237
|
+
Error::new(runtime_error_class(), "Lock acquisition failed")
|
|
238
|
+
})?;
|
|
239
|
+
|
|
240
|
+
match handle_guard.as_mut() {
|
|
241
|
+
Some(handle) => {
|
|
242
|
+
handle.stop().map_err(|e| {
|
|
243
|
+
error!("Failed to stop worker system: {}", e);
|
|
244
|
+
Error::new(runtime_error_class(), e)
|
|
245
|
+
})?;
|
|
246
|
+
*handle_guard = None;
|
|
247
|
+
Ok("Worker system stopped".to_string())
|
|
248
|
+
}
|
|
249
|
+
None => Ok("Worker system not running".to_string()),
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Get worker system status
|
|
254
|
+
pub fn get_worker_status() -> Result<Value, Error> {
|
|
255
|
+
let handle_guard = WORKER_SYSTEM.lock().map_err(|e| {
|
|
256
|
+
error!("Failed to acquire worker system lock: {}", e);
|
|
257
|
+
Error::new(runtime_error_class(), "Lock acquisition failed")
|
|
258
|
+
})?;
|
|
259
|
+
|
|
260
|
+
let ruby = magnus::Ruby::get().map_err(|err| {
|
|
261
|
+
error!("Failed to get ruby system: {err}");
|
|
262
|
+
Error::new(runtime_error_class(), "Failed to get ruby system: {err}")
|
|
263
|
+
})?;
|
|
264
|
+
let hash = ruby.hash_new();
|
|
265
|
+
|
|
266
|
+
if let Some(handle) = handle_guard.as_ref() {
|
|
267
|
+
let runtime = handle.runtime_handle();
|
|
268
|
+
let status = runtime
|
|
269
|
+
.block_on(async { handle.status().await })
|
|
270
|
+
.map_err(|err| {
|
|
271
|
+
error!("Failed to get status from runtime: {err}");
|
|
272
|
+
Error::new(
|
|
273
|
+
runtime_error_class(),
|
|
274
|
+
"Failed to get status from runtime {err}",
|
|
275
|
+
)
|
|
276
|
+
})?;
|
|
277
|
+
|
|
278
|
+
hash.aset("running", status.running)?;
|
|
279
|
+
hash.aset("environment", status.environment)?;
|
|
280
|
+
hash.aset(
|
|
281
|
+
"worker_core_status",
|
|
282
|
+
format!("{:?}", status.worker_core_status),
|
|
283
|
+
)?;
|
|
284
|
+
hash.aset("web_api_enabled", status.web_api_enabled)?;
|
|
285
|
+
hash.aset("supported_namespaces", status.supported_namespaces)?;
|
|
286
|
+
hash.aset("database_pool_size", status.database_pool_size)?;
|
|
287
|
+
hash.aset("database_pool_idle", status.database_pool_idle)?;
|
|
288
|
+
} else {
|
|
289
|
+
hash.aset("running", false)?;
|
|
290
|
+
hash.aset("error", "Worker system not initialized")?;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Ok(hash.as_value())
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/// Transition to graceful shutdown
|
|
297
|
+
pub fn transition_to_graceful_shutdown() -> Result<String, Error> {
|
|
298
|
+
let handle_guard = WORKER_SYSTEM.lock().map_err(|e| {
|
|
299
|
+
error!("Failed to acquire worker system lock: {}", e);
|
|
300
|
+
Error::new(runtime_error_class(), "Lock acquisition failed")
|
|
301
|
+
})?;
|
|
302
|
+
|
|
303
|
+
let handle = handle_guard
|
|
304
|
+
.as_ref()
|
|
305
|
+
.ok_or_else(|| Error::new(runtime_error_class(), "Worker system not running"))?;
|
|
306
|
+
|
|
307
|
+
let runtime = handle.runtime_handle();
|
|
308
|
+
runtime.block_on(async {
|
|
309
|
+
let mut worker_core = handle.system_handle.worker_core.lock().await;
|
|
310
|
+
worker_core.stop().await.map_err(|e| {
|
|
311
|
+
error!("Failed to transition to graceful shutdown: {}", e);
|
|
312
|
+
Error::new(
|
|
313
|
+
runtime_error_class(),
|
|
314
|
+
format!("Graceful shutdown failed: {}", e),
|
|
315
|
+
)
|
|
316
|
+
})
|
|
317
|
+
})?;
|
|
318
|
+
|
|
319
|
+
Ok("Worker system transitioned to graceful shutdown".to_string())
|
|
320
|
+
}
|