hatchet-sdk 0.5.0 → 0.7.0
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/CHANGELOG.md +14 -0
- data/lib/hatchet/batch.rb +58 -0
- data/lib/hatchet/clients/grpc/dispatcher.rb +42 -0
- data/lib/hatchet/clients/rest/.openapi-generator/FILES +9 -0
- data/lib/hatchet/clients/rest/README.md +16 -3
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/api/filter_api.rb +4 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/api/operator_api.rb +405 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/api/webhook_api.rb +2 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/api/worker_api.rb +3 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/api_meta.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/create_tenant_invite_request.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/pause_workflow_request.rb +105 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/pause_workflow_request_pause.rb +343 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/pause_workflow_request_unpause.rb +262 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/tenant_invite.rb +11 -1
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/tenant_member.rb +11 -1
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/tenant_member_role.rb +2 -1
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/update_tenant_invite_request.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/update_tenant_member_request.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_create_http_operator_request.rb +346 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_event.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_http_operator.rb +372 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_http_operator_list.rb +231 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_task_event_type.rb +3 -1
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_task_summary.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_update_http_operator_request.rb +252 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/v1_workflow_run.rb +14 -4
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/workflow.rb +43 -1
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/workflow_pause_scheduled_cron_run_queue_behavior.rb +40 -0
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest/models/workflow_update_request.rb +7 -8
- data/lib/hatchet/clients/rest/lib/hatchet-sdk-rest.rb +9 -0
- data/lib/hatchet/concurrency.rb +5 -1
- data/lib/hatchet/context.rb +42 -3
- data/lib/hatchet/contracts/dispatcher/dispatcher_pb.rb +4 -1
- data/lib/hatchet/contracts/dispatcher/dispatcher_services_pb.rb +3 -0
- data/lib/hatchet/contracts/v1/workflows_pb.rb +2 -1
- data/lib/hatchet/durable_context.rb +5 -12
- data/lib/hatchet/features/cel.rb +2 -2
- data/lib/hatchet/features/cron.rb +5 -5
- data/lib/hatchet/features/events.rb +7 -7
- data/lib/hatchet/features/filters.rb +7 -7
- data/lib/hatchet/features/logs.rb +2 -2
- data/lib/hatchet/features/metrics.rb +6 -6
- data/lib/hatchet/features/rate_limits.rb +2 -2
- data/lib/hatchet/features/runs.rb +4 -5
- data/lib/hatchet/features/scheduled.rb +7 -7
- data/lib/hatchet/features/tenant.rb +2 -2
- data/lib/hatchet/features/workers.rb +5 -5
- data/lib/hatchet/features/workflows.rb +6 -6
- data/lib/hatchet/task.rb +13 -0
- data/lib/hatchet/version.rb +1 -1
- data/lib/hatchet/worker/runner.rb +113 -0
- data/lib/hatchet/workflow.rb +77 -33
- data/lib/hatchet-sdk.rb +86 -28
- data/sig/hatchet/batch.rbs +12 -0
- data/sig/hatchet/task.rbs +2 -0
- metadata +13 -2
data/lib/hatchet/task.rb
CHANGED
|
@@ -68,6 +68,9 @@ module Hatchet
|
|
|
68
68
|
# @return [Hatchet::EvictionPolicy, nil] Eviction policy for durable tasks
|
|
69
69
|
attr_reader :eviction_policy
|
|
70
70
|
|
|
71
|
+
# @return [Hatchet::BatchTaskConfig, nil] Batch configuration, if this is a batch task
|
|
72
|
+
attr_reader :batch
|
|
73
|
+
|
|
71
74
|
# @return [Proc, nil] The task execution block
|
|
72
75
|
attr_reader :fn
|
|
73
76
|
|
|
@@ -112,6 +115,7 @@ module Hatchet
|
|
|
112
115
|
skip_if: [],
|
|
113
116
|
durable: false,
|
|
114
117
|
eviction_policy: nil,
|
|
118
|
+
batch: nil,
|
|
115
119
|
workflow: nil,
|
|
116
120
|
client: nil,
|
|
117
121
|
deps: nil,
|
|
@@ -131,6 +135,7 @@ module Hatchet
|
|
|
131
135
|
@skip_if = skip_if
|
|
132
136
|
@durable = durable
|
|
133
137
|
@eviction_policy = eviction_policy
|
|
138
|
+
@batch = batch
|
|
134
139
|
@workflow = workflow
|
|
135
140
|
@client = client
|
|
136
141
|
@deps = deps
|
|
@@ -191,6 +196,14 @@ module Hatchet
|
|
|
191
196
|
|
|
192
197
|
opts[:is_durable] = @durable
|
|
193
198
|
|
|
199
|
+
# Batch tasks buffer many concurrent runs into a single execution; per-item retry
|
|
200
|
+
# semantics don't apply, so retries is always forced to 0, regardless of any retries
|
|
201
|
+
# the caller may have supplied.
|
|
202
|
+
if @batch
|
|
203
|
+
opts[:batch] = @batch.to_proto
|
|
204
|
+
opts[:retries] = 0
|
|
205
|
+
end
|
|
206
|
+
|
|
194
207
|
::V1::CreateTaskOpts.new(**opts)
|
|
195
208
|
end
|
|
196
209
|
|
data/lib/hatchet/version.rb
CHANGED
|
@@ -226,6 +226,11 @@ module Hatchet
|
|
|
226
226
|
end
|
|
227
227
|
|
|
228
228
|
def execute_task(action)
|
|
229
|
+
if batch_action?(action)
|
|
230
|
+
execute_batch_task(action)
|
|
231
|
+
return
|
|
232
|
+
end
|
|
233
|
+
|
|
229
234
|
action_key = nil
|
|
230
235
|
prepare_action_execution(action)
|
|
231
236
|
|
|
@@ -329,6 +334,114 @@ module Hatchet
|
|
|
329
334
|
send_result(action, result)
|
|
330
335
|
end
|
|
331
336
|
|
|
337
|
+
def batch_action?(action)
|
|
338
|
+
action.respond_to?(:action_type) && action.action_type == :START_BATCH
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Handles a START_BATCH action: invokes the registered batch task block once with
|
|
342
|
+
# every buffered item, then reports completion/failure back per-member (or, for
|
|
343
|
+
# broadcast batch tasks, the same result for every member) via send_batch_action_event.
|
|
344
|
+
def execute_batch_task(action)
|
|
345
|
+
task_key = action.action_id.downcase
|
|
346
|
+
task = @task_map[task_key]
|
|
347
|
+
|
|
348
|
+
unless task
|
|
349
|
+
@logger.error("No task found for action: #{task_key}")
|
|
350
|
+
return
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
batch_items = parse_batch_items(action)
|
|
354
|
+
send_batch_started(action, batch_items.keys)
|
|
355
|
+
|
|
356
|
+
ctx = build_context(action, task)
|
|
357
|
+
|
|
358
|
+
begin
|
|
359
|
+
result = task.call(batch_items, ctx)
|
|
360
|
+
|
|
361
|
+
# If the handler cancelled the batch (ctx.cancel), a CANCELLED batch event
|
|
362
|
+
# covering every member was already sent from within cancel itself. Don't also
|
|
363
|
+
# send a COMPLETED event for the same members afterward.
|
|
364
|
+
send_batch_result(action, task, batch_items, result) unless ctx.cancelled?
|
|
365
|
+
rescue StandardError => e
|
|
366
|
+
@logger.error("Error in batch task #{action.action_id}: #{e.message}")
|
|
367
|
+
send_batch_failure_for_all(action, batch_items.keys, e)
|
|
368
|
+
end
|
|
369
|
+
ensure
|
|
370
|
+
ContextVars.clear
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def send_batch_started(action, member_ids)
|
|
374
|
+
items = member_ids.map { |id| { task_run_external_id: id } }
|
|
375
|
+
@dispatcher_client.send_batch_action_event(action: action, event_type: :STEP_EVENT_TYPE_STARTED, items: items)
|
|
376
|
+
rescue StandardError => e
|
|
377
|
+
@logger.warn("Failed to send batch STARTED event: #{e.message}")
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def send_batch_result(action, task, batch_items, result)
|
|
381
|
+
if task.batch.broadcast_output
|
|
382
|
+
payload = JSON.generate(result.nil? ? {} : result)
|
|
383
|
+
items = batch_items.keys.map { |id| { task_run_external_id: id, event_payload: payload } }
|
|
384
|
+
@dispatcher_client.send_batch_action_event(action: action, event_type: :STEP_EVENT_TYPE_COMPLETED, items: items)
|
|
385
|
+
return
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
unless result.is_a?(Hash)
|
|
389
|
+
send_batch_failure_for_all(
|
|
390
|
+
action, batch_items.keys,
|
|
391
|
+
StandardError.new("batch task handler must return a Hash keyed by batch member id"),
|
|
392
|
+
)
|
|
393
|
+
return
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
missing = batch_items.keys - result.keys
|
|
397
|
+
extra = result.keys - batch_items.keys
|
|
398
|
+
|
|
399
|
+
if !missing.empty? || !extra.empty?
|
|
400
|
+
send_batch_failure_for_all(
|
|
401
|
+
action, batch_items.keys,
|
|
402
|
+
StandardError.new("batch task handler result keys do not match batch member ids (missing=#{missing}, extra=#{extra})"),
|
|
403
|
+
)
|
|
404
|
+
return
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
completed_items = []
|
|
408
|
+
failed_items = []
|
|
409
|
+
|
|
410
|
+
result.each do |id, value|
|
|
411
|
+
completed_items << { task_run_external_id: id, event_payload: JSON.generate(value) }
|
|
412
|
+
rescue StandardError => e
|
|
413
|
+
failed_items << { task_run_external_id: id, event_payload: JSON.generate({ "error" => e.message }) }
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
unless completed_items.empty?
|
|
417
|
+
@dispatcher_client.send_batch_action_event(action: action, event_type: :STEP_EVENT_TYPE_COMPLETED, items: completed_items)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
return if failed_items.empty?
|
|
421
|
+
|
|
422
|
+
@dispatcher_client.send_batch_action_event(action: action, event_type: :STEP_EVENT_TYPE_FAILED, items: failed_items)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def send_batch_failure_for_all(action, member_ids, error)
|
|
426
|
+
payload = JSON.generate({ "error" => error.message })
|
|
427
|
+
items = member_ids.map { |id| { task_run_external_id: id, event_payload: payload, should_not_retry: true } }
|
|
428
|
+
@dispatcher_client.send_batch_action_event(action: action, event_type: :STEP_EVENT_TYPE_FAILED, items: items)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def parse_batch_items(action)
|
|
432
|
+
raw = action.respond_to?(:action_payload) ? action.action_payload : nil
|
|
433
|
+
return {} if raw.nil? || raw.to_s.empty?
|
|
434
|
+
|
|
435
|
+
parsed = JSON.parse(raw)
|
|
436
|
+
return {} unless parsed.is_a?(Hash)
|
|
437
|
+
|
|
438
|
+
parsed.each_with_object({}) do |(id, item), acc|
|
|
439
|
+
acc[id] = item.is_a?(Hash) && item["payload"].is_a?(Hash) ? (item["payload"]["input"] || {}) : {}
|
|
440
|
+
end
|
|
441
|
+
rescue JSON::ParserError
|
|
442
|
+
{}
|
|
443
|
+
end
|
|
444
|
+
|
|
332
445
|
def action_key_for(action)
|
|
333
446
|
"#{action.task_run_external_id}/#{action.retry_count}"
|
|
334
447
|
end
|
data/lib/hatchet/workflow.rb
CHANGED
|
@@ -98,10 +98,23 @@ module Hatchet
|
|
|
98
98
|
@id ||= resolve_workflow_id
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
# Define a task within this workflow
|
|
101
|
+
# Define a task within this workflow. The block receives the workflow input
|
|
102
|
+
# and a {Context} object, and its return value (a Hash) becomes the task
|
|
103
|
+
# output.
|
|
102
104
|
#
|
|
103
|
-
# @param name [Symbol, String]
|
|
104
|
-
# @
|
|
105
|
+
# @param name [Symbol, String] The name of the task
|
|
106
|
+
# @option opts [Array<Task, Symbol>] :parents ([]) A list of tasks that are parents of the task. Note: parents must be defined before their children
|
|
107
|
+
# @option opts [Integer, String, nil] :execution_timeout (nil) The maximum time to wait for the task to complete, in seconds or as a duration string (e.g. "60s")
|
|
108
|
+
# @option opts [Integer, String, nil] :schedule_timeout (nil) The maximum time to wait for the task to be scheduled
|
|
109
|
+
# @option opts [Integer, nil] :retries (nil) The number of times to retry the task before failing
|
|
110
|
+
# @option opts [Float, nil] :backoff_factor (nil) The backoff factor for controlling exponential backoff in retries
|
|
111
|
+
# @option opts [Integer, nil] :backoff_max_seconds (nil) The maximum number of seconds to allow retries with exponential backoff to continue
|
|
112
|
+
# @option opts [Array<RateLimit>] :rate_limits ([]) A list of rate limit configurations for the task
|
|
113
|
+
# @option opts [ConcurrencyExpression, Array<ConcurrencyExpression>, nil] :concurrency (nil) A concurrency expression (or list of them) controlling the concurrency settings for this task
|
|
114
|
+
# @option opts [Hash, nil] :desired_worker_labels (nil) A hash of desired worker labels that determine to which worker the task should be assigned
|
|
115
|
+
# @option opts [Array] :wait_for ([]) A list of conditions that must be met before the task can run
|
|
116
|
+
# @option opts [Array] :skip_if ([]) A list of conditions that, if met, will cause the task to be skipped
|
|
117
|
+
# @option opts [Hash, nil] :deps (nil) Dependency providers to inject into the task's context
|
|
105
118
|
# @yield [input, ctx] The task execution block
|
|
106
119
|
# @return [Task] The created task
|
|
107
120
|
def task(name, **opts, &)
|
|
@@ -130,6 +143,25 @@ module Hatchet
|
|
|
130
143
|
task(name, durable: true, eviction_policy: eviction_policy, **opts, &)
|
|
131
144
|
end
|
|
132
145
|
|
|
146
|
+
# Define a batch task within this workflow.
|
|
147
|
+
#
|
|
148
|
+
# Batch tasks buffer concurrent runs until Hatchet flushes the batch (size reached or
|
|
149
|
+
# flush interval), then invoke the block once with all buffered inputs keyed by each
|
|
150
|
+
# run's task-run external id. The block must return a Hash mapping each id to its
|
|
151
|
+
# output, or use +broadcast_output+ on the batch config to return the same result to
|
|
152
|
+
# all callers. retries is always forced to 0 for batch tasks.
|
|
153
|
+
#
|
|
154
|
+
# Preview: batch tasks are in beta and may change in future releases.
|
|
155
|
+
#
|
|
156
|
+
# @param name [Symbol, String] Task name
|
|
157
|
+
# @param batch [Hatchet::BatchTaskConfig] Batch configuration
|
|
158
|
+
# @param opts [Hash] Other Task options forwarded to {#task}.
|
|
159
|
+
# @yield [inputs, ctx] The batch execution block, receiving a Hash of task-run external id => input
|
|
160
|
+
# @return [Task] The created batch task
|
|
161
|
+
def batch_task(name, batch:, **opts, &)
|
|
162
|
+
task(name, batch: batch, **opts, &)
|
|
163
|
+
end
|
|
164
|
+
|
|
133
165
|
# Define an on_failure task for this workflow
|
|
134
166
|
#
|
|
135
167
|
# @param opts [Hash] Task options
|
|
@@ -219,55 +251,65 @@ module Hatchet
|
|
|
219
251
|
::V1::CreateWorkflowVersionRequest.new(**args)
|
|
220
252
|
end
|
|
221
253
|
|
|
222
|
-
# Run this workflow synchronously
|
|
254
|
+
# Run this workflow synchronously and wait for it to complete.
|
|
223
255
|
#
|
|
224
|
-
# @param input [Hash]
|
|
225
|
-
# @param options [TriggerWorkflowOptions, nil]
|
|
226
|
-
# @return [Hash] The workflow run output
|
|
256
|
+
# @param input [Hash] The input data for the workflow
|
|
257
|
+
# @param options [TriggerWorkflowOptions, nil] Additional options for workflow execution, such as +additional_metadata:+ and +priority:+
|
|
258
|
+
# @return [Hash] The workflow run output, keyed by task name (e.g. `{"step1" => {...}, "step2" => {...}}`)
|
|
259
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
260
|
+
# @raise [Hatchet::FailedRunError] If the workflow run failed
|
|
227
261
|
def run(input = {}, options: nil)
|
|
228
262
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
229
263
|
|
|
230
264
|
@client.admin.trigger_workflow(self, input, options: options)
|
|
231
265
|
end
|
|
232
266
|
|
|
233
|
-
#
|
|
267
|
+
# Trigger a workflow run without waiting for it to complete. Useful for
|
|
268
|
+
# starting a run and immediately returning a reference to it without
|
|
269
|
+
# blocking while the workflow runs.
|
|
234
270
|
#
|
|
235
|
-
# @param input [Hash]
|
|
236
|
-
# @param options [TriggerWorkflowOptions, nil]
|
|
237
|
-
# @return [WorkflowRunRef]
|
|
271
|
+
# @param input [Hash] The input data for the workflow
|
|
272
|
+
# @param options [TriggerWorkflowOptions, nil] Additional options for workflow execution
|
|
273
|
+
# @return [WorkflowRunRef] A reference to the workflow run, whose +result+ method blocks until the run completes
|
|
274
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
238
275
|
def run_no_wait(input = {}, options: nil)
|
|
239
276
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
240
277
|
|
|
241
278
|
@client.admin.trigger_workflow_no_wait(self, input, options: options)
|
|
242
279
|
end
|
|
243
280
|
|
|
244
|
-
# Run
|
|
281
|
+
# Run this workflow in bulk and wait for all runs to complete. Runs are
|
|
282
|
+
# triggered via bulk gRPC triggering (batched by 1000) and results are
|
|
283
|
+
# collected concurrently.
|
|
245
284
|
#
|
|
246
|
-
# @param items [Array<Hash>]
|
|
247
|
-
# @param return_exceptions [Boolean]
|
|
248
|
-
# @return [Array]
|
|
285
|
+
# @param items [Array<Hash>] A list of bulk run items, as created by {#create_bulk_run_item}
|
|
286
|
+
# @param return_exceptions [Boolean] If +true+, exceptions are returned as part of the results instead of being raised
|
|
287
|
+
# @return [Array] A list of results for each workflow run
|
|
288
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
249
289
|
def run_many(items, return_exceptions: false)
|
|
250
290
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
251
291
|
|
|
252
292
|
@client.admin.trigger_workflow_many(self, items, return_exceptions: return_exceptions)
|
|
253
293
|
end
|
|
254
294
|
|
|
255
|
-
# Run
|
|
295
|
+
# Run this workflow in bulk without waiting for the runs to complete.
|
|
256
296
|
#
|
|
257
|
-
# @param items [Array<Hash>]
|
|
258
|
-
# @return [Array<WorkflowRunRef>]
|
|
297
|
+
# @param items [Array<Hash>] A list of bulk run items, as created by {#create_bulk_run_item}
|
|
298
|
+
# @return [Array<WorkflowRunRef>] A list of references to the triggered workflow runs
|
|
299
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
259
300
|
def run_many_no_wait(items)
|
|
260
301
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
261
302
|
|
|
262
303
|
@client.admin.trigger_workflow_many_no_wait(self, items)
|
|
263
304
|
end
|
|
264
305
|
|
|
265
|
-
# Create a bulk run item for
|
|
306
|
+
# Create a bulk run item for this workflow, intended to be used with the
|
|
307
|
+
# {#run_many} methods.
|
|
266
308
|
#
|
|
267
|
-
# @param input [Hash]
|
|
268
|
-
# @param key [String, nil]
|
|
269
|
-
# @param options [TriggerWorkflowOptions, nil]
|
|
270
|
-
# @return [Hash]
|
|
309
|
+
# @param input [Hash] The input data for the workflow
|
|
310
|
+
# @param key [String, nil] The key for the workflow run, used for identification and deduplication
|
|
311
|
+
# @param options [TriggerWorkflowOptions, nil] Additional options for the workflow run
|
|
312
|
+
# @return [Hash] A bulk run item that can be passed to the +run_many+ methods
|
|
271
313
|
def create_bulk_run_item(input: {}, key: nil, options: nil)
|
|
272
314
|
item = { input: input }
|
|
273
315
|
item[:key] = key if key
|
|
@@ -275,24 +317,26 @@ module Hatchet
|
|
|
275
317
|
item
|
|
276
318
|
end
|
|
277
319
|
|
|
278
|
-
# Schedule this workflow
|
|
320
|
+
# Schedule this workflow to run at a specific time.
|
|
279
321
|
#
|
|
280
|
-
# @param time [Time] When to execute
|
|
281
|
-
# @param input [Hash]
|
|
282
|
-
# @param options [ScheduleTriggerWorkflowOptions, nil]
|
|
283
|
-
# @return [Object]
|
|
322
|
+
# @param time [Time] When to execute the workflow
|
|
323
|
+
# @param input [Hash] The input data for the workflow
|
|
324
|
+
# @param options [ScheduleTriggerWorkflowOptions, nil] Additional schedule options
|
|
325
|
+
# @return [Object] The schedule response from the Hatchet engine
|
|
326
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
284
327
|
def schedule(time, input: {}, options: nil)
|
|
285
328
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
286
329
|
|
|
287
330
|
@client.admin.schedule_workflow(self, time, input: input, options: options)
|
|
288
331
|
end
|
|
289
332
|
|
|
290
|
-
# Create a cron trigger for this workflow
|
|
333
|
+
# Create a cron trigger for this workflow.
|
|
291
334
|
#
|
|
292
|
-
# @param cron_name [String]
|
|
293
|
-
# @param expression [String]
|
|
294
|
-
# @param input [Hash]
|
|
295
|
-
# @return [Object]
|
|
335
|
+
# @param cron_name [String] The name of the cron job
|
|
336
|
+
# @param expression [String] The cron expression that defines the schedule
|
|
337
|
+
# @param input [Hash] The input data for the workflow
|
|
338
|
+
# @return [Object] The created cron workflow trigger
|
|
339
|
+
# @raise [Hatchet::Error] If no client is associated with the workflow
|
|
296
340
|
def create_cron(cron_name, expression, input: {})
|
|
297
341
|
raise Error, "No client associated with workflow #{@name}" unless @client
|
|
298
342
|
|
data/lib/hatchet-sdk.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Hatchet
|
|
|
31
31
|
require_relative "hatchet/conditions"
|
|
32
32
|
require_relative "hatchet/condition_converter"
|
|
33
33
|
require_relative "hatchet/rate_limit"
|
|
34
|
+
require_relative "hatchet/batch"
|
|
34
35
|
require_relative "hatchet/labels"
|
|
35
36
|
require_relative "hatchet/trigger_options"
|
|
36
37
|
require_relative "hatchet/default_filter"
|
|
@@ -112,83 +113,103 @@ module Hatchet
|
|
|
112
113
|
@rest_client ||= Hatchet::Clients.rest_client(@config)
|
|
113
114
|
end
|
|
114
115
|
|
|
115
|
-
#
|
|
116
|
+
# The events client, which you can use to push events to Hatchet to trigger
|
|
117
|
+
# event-driven workflows.
|
|
116
118
|
# @return [Hatchet::Features::Events]
|
|
117
119
|
def events
|
|
118
120
|
@events ||= Hatchet::Features::Events.new(rest_client, event_grpc, @config)
|
|
119
121
|
end
|
|
120
122
|
|
|
121
|
-
#
|
|
123
|
+
# The runs client is a client for interacting with task and workflow runs
|
|
124
|
+
# within Hatchet.
|
|
122
125
|
# @return [Hatchet::Features::Runs]
|
|
123
126
|
def runs
|
|
124
127
|
@runs ||= Hatchet::Features::Runs.new(rest_client, @config, client: self)
|
|
125
128
|
end
|
|
126
129
|
|
|
127
|
-
#
|
|
130
|
+
# The tenant client is a client for reading information about the tenant
|
|
131
|
+
# you're operating in.
|
|
128
132
|
# @return [Hatchet::Features::Tenant]
|
|
129
133
|
def tenant
|
|
130
134
|
@tenant ||= Hatchet::Features::Tenant.new(rest_client, @config)
|
|
131
135
|
end
|
|
132
136
|
|
|
133
|
-
#
|
|
137
|
+
# The logs client is a client for interacting with Hatchet's logs API.
|
|
134
138
|
# @return [Hatchet::Features::Logs]
|
|
135
139
|
def logs
|
|
136
140
|
@logs ||= Hatchet::Features::Logs.new(rest_client, @config)
|
|
137
141
|
end
|
|
138
142
|
|
|
139
|
-
#
|
|
143
|
+
# The workers client is a client for managing workers programmatically
|
|
144
|
+
# within Hatchet.
|
|
140
145
|
# @return [Hatchet::Features::Workers]
|
|
141
146
|
def workers
|
|
142
147
|
@workers ||= Hatchet::Features::Workers.new(rest_client, @config)
|
|
143
148
|
end
|
|
144
149
|
|
|
145
|
-
#
|
|
150
|
+
# The CEL client is a client for debugging CEL expressions within Hatchet.
|
|
146
151
|
# @return [Hatchet::Features::CEL]
|
|
147
152
|
def cel
|
|
148
153
|
@cel ||= Hatchet::Features::CEL.new(rest_client, @config)
|
|
149
154
|
end
|
|
150
155
|
|
|
151
|
-
#
|
|
156
|
+
# The workflows client is a client for managing workflow declarations
|
|
157
|
+
# programmatically within Hatchet. Note that workflows are the declaration,
|
|
158
|
+
# _not_ the individual runs; if you're looking for runs, use the runs
|
|
159
|
+
# client instead.
|
|
152
160
|
# @return [Hatchet::Features::Workflows]
|
|
153
161
|
def workflows
|
|
154
162
|
@workflows ||= Hatchet::Features::Workflows.new(rest_client, @config)
|
|
155
163
|
end
|
|
156
164
|
|
|
157
|
-
#
|
|
165
|
+
# The filters client is a client for managing filters within Hatchet, which
|
|
166
|
+
# scope event triggers to workflows using CEL expressions.
|
|
158
167
|
# @return [Hatchet::Features::Filters]
|
|
159
168
|
def filters
|
|
160
169
|
@filters ||= Hatchet::Features::Filters.new(rest_client, @config)
|
|
161
170
|
end
|
|
162
171
|
|
|
163
|
-
#
|
|
172
|
+
# The metrics client is a client for reading metrics out of Hatchet's
|
|
173
|
+
# metrics API.
|
|
164
174
|
# @return [Hatchet::Features::Metrics]
|
|
165
175
|
def metrics
|
|
166
176
|
@metrics ||= Hatchet::Features::Metrics.new(rest_client, @config)
|
|
167
177
|
end
|
|
168
178
|
|
|
169
|
-
#
|
|
179
|
+
# The rate limits client is a wrapper for Hatchet's gRPC API that makes it
|
|
180
|
+
# easier to work with rate limits in Hatchet.
|
|
170
181
|
# @return [Hatchet::Features::RateLimits]
|
|
171
182
|
def rate_limits
|
|
172
183
|
@rate_limits ||= Hatchet::Features::RateLimits.new(admin_grpc, @config)
|
|
173
184
|
end
|
|
174
185
|
|
|
175
|
-
#
|
|
186
|
+
# The cron client is a client for managing cron workflow triggers within
|
|
187
|
+
# Hatchet.
|
|
176
188
|
# @return [Hatchet::Features::Cron]
|
|
177
189
|
def cron
|
|
178
190
|
@cron ||= Hatchet::Features::Cron.new(rest_client, @config)
|
|
179
191
|
end
|
|
180
192
|
|
|
181
|
-
#
|
|
193
|
+
# The scheduled client is a client for managing scheduled workflow runs
|
|
194
|
+
# within Hatchet.
|
|
182
195
|
# @return [Hatchet::Features::Scheduled]
|
|
183
196
|
def scheduled
|
|
184
197
|
@scheduled ||= Hatchet::Features::Scheduled.new(rest_client, @config)
|
|
185
198
|
end
|
|
186
199
|
|
|
187
|
-
#
|
|
200
|
+
# Define a Hatchet workflow, which can then declare tasks and be run,
|
|
201
|
+
# scheduled, and so on.
|
|
188
202
|
#
|
|
189
|
-
# @param name [String]
|
|
190
|
-
# @
|
|
191
|
-
# @
|
|
203
|
+
# @param name [String] The name of the workflow
|
|
204
|
+
# @option opts [Array<String>] :on_events ([]) A list of event triggers for the workflow - events which cause the workflow to be run
|
|
205
|
+
# @option opts [Array<String>] :on_crons ([]) A list of cron triggers for the workflow
|
|
206
|
+
# @option opts [ConcurrencyExpression, Array<ConcurrencyExpression>, nil] :concurrency (nil) A concurrency object (or list of them) controlling the concurrency settings for this workflow
|
|
207
|
+
# @option opts [Integer, nil] :default_priority (nil) The default priority of the workflow. Higher values will cause runs of this workflow to have priority in scheduling over other, lower priority ones
|
|
208
|
+
# @option opts [Hash, nil] :task_defaults (nil) Default task settings for this workflow
|
|
209
|
+
# @option opts [Array<DefaultFilter>] :default_filters ([]) A list of filters to create when the workflow is created
|
|
210
|
+
# @option opts [Symbol, nil] :sticky (nil) A sticky strategy for the workflow, either +:soft+ or +:hard+
|
|
211
|
+
# @option opts [TTLBasedIdempotencyConfig, StatusBasedIdempotencyConfig, nil] :idempotency (nil) An idempotency configuration for the workflow
|
|
212
|
+
# @return [Hatchet::Workflow] The created workflow object, which can be used to declare tasks, run the workflow, and so on
|
|
192
213
|
#
|
|
193
214
|
# @example
|
|
194
215
|
# wf = hatchet.workflow(name: "MyWorkflow")
|
|
@@ -197,12 +218,18 @@ module Hatchet
|
|
|
197
218
|
Workflow.new(name: name, client: self, **opts)
|
|
198
219
|
end
|
|
199
220
|
|
|
200
|
-
# Create a standalone task
|
|
221
|
+
# Create a standalone Hatchet task. The task is automatically wrapped in a
|
|
222
|
+
# single-task workflow, so it can be run, scheduled, and registered on a
|
|
223
|
+
# worker just like a workflow. The block receives the run's input and a
|
|
224
|
+
# {Context} object.
|
|
201
225
|
#
|
|
202
|
-
# @param name [String]
|
|
203
|
-
# @
|
|
226
|
+
# @param name [String] The name of the task
|
|
227
|
+
# @option opts [Array<String>] :on_events ([]) A list of event triggers for the task - events which cause the task to be run
|
|
228
|
+
# @option opts [Array<DefaultFilter>] :default_filters ([]) A list of filters to create when the task is created
|
|
229
|
+
# @option opts [TTLBasedIdempotencyConfig, StatusBasedIdempotencyConfig, nil] :idempotency (nil) An idempotency configuration for the task
|
|
230
|
+
# @param opts [Hash] Any other keyword arguments (+retries:+, +execution_timeout:+, +concurrency:+, and so on) are forwarded to the task declaration - see {Workflow#task} for the full list
|
|
204
231
|
# @yield [input, ctx] The task execution block
|
|
205
|
-
# @return [Hatchet::Task]
|
|
232
|
+
# @return [Hatchet::Task] The created task object, which can be run, scheduled, and registered on a worker
|
|
206
233
|
#
|
|
207
234
|
# @example
|
|
208
235
|
# my_task = hatchet.task(name: "my_task") { |input, ctx| { "result" => "done" } }
|
|
@@ -214,16 +241,42 @@ module Hatchet
|
|
|
214
241
|
wf.task(name, **opts, &block)
|
|
215
242
|
end
|
|
216
243
|
|
|
217
|
-
# Create a standalone
|
|
244
|
+
# Create a standalone batch task (auto-wraps in a single-task workflow).
|
|
218
245
|
#
|
|
219
|
-
#
|
|
246
|
+
# Batch tasks buffer concurrent runs until Hatchet flushes the batch (size reached or
|
|
247
|
+
# flush interval), then invoke the block once with all buffered inputs keyed by each
|
|
248
|
+
# run's task-run external id. The block must return a Hash mapping each id to its
|
|
249
|
+
# output, or use +broadcast_output+ on the batch config to return the same result to
|
|
250
|
+
# all callers. retries is always forced to 0 for batch tasks.
|
|
251
|
+
#
|
|
252
|
+
# Preview: batch tasks are in beta and may change in future releases.
|
|
253
|
+
#
|
|
254
|
+
# @param name [String] The name of the task
|
|
255
|
+
# @param batch [Hatchet::BatchTaskConfig] The batch configuration (+max_size+, flush interval, +broadcast_output+)
|
|
256
|
+
# @param opts [Hash] Any other keyword arguments (+on_events:+, +idempotency:+, and so on) are forwarded to {#task}
|
|
257
|
+
# @yield [inputs, ctx] The batch execution block, receiving a Hash of task-run external id => input
|
|
258
|
+
# @return [Hatchet::Task] The created batch task object
|
|
259
|
+
#
|
|
260
|
+
# @example
|
|
261
|
+
# batch = hatchet.batch_task(name: "my_batch", batch: Hatchet::BatchTaskConfig.new(max_size: 3)) do |inputs, ctx|
|
|
262
|
+
# inputs.transform_values { |input| { "result" => input["message"].upcase } }
|
|
263
|
+
# end
|
|
264
|
+
def batch_task(name:, batch:, **opts, &block)
|
|
265
|
+
task(name: name, batch: batch, **opts, &block)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Create a standalone _durable_ Hatchet task, which works using Hatchet's
|
|
269
|
+
# durable execution capabilities. Durable tasks receive a {DurableContext}
|
|
270
|
+
# with additional methods like +sleep_for+ and +wait_for+.
|
|
271
|
+
#
|
|
272
|
+
# @param name [String] The name of the task
|
|
220
273
|
# @param eviction_policy [Hatchet::EvictionPolicy, nil] Eviction policy for this
|
|
221
274
|
# durable task. Defaults to {Hatchet::DEFAULT_DURABLE_TASK_EVICTION_POLICY}
|
|
222
275
|
# (15-minute TTL, capacity-eviction enabled). Pass ``nil`` to disable
|
|
223
276
|
# eviction entirely for this task.
|
|
224
|
-
# @param opts [Hash]
|
|
277
|
+
# @param opts [Hash] Any other keyword arguments (+retries:+, +execution_timeout:+, and so on) are forwarded to the task declaration - see {Workflow#task} for the full list
|
|
225
278
|
# @yield [input, ctx] The task execution block
|
|
226
|
-
# @return [Hatchet::Task]
|
|
279
|
+
# @return [Hatchet::Task] The created durable task object
|
|
227
280
|
def durable_task(name:, eviction_policy: Hatchet::DEFAULT_DURABLE_TASK_EVICTION_POLICY, **opts, &block)
|
|
228
281
|
wf = Workflow.new(name: name, client: self,
|
|
229
282
|
on_events: opts.delete(:on_events) || [],
|
|
@@ -231,11 +284,16 @@ module Hatchet
|
|
|
231
284
|
wf.durable_task(name, eviction_policy: eviction_policy, **opts, &block)
|
|
232
285
|
end
|
|
233
286
|
|
|
234
|
-
# Create a
|
|
287
|
+
# Create a Hatchet worker on which to run workflows.
|
|
235
288
|
#
|
|
236
|
-
# @param name [String]
|
|
237
|
-
# @
|
|
238
|
-
# @
|
|
289
|
+
# @param name [String] The name of the worker
|
|
290
|
+
# @option opts [Array<Workflow, Task>] :workflows ([]) A list of workflows (or standalone tasks) to register on the worker
|
|
291
|
+
# @option opts [Integer] :slots (10) Slot count for standard tasks, i.e. the number of tasks the worker can run concurrently
|
|
292
|
+
# @option opts [Integer, nil] :durable_slots (nil) Slot count for durable tasks; defaults to +slots+ if not provided
|
|
293
|
+
# @option opts [Hash] :labels ({}) A hash of labels to assign to the worker, for use with worker affinity; merged with the client's +worker_preset_labels+
|
|
294
|
+
# @return [Hatchet::Worker] The created worker object, which exposes an instance
|
|
295
|
+
# method +start+ which can be called to start the worker (blocking until
|
|
296
|
+
# shutdown), and +stop+ to request a graceful shutdown
|
|
239
297
|
#
|
|
240
298
|
# @example
|
|
241
299
|
# worker = hatchet.worker("my-worker", workflows: [wf], slots: 10)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Hatchet
|
|
2
|
+
class BatchTaskConfig
|
|
3
|
+
attr_reader max_size: Integer
|
|
4
|
+
attr_reader max_interval_ms: Integer?
|
|
5
|
+
attr_reader group_key: String?
|
|
6
|
+
attr_reader group_max_runs: Integer?
|
|
7
|
+
attr_reader broadcast_output: bool
|
|
8
|
+
|
|
9
|
+
def initialize: (max_size: Integer, ?max_interval_ms: Integer?, ?group_key: String?, ?group_max_runs: Integer?, ?broadcast_output: bool) -> void
|
|
10
|
+
def to_proto: () -> untyped
|
|
11
|
+
end
|
|
12
|
+
end
|
data/sig/hatchet/task.rbs
CHANGED
|
@@ -14,6 +14,7 @@ module Hatchet
|
|
|
14
14
|
attr_reader skip_if: Array[untyped]
|
|
15
15
|
attr_reader durable: bool
|
|
16
16
|
attr_reader eviction_policy: EvictionPolicy?
|
|
17
|
+
attr_reader batch: BatchTaskConfig?
|
|
17
18
|
attr_reader fn: Proc?
|
|
18
19
|
attr_reader workflow: Workflow?
|
|
19
20
|
attr_reader client: Client?
|
|
@@ -34,6 +35,7 @@ module Hatchet
|
|
|
34
35
|
?skip_if: Array[untyped],
|
|
35
36
|
?durable: bool,
|
|
36
37
|
?eviction_policy: EvictionPolicy?,
|
|
38
|
+
?batch: BatchTaskConfig?,
|
|
37
39
|
?workflow: Workflow?,
|
|
38
40
|
?client: Client?,
|
|
39
41
|
?deps: Hash[Symbol, Proc]?
|