hatchet-sdk 0.1.1 → 0.3.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/CHANGELOG.md +30 -0
  4. data/lib/hatchet/clients/grpc/admin.rb +45 -2
  5. data/lib/hatchet/clients/grpc/dispatcher.rb +33 -8
  6. data/lib/hatchet/condition_converter.rb +20 -12
  7. data/lib/hatchet/context.rb +6 -1
  8. data/lib/hatchet/contracts/dispatcher/dispatcher_pb.rb +3 -1
  9. data/lib/hatchet/contracts/dispatcher/dispatcher_services_pb.rb +1 -0
  10. data/lib/hatchet/contracts/v1/dispatcher_pb.rb +23 -1
  11. data/lib/hatchet/contracts/v1/dispatcher_services_pb.rb +2 -0
  12. data/lib/hatchet/contracts/v1/shared/condition_pb.rb +3 -1
  13. data/lib/hatchet/contracts/v1/shared/trigger_pb.rb +17 -0
  14. data/lib/hatchet/contracts/v1/workflows_pb.rb +4 -3
  15. data/lib/hatchet/contracts/v1/workflows_services_pb.rb +1 -0
  16. data/lib/hatchet/contracts/workflows/workflows_pb.rb +2 -4
  17. data/lib/hatchet/contracts/workflows/workflows_services_pb.rb +1 -1
  18. data/lib/hatchet/durable_context.rb +102 -33
  19. data/lib/hatchet/engine_version.rb +50 -0
  20. data/lib/hatchet/eviction_policy.rb +60 -0
  21. data/lib/hatchet/exceptions.rb +26 -0
  22. data/lib/hatchet/features/cron.rb +2 -1
  23. data/lib/hatchet/task.rb +7 -0
  24. data/lib/hatchet/version.rb +1 -1
  25. data/lib/hatchet/worker/durable_event_listener.rb +735 -0
  26. data/lib/hatchet/worker/durable_eviction/cache.rb +205 -0
  27. data/lib/hatchet/worker/durable_eviction/manager.rb +233 -0
  28. data/lib/hatchet/worker/runner.rb +279 -53
  29. data/lib/hatchet/worker_obj.rb +60 -4
  30. data/lib/hatchet/workflow.rb +8 -4
  31. data/lib/hatchet-sdk.rb +13 -3
  32. data/sig/hatchet/clients/grpc/dispatcher.rbs +2 -0
  33. data/sig/hatchet/durable_context.rbs +8 -2
  34. data/sig/hatchet/engine_version.rbs +12 -0
  35. data/sig/hatchet/eviction_policy.rbs +14 -0
  36. data/sig/hatchet/exceptions.rbs +12 -0
  37. data/sig/hatchet/task.rbs +2 -0
  38. data/sig/hatchet/worker/durable_event_listener.rbs +31 -0
  39. data/sig/hatchet/worker/durable_eviction/cache.rbs +41 -0
  40. data/sig/hatchet/worker/durable_eviction/manager.rbs +37 -0
  41. data/sig/hatchet/worker/runner.rbs +7 -1
  42. data/sig/hatchet/worker_obj.rbs +3 -0
  43. data/sig/hatchet/workflow.rbs +1 -1
  44. data/sig/hatchet-sdk.rbs +1 -1
  45. metadata +15 -4
@@ -5,9 +5,10 @@
5
5
  require 'google/protobuf'
6
6
 
7
7
  require 'google/protobuf/timestamp_pb'
8
+ require 'v1/shared/trigger_pb'
8
9
 
9
10
 
