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,292 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative 'observability/types'
|
|
5
|
+
|
|
6
|
+
module TaskerCore
|
|
7
|
+
# Observability - Access worker health, metrics, and configuration via FFI
|
|
8
|
+
#
|
|
9
|
+
# TAS-77: This module provides Ruby-friendly access to worker observability data
|
|
10
|
+
# without requiring the HTTP web API. Data is fetched directly from Rust via FFI.
|
|
11
|
+
#
|
|
12
|
+
# @example Check worker health
|
|
13
|
+
# health = TaskerCore::Observability.health_basic
|
|
14
|
+
# puts health.status # => "healthy"
|
|
15
|
+
# puts health.worker_id
|
|
16
|
+
#
|
|
17
|
+
# @example Get detailed health for K8s probes
|
|
18
|
+
# if TaskerCore::Observability.ready?
|
|
19
|
+
# puts "Worker ready to receive requests"
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example Get metrics
|
|
23
|
+
# events = TaskerCore::Observability.event_stats
|
|
24
|
+
# puts "Events routed: #{events.router.total_routed}"
|
|
25
|
+
#
|
|
26
|
+
# @example Query configuration
|
|
27
|
+
# config = TaskerCore::Observability.config
|
|
28
|
+
# puts "Environment: #{config.metadata.environment}"
|
|
29
|
+
#
|
|
30
|
+
# @example List templates
|
|
31
|
+
# templates_json = TaskerCore::Observability.templates_list
|
|
32
|
+
# templates = JSON.parse(templates_json)
|
|
33
|
+
#
|
|
34
|
+
module Observability
|
|
35
|
+
class << self
|
|
36
|
+
# ========================================================================
|
|
37
|
+
# Health Methods
|
|
38
|
+
# ========================================================================
|
|
39
|
+
|
|
40
|
+
# Get basic health status
|
|
41
|
+
#
|
|
42
|
+
# @return [Types::BasicHealth] Basic health response
|
|
43
|
+
def health_basic
|
|
44
|
+
json = TaskerCore::FFI.health_basic
|
|
45
|
+
data = JSON.parse(json)
|
|
46
|
+
Types::BasicHealth.new(
|
|
47
|
+
status: data['status'],
|
|
48
|
+
worker_id: data['worker_id'],
|
|
49
|
+
timestamp: data['timestamp']
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get liveness status (Kubernetes liveness probe)
|
|
54
|
+
#
|
|
55
|
+
# @return [Types::BasicHealth] Liveness response
|
|
56
|
+
def health_live
|
|
57
|
+
json = TaskerCore::FFI.health_live
|
|
58
|
+
data = JSON.parse(json)
|
|
59
|
+
Types::BasicHealth.new(
|
|
60
|
+
status: data['status'],
|
|
61
|
+
worker_id: data['worker_id'],
|
|
62
|
+
timestamp: data['timestamp']
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get readiness status (Kubernetes readiness probe)
|
|
67
|
+
#
|
|
68
|
+
# @return [Types::DetailedHealth] Detailed health with checks
|
|
69
|
+
def health_ready
|
|
70
|
+
json = TaskerCore::FFI.health_ready
|
|
71
|
+
parse_detailed_health(json)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Get detailed health information
|
|
75
|
+
#
|
|
76
|
+
# @return [Types::DetailedHealth] Comprehensive health data
|
|
77
|
+
def health_detailed
|
|
78
|
+
json = TaskerCore::FFI.health_detailed
|
|
79
|
+
parse_detailed_health(json)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Check if worker is ready to receive requests
|
|
83
|
+
#
|
|
84
|
+
# @return [Boolean] true if worker is ready
|
|
85
|
+
def ready?
|
|
86
|
+
health_ready.status == 'healthy'
|
|
87
|
+
rescue StandardError
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Check if worker is alive
|
|
92
|
+
#
|
|
93
|
+
# @return [Boolean] true if worker is alive
|
|
94
|
+
def alive?
|
|
95
|
+
# Liveness probe returns 'alive' status, not 'healthy'
|
|
96
|
+
health_live.status == 'alive'
|
|
97
|
+
rescue StandardError
|
|
98
|
+
false
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# ========================================================================
|
|
102
|
+
# Metrics Methods
|
|
103
|
+
# ========================================================================
|
|
104
|
+
|
|
105
|
+
# Get worker metrics as JSON string
|
|
106
|
+
#
|
|
107
|
+
# @return [String] JSON-formatted metrics
|
|
108
|
+
def metrics_worker
|
|
109
|
+
TaskerCore::FFI.metrics_worker
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Get domain event statistics
|
|
113
|
+
#
|
|
114
|
+
# @return [Types::DomainEventStats] Event routing statistics
|
|
115
|
+
def event_stats
|
|
116
|
+
json = TaskerCore::FFI.metrics_events
|
|
117
|
+
data = JSON.parse(json)
|
|
118
|
+
|
|
119
|
+
Types::DomainEventStats.new(
|
|
120
|
+
router: Types::EventRouterStats.new(
|
|
121
|
+
total_routed: data['router']['total_routed'],
|
|
122
|
+
durable_routed: data['router']['durable_routed'],
|
|
123
|
+
fast_routed: data['router']['fast_routed'],
|
|
124
|
+
broadcast_routed: data['router']['broadcast_routed'],
|
|
125
|
+
fast_delivery_errors: data['router']['fast_delivery_errors'],
|
|
126
|
+
routing_errors: data['router']['routing_errors']
|
|
127
|
+
),
|
|
128
|
+
in_process_bus: Types::InProcessEventBusStats.new(
|
|
129
|
+
total_events_dispatched: data['in_process_bus']['total_events_dispatched'],
|
|
130
|
+
rust_handler_dispatches: data['in_process_bus']['rust_handler_dispatches'],
|
|
131
|
+
ffi_channel_dispatches: data['in_process_bus']['ffi_channel_dispatches'],
|
|
132
|
+
rust_handler_errors: data['in_process_bus']['rust_handler_errors'],
|
|
133
|
+
ffi_channel_drops: data['in_process_bus']['ffi_channel_drops'],
|
|
134
|
+
rust_subscriber_patterns: data['in_process_bus']['rust_subscriber_patterns'],
|
|
135
|
+
rust_handler_count: data['in_process_bus']['rust_handler_count'],
|
|
136
|
+
ffi_subscriber_count: data['in_process_bus']['ffi_subscriber_count']
|
|
137
|
+
),
|
|
138
|
+
captured_at: data['captured_at'],
|
|
139
|
+
worker_id: data['worker_id']
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Get Prometheus-formatted metrics
|
|
144
|
+
#
|
|
145
|
+
# @return [String] Prometheus text format metrics
|
|
146
|
+
def prometheus_metrics
|
|
147
|
+
TaskerCore::FFI.metrics_prometheus
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# ========================================================================
|
|
151
|
+
# Template Methods
|
|
152
|
+
# ========================================================================
|
|
153
|
+
|
|
154
|
+
# List all templates as JSON
|
|
155
|
+
#
|
|
156
|
+
# @param include_cache_stats [Boolean] Include cache statistics
|
|
157
|
+
# @return [String] JSON-formatted template list
|
|
158
|
+
def templates_list(include_cache_stats: false)
|
|
159
|
+
TaskerCore::FFI.templates_list(include_cache_stats)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Get a specific template as JSON
|
|
163
|
+
#
|
|
164
|
+
# @param namespace [String] Template namespace
|
|
165
|
+
# @param name [String] Template name
|
|
166
|
+
# @param version [String] Template version
|
|
167
|
+
# @return [String] JSON-formatted template
|
|
168
|
+
def template_get(namespace:, name:, version:)
|
|
169
|
+
TaskerCore::FFI.template_get(namespace, name, version)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Validate a template for worker execution
|
|
173
|
+
#
|
|
174
|
+
# @param namespace [String] Template namespace
|
|
175
|
+
# @param name [String] Template name
|
|
176
|
+
# @param version [String] Template version
|
|
177
|
+
# @return [Types::TemplateValidation] Validation result
|
|
178
|
+
def template_validate(namespace:, name:, version:)
|
|
179
|
+
json = TaskerCore::FFI.template_validate(namespace, name, version)
|
|
180
|
+
data = JSON.parse(json)
|
|
181
|
+
|
|
182
|
+
Types::TemplateValidation.new(
|
|
183
|
+
valid: data['valid'],
|
|
184
|
+
namespace: data['namespace'],
|
|
185
|
+
name: data['name'],
|
|
186
|
+
version: data['version'],
|
|
187
|
+
handler_count: data['handler_count'],
|
|
188
|
+
issues: data['issues'] || [],
|
|
189
|
+
handler_metadata: data['handler_metadata']
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Get template cache statistics
|
|
194
|
+
#
|
|
195
|
+
# @return [Types::CacheStats] Cache statistics
|
|
196
|
+
def cache_stats
|
|
197
|
+
json = TaskerCore::FFI.templates_cache_stats
|
|
198
|
+
data = JSON.parse(json)
|
|
199
|
+
|
|
200
|
+
# Map Rust field names to Ruby struct names
|
|
201
|
+
Types::CacheStats.new(
|
|
202
|
+
total_entries: data['total_cached'] || 0,
|
|
203
|
+
hits: data['cache_hits'] || 0,
|
|
204
|
+
misses: data['cache_misses'] || 0,
|
|
205
|
+
evictions: data['cache_evictions'] || 0,
|
|
206
|
+
last_maintenance: nil # Not provided by Rust API
|
|
207
|
+
)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# TAS-169: Removed cache_clear and template_refresh methods.
|
|
211
|
+
# Cache operations are now internal-only. Restart worker to refresh templates.
|
|
212
|
+
# Distributed cache status is available via health_detailed.
|
|
213
|
+
|
|
214
|
+
# ========================================================================
|
|
215
|
+
# Config Methods
|
|
216
|
+
# ========================================================================
|
|
217
|
+
|
|
218
|
+
# Get runtime configuration (safe fields only, no secrets)
|
|
219
|
+
#
|
|
220
|
+
# @return [Types::RuntimeConfig] Configuration data
|
|
221
|
+
def config
|
|
222
|
+
json = TaskerCore::FFI.config_runtime
|
|
223
|
+
data = JSON.parse(json)
|
|
224
|
+
|
|
225
|
+
Types::RuntimeConfig.new(
|
|
226
|
+
metadata: Types::ConfigMetadata.new(
|
|
227
|
+
timestamp: data['metadata']['timestamp'],
|
|
228
|
+
environment: data['metadata']['environment'],
|
|
229
|
+
version: data['metadata']['version']
|
|
230
|
+
),
|
|
231
|
+
worker_id: data['worker_id'],
|
|
232
|
+
worker_type: data['worker_type'],
|
|
233
|
+
auth: Types::SafeAuthConfig.new(
|
|
234
|
+
enabled: data['auth']['enabled'],
|
|
235
|
+
verification_method: data['auth']['verification_method'],
|
|
236
|
+
jwt_issuer: data['auth']['jwt_issuer'],
|
|
237
|
+
jwt_audience: data['auth']['jwt_audience'],
|
|
238
|
+
api_key_header: data['auth']['api_key_header'],
|
|
239
|
+
api_key_count: data['auth']['api_key_count'],
|
|
240
|
+
strict_validation: data['auth']['strict_validation'],
|
|
241
|
+
allowed_algorithms: data['auth']['allowed_algorithms']
|
|
242
|
+
),
|
|
243
|
+
messaging: Types::SafeMessagingConfig.new(
|
|
244
|
+
backend: data['messaging']['backend'],
|
|
245
|
+
queues: data['messaging']['queues']
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Get the current environment name
|
|
251
|
+
#
|
|
252
|
+
# @return [String] Environment name
|
|
253
|
+
def environment
|
|
254
|
+
TaskerCore::FFI.config_environment
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
# Parse detailed health JSON into typed struct
|
|
260
|
+
def parse_detailed_health(json)
|
|
261
|
+
data = JSON.parse(json)
|
|
262
|
+
|
|
263
|
+
checks = data['checks'].transform_values do |check|
|
|
264
|
+
Types::HealthCheck.new(
|
|
265
|
+
status: check['status'],
|
|
266
|
+
message: check['message'],
|
|
267
|
+
duration_ms: check['duration_ms'],
|
|
268
|
+
last_checked: check['last_checked']
|
|
269
|
+
)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
system_info = Types::WorkerSystemInfo.new(
|
|
273
|
+
version: data['system_info']['version'],
|
|
274
|
+
environment: data['system_info']['environment'],
|
|
275
|
+
uptime_seconds: data['system_info']['uptime_seconds'],
|
|
276
|
+
worker_type: data['system_info']['worker_type'],
|
|
277
|
+
database_pool_size: data['system_info']['database_pool_size'],
|
|
278
|
+
command_processor_active: data['system_info']['command_processor_active'],
|
|
279
|
+
supported_namespaces: data['system_info']['supported_namespaces']
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
Types::DetailedHealth.new(
|
|
283
|
+
status: data['status'],
|
|
284
|
+
timestamp: data['timestamp'],
|
|
285
|
+
worker_id: data['worker_id'],
|
|
286
|
+
checks: checks,
|
|
287
|
+
system_info: system_info
|
|
288
|
+
)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|