10
- descriptor_data = "\n\x19workflows/workflows.proto\x1a\x1fgoogle/protobuf/timestamp.proto\">\n\x12PutWorkflowRequest\x12(\n\x04opts\x18\x01 \x01(\x0b\x32\x1a.CreateWorkflowVersionOpts\"\xbf\x04\n\x19\x43reateWorkflowVersionOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12\x36\n\x12scheduled_triggers\x18\x06 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12$\n\x04jobs\x18\x07 \x03(\x0b\x32\x16.CreateWorkflowJobOpts\x12-\n\x0b\x63oncurrency\x18\x08 \x01(\x0b\x32\x18.WorkflowConcurrencyOpts\x12\x1d\n\x10schedule_timeout\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ncron_input\x18\n \x01(\tH\x01\x88\x01\x01\x12\x33\n\x0eon_failure_job\x18\x0b \x01(\x0b\x32\x16.CreateWorkflowJobOptsH\x02\x88\x01\x01\x12$\n\x06sticky\x18\x0c \x01(\x0e\x32\x0f.StickyStrategyH\x03\x88\x01\x01\x12 \n\x04kind\x18\r \x01(\x0e\x32\r.WorkflowKindH\x04\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0e \x01(\x05H\x05\x88\x01\x01\x42\x13\n\x11_schedule_timeoutB\r\n\x0b_cron_inputB\x11\n\x0f_on_failure_jobB\t\n\x07_stickyB\x07\n\x05_kindB\x13\n\x11_default_priority\"\xd0\x01\n\x17WorkflowConcurrencyOpts\x12\x13\n\x06\x61\x63tion\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x36\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x19.ConcurrencyLimitStrategyH\x02\x88\x01\x01\x12\x17\n\nexpression\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\t\n\x07_actionB\x0b\n\t_max_runsB\x11\n\x0f_limit_strategyB\r\n\x0b_expression\"h\n\x15\x43reateWorkflowJobOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12&\n\x05steps\x18\x04 \x03(\x0b\x32\x17.CreateWorkflowStepOptsJ\x04\x08\x03\x10\x04\"\xe5\x01\n\x13\x44\x65siredWorkerLabels\x12\x16\n\tstr_value\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tint_value\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x15\n\x08required\x18\x03 \x01(\x08H\x02\x88\x01\x01\x12/\n\ncomparator\x18\x04 \x01(\x0e\x32\x16.WorkerLabelComparatorH\x03\x88\x01\x01\x12\x13\n\x06weight\x18\x05 \x01(\x05H\x04\x88\x01\x01\x42\x0c\n\n_str_valueB\x0c\n\n_int_valueB\x0b\n\t_requiredB\r\n\x0b_comparatorB\t\n\x07_weight\"\xb5\x03\n\x16\x43reateWorkflowStepOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x11\n\tuser_data\x18\x06 \x01(\t\x12\x0f\n\x07retries\x18\x07 \x01(\x05\x12)\n\x0brate_limits\x18\x08 \x03(\x0b\x32\x14.CreateStepRateLimit\x12@\n\rworker_labels\x18\t \x03(\x0b\x32).CreateWorkflowStepOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\n \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\x0b \x01(\x05H\x01\x88\x01\x01\x1aI\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.DesiredWorkerLabels:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_seconds\"\xfa\x01\n\x13\x43reateStepRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12)\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x12.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"\x16\n\x14ListWorkflowsRequest\"\x83\x03\n\x17ScheduleWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tschedules\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05input\x18\x03 \x01(\t\x12\x16\n\tparent_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x05\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x0b\n\t_priority\"O\n\x11ScheduledWorkflow\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ntrigger_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xe3\x01\n\x0fWorkflowVersion\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\r\n\x05order\x18\x06 \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x07 \x01(\t\x12/\n\x13scheduled_workflows\x18\x08 \x03(\x0b\x32\x12.ScheduledWorkflow\"?\n\x17WorkflowTriggerEventRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x11\n\tevent_key\x18\x02 \x01(\t\"9\n\x16WorkflowTriggerCronRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x0c\n\x04\x63ron\x18\x02 \x01(\t\"H\n\x1a\x42ulkTriggerWorkflowRequest\x12*\n\tworkflows\x18\x01 \x03(\x0b\x32\x17.TriggerWorkflowRequest\"7\n\x1b\x42ulkTriggerWorkflowResponse\x12\x18\n\x10workflow_run_ids\x18\x01 \x03(\t\"\x89\x03\n\x16TriggerWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05input\x18\x02 \x01(\t\x12\x16\n\tparent_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x05 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x06 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x07 \x01(\tH\x04\x88\x01\x01\x12\x1e\n\x11\x64\x65sired_worker_id\x18\x08 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x06\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x14\n\x12_desired_worker_idB\x0b\n\t_priority\"2\n\x17TriggerWorkflowResponse\x12\x17\n\x0fworkflow_run_id\x18\x01 \x01(\t\"W\n\x13PutRateLimitRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\x05\x12$\n\x08\x64uration\x18\x03 \x01(\x0e\x32\x12.RateLimitDuration\"\x16\n\x14PutRateLimitResponse*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*2\n\x0cWorkflowKind\x12\x0c\n\x08\x46UNCTION\x10\x00\x12\x0b\n\x07\x44URABLE\x10\x01\x12\x07\n\x03\x44\x41G\x10\x02*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04*\x85\x01\n\x15WorkerLabelComparator\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x19\n\x15GREATER_THAN_OR_EQUAL\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x16\n\x12LESS_THAN_OR_EQUAL\x10\x05*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06\x32\xdc\x02\n\x0fWorkflowService\x12\x34\n\x0bPutWorkflow\x12\x13.PutWorkflowRequest\x1a\x10.WorkflowVersion\x12>\n\x10ScheduleWorkflow\x12\x18.ScheduleWorkflowRequest\x1a\x10.WorkflowVersion\x12\x44\n\x0fTriggerWorkflow\x12\x17.TriggerWorkflowRequest\x1a\x18.TriggerWorkflowResponse\x12P\n\x13\x42ulkTriggerWorkflow\x12\x1b.BulkTriggerWorkflowRequest\x1a\x1c.BulkTriggerWorkflowResponse\x12;\n\x0cPutRateLimit\x12\x14.PutRateLimitRequest\x1a\x15.PutRateLimitResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/admin/contractsb\x06proto3"
11
+ descriptor_data = "\n\x19workflows/workflows.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17v1/shared/trigger.proto\">\n\x12PutWorkflowRequest\x12(\n\x04opts\x18\x01 \x01(\x0b\x32\x1a.CreateWorkflowVersionOpts\"\xbf\x04\n\x19\x43reateWorkflowVersionOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x16\n\x0e\x65vent_triggers\x18\x04 \x03(\t\x12\x15\n\rcron_triggers\x18\x05 \x03(\t\x12\x36\n\x12scheduled_triggers\x18\x06 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12$\n\x04jobs\x18\x07 \x03(\x0b\x32\x16.CreateWorkflowJobOpts\x12-\n\x0b\x63oncurrency\x18\x08 \x01(\x0b\x32\x18.WorkflowConcurrencyOpts\x12\x1d\n\x10schedule_timeout\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ncron_input\x18\n \x01(\tH\x01\x88\x01\x01\x12\x33\n\x0eon_failure_job\x18\x0b \x01(\x0b\x32\x16.CreateWorkflowJobOptsH\x02\x88\x01\x01\x12$\n\x06sticky\x18\x0c \x01(\x0e\x32\x0f.StickyStrategyH\x03\x88\x01\x01\x12 \n\x04kind\x18\r \x01(\x0e\x32\r.WorkflowKindH\x04\x88\x01\x01\x12\x1d\n\x10\x64\x65\x66\x61ult_priority\x18\x0e \x01(\x05H\x05\x88\x01\x01\x42\x13\n\x11_schedule_timeoutB\r\n\x0b_cron_inputB\x11\n\x0f_on_failure_jobB\t\n\x07_stickyB\x07\n\x05_kindB\x13\n\x11_default_priority\"\xd0\x01\n\x17WorkflowConcurrencyOpts\x12\x13\n\x06\x61\x63tion\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08max_runs\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\x36\n\x0elimit_strategy\x18\x03 \x01(\x0e\x32\x19.ConcurrencyLimitStrategyH\x02\x88\x01\x01\x12\x17\n\nexpression\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\t\n\x07_actionB\x0b\n\t_max_runsB\x11\n\x0f_limit_strategyB\r\n\x0b_expression\"h\n\x15\x43reateWorkflowJobOpts\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12&\n\x05steps\x18\x04 \x03(\x0b\x32\x17.CreateWorkflowStepOptsJ\x04\x08\x03\x10\x04\"\xb8\x03\n\x16\x43reateWorkflowStepOpts\x12\x13\n\x0breadable_id\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x0f\n\x07timeout\x18\x03 \x01(\t\x12\x0e\n\x06inputs\x18\x04 \x01(\t\x12\x0f\n\x07parents\x18\x05 \x03(\t\x12\x11\n\tuser_data\x18\x06 \x01(\t\x12\x0f\n\x07retries\x18\x07 \x01(\x05\x12)\n\x0brate_limits\x18\x08 \x03(\x0b\x32\x14.CreateStepRateLimit\x12@\n\rworker_labels\x18\t \x03(\x0b\x32).CreateWorkflowStepOpts.WorkerLabelsEntry\x12\x1b\n\x0e\x62\x61\x63koff_factor\x18\n \x01(\x02H\x00\x88\x01\x01\x12 \n\x13\x62\x61\x63koff_max_seconds\x18\x0b \x01(\x05H\x01\x88\x01\x01\x1aL\n\x11WorkerLabelsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.v1.DesiredWorkerLabels:\x02\x38\x01\x42\x11\n\x0f_backoff_factorB\x16\n\x14_backoff_max_seconds\"\xfa\x01\n\x13\x43reateStepRateLimit\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x12\n\x05units\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x15\n\x08key_expr\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nunits_expr\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1e\n\x11limit_values_expr\x18\x05 \x01(\tH\x03\x88\x01\x01\x12)\n\x08\x64uration\x18\x06 \x01(\x0e\x32\x12.RateLimitDurationH\x04\x88\x01\x01\x42\x08\n\x06_unitsB\x0b\n\t_key_exprB\r\n\x0b_units_exprB\x14\n\x12_limit_values_exprB\x0b\n\t_duration\"\x16\n\x14ListWorkflowsRequest\"\x83\x03\n\x17ScheduleWorkflowRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tschedules\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05input\x18\x03 \x01(\t\x12\x16\n\tparent_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12(\n\x1bparent_task_run_external_id\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hild_index\x18\x06 \x01(\x05H\x02\x88\x01\x01\x12\x16\n\tchild_key\x18\x07 \x01(\tH\x03\x88\x01\x01\x12 \n\x13\x61\x64\x64itional_metadata\x18\x08 \x01(\tH\x04\x88\x01\x01\x12\x15\n\x08priority\x18\t \x01(\x05H\x05\x88\x01\x01\x42\x0c\n\n_parent_idB\x1e\n\x1c_parent_task_run_external_idB\x0e\n\x0c_child_indexB\x0c\n\n_child_keyB\x16\n\x14_additional_metadataB\x0b\n\t_priority\"O\n\x11ScheduledWorkflow\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ntrigger_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xe3\x01\n\x0fWorkflowVersion\x12\n\n\x02id\x18\x01 \x01(\t\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\r\n\x05order\x18\x06 \x01(\x03\x12\x13\n\x0bworkflow_id\x18\x07 \x01(\t\x12/\n\x13scheduled_workflows\x18\x08 \x03(\x0b\x32\x12.ScheduledWorkflow\"?\n\x17WorkflowTriggerEventRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x11\n\tevent_key\x18\x02 \x01(\t\"9\n\x16WorkflowTriggerCronRef\x12\x11\n\tparent_id\x18\x01 \x01(\t\x12\x0c\n\x04\x63ron\x18\x02 \x01(\t\"K\n\x1a\x42ulkTriggerWorkflowRequest\x12-\n\tworkflows\x18\x01 \x03(\x0b\x32\x1a.v1.TriggerWorkflowRequest\"7\n\x1b\x42ulkTriggerWorkflowResponse\x12\x18\n\x10workflow_run_ids\x18\x01 \x03(\t\"2\n\x17TriggerWorkflowResponse\x12\x17\n\x0fworkflow_run_id\x18\x01 \x01(\t\"W\n\x13PutRateLimitRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05limit\x18\x02 \x01(\x05\x12$\n\x08\x64uration\x18\x03 \x01(\x0e\x32\x12.RateLimitDuration\"\x16\n\x14PutRateLimitResponse*$\n\x0eStickyStrategy\x12\x08\n\x04SOFT\x10\x00\x12\x08\n\x04HARD\x10\x01*2\n\x0cWorkflowKind\x12\x0c\n\x08\x46UNCTION\x10\x00\x12\x0b\n\x07\x44URABLE\x10\x01\x12\x07\n\x03\x44\x41G\x10\x02*\x7f\n\x18\x43oncurrencyLimitStrategy\x12\x16\n\x12\x43\x41NCEL_IN_PROGRESS\x10\x00\x12\x0f\n\x0b\x44ROP_NEWEST\x10\x01\x12\x10\n\x0cQUEUE_NEWEST\x10\x02\x12\x15\n\x11GROUP_ROUND_ROBIN\x10\x03\x12\x11\n\rCANCEL_NEWEST\x10\x04*]\n\x11RateLimitDuration\x12\n\n\x06SECOND\x10\x00\x12\n\n\x06MINUTE\x10\x01\x12\x08\n\x04HOUR\x10\x02\x12\x07\n\x03\x44\x41Y\x10\x03\x12\x08\n\x04WEEK\x10\x04\x12\t\n\x05MONTH\x10\x05\x12\x08\n\x04YEAR\x10\x06\x32\xdf\x02\n\x0fWorkflowService\x12\x34\n\x0bPutWorkflow\x12\x13.PutWorkflowRequest\x1a\x10.WorkflowVersion\x12>\n\x10ScheduleWorkflow\x12\x18.ScheduleWorkflowRequest\x1a\x10.WorkflowVersion\x12G\n\x0fTriggerWorkflow\x12\x1a.v1.TriggerWorkflowRequest\x1a\x18.TriggerWorkflowResponse\x12P\n\x13\x42ulkTriggerWorkflow\x12\x1b.BulkTriggerWorkflowRequest\x1a\x1c.BulkTriggerWorkflowResponse\x12;\n\x0cPutRateLimit\x12\x14.PutRateLimitRequest\x1a\x15.PutRateLimitResponseBBZ@github.com/hatchet-dev/hatchet/internal/services/admin/contractsb\x06proto3"
11
12
 
12
13
  pool = ::Google::Protobuf::DescriptorPool.generated_pool
13
14
  pool.add_serialized_file(descriptor_data)
@@ -16,7 +17,6 @@ PutWorkflowRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("P
16
17
  CreateWorkflowVersionOpts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("CreateWorkflowVersionOpts").msgclass
17
18
  WorkflowConcurrencyOpts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("WorkflowConcurrencyOpts").msgclass
18
19
  CreateWorkflowJobOpts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("CreateWorkflowJobOpts").msgclass
19
- DesiredWorkerLabels = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("DesiredWorkerLabels").msgclass
20
20
  CreateWorkflowStepOpts = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("CreateWorkflowStepOpts").msgclass
21
21
  CreateStepRateLimit = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("CreateStepRateLimit").msgclass
22
22
  ListWorkflowsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("ListWorkflowsRequest").msgclass
@@ -27,12 +27,10 @@ WorkflowTriggerEventRef = ::Google::Protobuf::DescriptorPool.generated_pool.look
27
27
  WorkflowTriggerCronRef = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("WorkflowTriggerCronRef").msgclass
28
28
  BulkTriggerWorkflowRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("BulkTriggerWorkflowRequest").msgclass
29
29
  BulkTriggerWorkflowResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("BulkTriggerWorkflowResponse").msgclass
30
- TriggerWorkflowRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("TriggerWorkflowRequest").msgclass
31
30
  TriggerWorkflowResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("TriggerWorkflowResponse").msgclass
32
31
  PutRateLimitRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("PutRateLimitRequest").msgclass
33
32
  PutRateLimitResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("PutRateLimitResponse").msgclass
34
33
  StickyStrategy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("StickyStrategy").enummodule
35
34
  WorkflowKind = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("WorkflowKind").enummodule
36
35
  ConcurrencyLimitStrategy = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("ConcurrencyLimitStrategy").enummodule
37
- WorkerLabelComparator = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("WorkerLabelComparator").enummodule
38
36
  RateLimitDuration = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("RateLimitDuration").enummodule
@@ -16,7 +16,7 @@ module WorkflowService
16
16
 
17
17
  rpc :PutWorkflow, ::PutWorkflowRequest, ::WorkflowVersion
18
18
  rpc :ScheduleWorkflow, ::ScheduleWorkflowRequest, ::WorkflowVersion
19
- rpc :TriggerWorkflow, ::TriggerWorkflowRequest, ::TriggerWorkflowResponse
19
+ rpc :TriggerWorkflow, ::V1::TriggerWorkflowRequest, ::TriggerWorkflowResponse
20
20
  rpc :BulkTriggerWorkflow, ::BulkTriggerWorkflowRequest, ::BulkTriggerWorkflowResponse
21
21
  rpc :PutRateLimit, ::PutRateLimitRequest, ::PutRateLimitResponse
22
22
  end
@@ -23,53 +23,123 @@ module Hatchet
23
23
  # result = ctx.wait_for("event", Hatchet::UserEventCondition.new(event_key: "user:update"))
24
24
  # end
25
25
  class DurableContext < Context
26
+ # @return [Hatchet::WorkerRuntime::DurableEviction::DurableEvictionManager, nil]
27
+ attr_accessor :eviction_manager
28
+
29
+ # @return [String, nil] The action key used by the eviction manager to
30
+ # identify this run invocation.
31
+ attr_accessor :action_key
32
+
33
+ # @return [Hatchet::WorkerRuntime::DurableEventListener, nil] New-style bidi
34
+ # listener. When set the context delegates through it instead of the
35
+ # legacy RegisterDurableEvent/ListenForDurableEvent path.
36
+ attr_accessor :durable_event_listener
37
+
38
+ # @return [Integer] Durable-task invocation count (>= 1).
39
+ attr_accessor :invocation_count
40
+
41
+ # @return [String, nil] Engine version string advertised via GetVersion.
42
+ attr_accessor :engine_version
43
+
26
44
  # Sleep for a specified duration. The task is suspended and resumed
27
45
  # by the engine after the duration expires.
28
46
  #
47
+ # Delegates to {#wait_for} with a {Hatchet::SleepCondition} so that both
48
+ # sleeps and event waits share a single registration / eviction path.
49
+ #
29
50
  # @param duration [Integer, String] Duration in seconds, or a duration string (e.g. "60s")
51
+ # @param label [String, nil] Optional wait label shown in durable event logs.
30
52
  # @return [Hash, nil] Result from the sleep event
31
- def sleep_for(duration:)
32
- signal_key = "sleep_#{duration}"
33
-
53
+ def sleep_for(duration:, label: nil)
34
54
  duration_str = duration.is_a?(String) ? duration : "#{duration}s"
55
+ duration_value = duration.is_a?(String) ? duration : duration.to_i
56
+ wait_index = increment_wait_index
57
+ signal_key = "sleep:#{duration_str}-#{wait_index}"
35
58
 
36
- # Build the sleep condition
37
- sleep_condition = ::V1::SleepMatchCondition.new(
38
- base: ::V1::BaseMatchCondition.new(
39
- readable_data_key: signal_key,
40
- action: :QUEUE,
41
- or_group_id: SecureRandom.uuid,
42
- ),
43
- sleep_for: duration_str,
44
- )
45
-
46
- conditions = ::V1::DurableEventListenerConditions.new(
47
- sleep_conditions: [sleep_condition],
48
- )
49
-
50
- # Register the durable event
51
- register_request = ::V1::RegisterDurableEventRequest.new(
52
- task_id: @step_run_id,
53
- signal_key: signal_key,
54
- conditions: conditions,
55
- )
56
-
57
- v1_dispatcher_stub.register_durable_event(register_request, metadata: @client.config.auth_metadata)
58
-
59
- # Listen for the durable event via bidi stream
60
- listen_for_event(signal_key)
59
+ wait_for(signal_key, Hatchet::SleepCondition.new(duration_value), label: label)
61
60
  end
62
61
 
63
62
  # Wait for a condition to be met (event or sleep).
64
63
  # The task is suspended and resumed when the condition is satisfied.
65
64
  #
65
+ # Register the durable wait with ``send_event`` first, then start eviction
66
+ # tracking only while blocked on ``wait_for_callback``.
67
+ #
66
68
  # @param key [String] A unique key for this wait operation
67
69
  # @param condition [Object] The condition to wait for (UserEventCondition, SleepCondition, Hash, etc.)
70
+ # @param label [String, nil] Optional wait label shown in durable event logs.
68
71
  # @return [Hash] Result from the wait, including which condition was satisfied
69
- def wait_for(key, condition)
72
+ def wait_for(key, condition, label: nil)
70
73
  conditions = build_durable_conditions(key, condition)
71
74
 
72
- # Register the durable event
75
+ if supports_durable_eviction?
76
+ invocation = @invocation_count || 1
77
+
78
+ event = Hatchet::WorkerRuntime::DurableEventListener::WaitForEvent.new(
79
+ wait_for_conditions: conditions,
80
+ label: label,
81
+ )
82
+
83
+ ack = @durable_event_listener.send_event(@step_run_id, invocation, event)
84
+
85
+ with_eviction_wait(wait_kind: "wait_for", resource_id: key) do
86
+ result = @durable_event_listener.wait_for_callback(
87
+ @step_run_id,
88
+ invocation,
89
+ ack[:branch_id],
90
+ ack[:node_id],
91
+ )
92
+
93
+ result[:payload] || {}
94
+ end
95
+ else
96
+ with_eviction_wait(wait_kind: "wait_for", resource_id: key) do
97
+ legacy_wait_for(key, conditions)
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ # Monotonically increasing per-context wait index, used to disambiguate
105
+ # multiple sleep/wait calls with otherwise identical resource ids.
106
+ def increment_wait_index
107
+ @wait_index ||= 0
108
+ index = @wait_index
109
+ @wait_index += 1
110
+ index
111
+ end
112
+
113
+ # Wrap a block in mark_waiting/mark_active calls on the eviction manager
114
+ # when one is attached. Safe no-op when not.
115
+ def with_eviction_wait(wait_kind:, resource_id:)
116
+ mgr = @eviction_manager
117
+ key = @action_key
118
+
119
+ mgr.mark_waiting(key, wait_kind: wait_kind, resource_id: resource_id) if mgr && key
120
+
121
+ begin
122
+ yield
123
+ ensure
124
+ mgr.mark_active(key) if mgr && key
125
+ end
126
+ end
127
+
128
+ # True when this context can use the durable-eviction bidi protocol.
129
+ #
130
+ # Matches the Python SDK's feature-gate naming more closely than
131
+ # ``use_new_listener?`` while preserving the same behavior here.
132
+ def supports_durable_eviction?
133
+ return false unless @durable_event_listener
134
+ return false unless @engine_version
135
+
136
+ !Hatchet::EngineVersion.semver_less_than?(
137
+ @engine_version,
138
+ Hatchet::MinEngineVersion::DURABLE_EVICTION,
139
+ )
140
+ end
141
+
142
+ def legacy_wait_for(key, conditions)
73
143
  register_request = ::V1::RegisterDurableEventRequest.new(
74
144
  task_id: @step_run_id,
75
145
  signal_key: key,
@@ -93,8 +163,6 @@ module Hatchet
93
163
  end
94
164
  end
95
165
 
96
- private
97
-
98
166
  # Get or create the V1::V1Dispatcher::Stub for durable events.
99
167
  def v1_dispatcher_stub
100
168
  @v1_dispatcher_stub ||= ::V1::V1Dispatcher::Stub.new(
@@ -194,7 +262,8 @@ module Hatchet
194
262
  def process_durable_condition(key, condition, or_group_id, sleep_conditions, user_event_conditions)
195
263
  ConditionConverter.convert_condition(
196
264
  condition,
197
- action: :QUEUE,
265
+ # Do not force base.action. Leaving it unset keeps protobuf default semantics on the server path.
266
+ action: nil,
198
267
  sleep_conditions: sleep_conditions,
199
268
  user_event_conditions: user_event_conditions,
200
269
  or_group_id: or_group_id,
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hatchet
4
+ # Minimum engine versions required for specific SDK features.
5
+ #
6
+ # Mirrors :class:`hatchet_sdk.engine_version.MinEngineVersion` in the
7
+ # Python SDK.
8
+ module MinEngineVersion
9
+ SLOT_CONFIG = "v0.78.23"
10
+ DURABLE_EVICTION = "v0.80.0"
11
+ OBSERVABILITY = "v0.82.0"
12
+ end
13
+
14
+ # Semver parsing + comparison helpers used to gate features by engine version.
15
+ module EngineVersion
16
+ module_function
17
+
18
+ # Parse a semver string like ``"v0.78.23"`` into ``[major, minor, patch]``.
19
+ #
20
+ # Returns ``[0, 0, 0]`` if parsing fails, matching the Python helper.
21
+ #
22
+ # @param version [String, nil] The version string (with or without a ``v`` prefix, optional ``-pre`` suffix)
23
+ # @return [Array(Integer, Integer, Integer)]
24
+ def parse_semver(version)
25
+ return [0, 0, 0] if version.nil?
26
+
27
+ v = version.to_s
28
+ v = v.sub(/\Av/, "")
29
+ v = v.split("-", 2).first || ""
30
+
31
+ parts = v.split(".")
32
+ return [0, 0, 0] if parts.length != 3
33
+
34
+ [Integer(parts[0]), Integer(parts[1]), Integer(parts[2])]
35
+ rescue ArgumentError, TypeError
36
+ [0, 0, 0]
37
+ end
38
+
39
+ # @param left_version [String, nil]
40
+ # @param right_version [String, nil]
41
+ # @return [Boolean] true if semver ``left_version`` is strictly less than ``right_version``
42
+ def semver_less_than?(left_version, right_version)
43
+ (parse_semver(left_version) <=> parse_semver(right_version)).negative?
44
+ end
45
+
46
+ class << self
47
+ alias semver_less_than semver_less_than?
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hatchet
4
+ # Task-scoped eviction parameters for *durable* tasks.
5
+ #
6
+ # Setting the durable task's eviction policy to ``nil`` means the task run is
7
+ # never eligible for eviction.
8
+ #
9
+ # @example
10
+ # Hatchet::EvictionPolicy.new(
11
+ # ttl: 600, # 10 minutes, in seconds
12
+ # allow_capacity_eviction: true,
13
+ # priority: 0,
14
+ # )
15
+ class EvictionPolicy
16
+ # @return [Numeric, nil] Maximum continuous waiting duration in seconds before
17
+ # TTL-eligible eviction. Applies to time spent in SDK-instrumented
18
+ # "waiting" states (e.g. :meth:`DurableContext#sleep_for`,
19
+ # :meth:`DurableContext#wait_for`). ``nil`` disables TTL eviction.
20
+ attr_reader :ttl
21
+
22
+ # @return [Boolean] Whether this task may be evicted under durable-slot pressure.
23
+ attr_reader :allow_capacity_eviction
24
+
25
+ # @return [Integer] Lower values are evicted first when multiple candidates exist.
26
+ attr_reader :priority
27
+
28
+ # @param ttl [Numeric, nil] TTL in seconds (or nil to disable TTL-based eviction)
29
+ # @param allow_capacity_eviction [Boolean]
30
+ # @param priority [Integer]
31
+ def initialize(ttl:, allow_capacity_eviction: true, priority: 0)
32
+ @ttl = ttl
33
+ @allow_capacity_eviction = allow_capacity_eviction
34
+ @priority = priority
35
+ freeze
36
+ end
37
+
38
+ def ==(other)
39
+ other.is_a?(EvictionPolicy) &&
40
+ other.ttl == ttl &&
41
+ other.allow_capacity_eviction == allow_capacity_eviction &&
42
+ other.priority == priority
43
+ end
44
+ alias eql? ==
45
+
46
+ def hash
47
+ [self.class, ttl, allow_capacity_eviction, priority].hash
48
+ end
49
+ end
50
+
51
+ # Shared sensible defaults.
52
+ #
53
+ # NOTE: When changing these values, update the :param eviction_policy: docstrings
54
+ # in :meth:`Workflow#durable_task` and :meth:`Client#durable_task` to match.
55
+ DEFAULT_DURABLE_TASK_EVICTION_POLICY = EvictionPolicy.new(
56
+ ttl: 15 * 60, # 15 minutes
57
+ allow_capacity_eviction: true,
58
+ priority: 0,
59
+ )
60
+ end
@@ -41,6 +41,32 @@ module Hatchet
41
41
  end
42
42
  end
43
43
 
44
+ # Raised by the engine when durable-task execution detects a non-deterministic
45
+ # replay (the workflow did something different compared to the recorded log).
46
+ class NonDeterminismError < Error
47
+ # @return [String, nil]
48
+ attr_reader :task_external_id
49
+ # @return [Integer, nil]
50
+ attr_reader :invocation_count
51
+ # @return [Integer, nil]
52
+ attr_reader :node_id
53
+
54
+ def initialize(message, task_external_id: nil, invocation_count: nil, node_id: nil)
55
+ @task_external_id = task_external_id
56
+ @invocation_count = invocation_count
57
+ @node_id = node_id
58
+ super(message)
59
+ end
60
+ end
61
+
62
+ # Raised inside a durable task thread when the eviction manager decides to
63
+ # evict that invocation (e.g. TTL expired, capacity pressure, worker shutdown).
64
+ class DurableTaskEvictedError < Error
65
+ def initialize(message = "Durable task evicted")
66
+ super
67
+ end
68
+ end
69
+
44
70
  # Raised when a workflow run fails with one or more task errors
45
71
  class FailedRunError < Error
46
72
  # @return [Array<TaskRunError>] The individual task run errors
@@ -139,7 +139,8 @@ module Hatchet
139
139
  return stripped if CRON_ALIASES.include?(stripped)
140
140
 
141
141
  parts = stripped.split
142
- raise ArgumentError, "Cron expression must have 5 parts: minute hour day month weekday" unless parts.length == 5
142
+ raise ArgumentError, "Cron expression must have 5 or 6 parts: (second) minute hour day month weekday" \
143
+ unless [5, 6].include?(parts.length)
143
144
 
144
145
  parts.each do |part|
145
146
  unless part == "*" || part.gsub("*/", "").gsub("-", "").gsub(",", "").match?(/\A\d+\z/)
data/lib/hatchet/task.rb CHANGED
@@ -65,6 +65,9 @@ module Hatchet
65
65
  # @return [Boolean] Whether this is a durable task
66
66
  attr_reader :durable
67
67
 
68
+ # @return [Hatchet::EvictionPolicy, nil] Eviction policy for durable tasks
69
+ attr_reader :eviction_policy
70
+
68
71
  # @return [Proc, nil] The task execution block
69
72
  attr_reader :fn
70
73
 
@@ -108,6 +111,7 @@ module Hatchet
108
111
  wait_for: [],
109
112
  skip_if: [],
110
113
  durable: false,
114
+ eviction_policy: nil,
111
115
  workflow: nil,
112
116
  client: nil,
113
117
  deps: nil,
@@ -126,6 +130,7 @@ module Hatchet
126
130
  @wait_for = wait_for
127
131
  @skip_if = skip_if
128
132
  @durable = durable
133
+ @eviction_policy = eviction_policy
129
134
  @workflow = workflow
130
135
  @client = client
131
136
  @deps = deps
@@ -184,6 +189,8 @@ module Hatchet
184
189
  conditions_proto = conditions_to_proto(config)
185
190
  opts[:conditions] = conditions_proto if conditions_proto
186
191
 
192
+ opts[:is_durable] = @durable
193
+
187
194
  ::V1::CreateTaskOpts.new(**opts)
188
195
  end
189
196
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hatchet
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.0"
5
5
  end