activejob-temporal 0.1.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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +130 -0
  3. data/LICENSE +21 -0
  4. data/README.md +198 -0
  5. data/activejob-temporal.gemspec +58 -0
  6. data/api/job_payload_schema.json +318 -0
  7. data/bin/temporal-worker +295 -0
  8. data/lib/activejob/temporal/active_job_handler_source.rb +84 -0
  9. data/lib/activejob/temporal/activities/aj_runner_activity.rb +454 -0
  10. data/lib/activejob/temporal/activities/best_effort_side_effects.rb +49 -0
  11. data/lib/activejob/temporal/activities/dependency_status_activity.rb +160 -0
  12. data/lib/activejob/temporal/activities/rate_limit_activity.rb +41 -0
  13. data/lib/activejob/temporal/adapter.rb +257 -0
  14. data/lib/activejob/temporal/audit_log.rb +118 -0
  15. data/lib/activejob/temporal/batch_enqueue_result.rb +110 -0
  16. data/lib/activejob/temporal/batch_enqueuer.rb +141 -0
  17. data/lib/activejob/temporal/bind_policy.rb +44 -0
  18. data/lib/activejob/temporal/cancel/batch_canceller.rb +154 -0
  19. data/lib/activejob/temporal/cancel/batch_summary.rb +45 -0
  20. data/lib/activejob/temporal/cancel.rb +236 -0
  21. data/lib/activejob/temporal/certificate_watcher.rb +76 -0
  22. data/lib/activejob/temporal/chain_options.rb +83 -0
  23. data/lib/activejob/temporal/child_workflow_options.rb +102 -0
  24. data/lib/activejob/temporal/client.rb +215 -0
  25. data/lib/activejob/temporal/conditional_enqueue.rb +56 -0
  26. data/lib/activejob/temporal/configurable.rb +55 -0
  27. data/lib/activejob/temporal/configuration.rb +981 -0
  28. data/lib/activejob/temporal/configured_job_compatibility.rb +44 -0
  29. data/lib/activejob/temporal/connection_worker_pool.rb +88 -0
  30. data/lib/activejob/temporal/dead_letter_payload_validation.rb +34 -0
  31. data/lib/activejob/temporal/dead_letter_queue.rb +163 -0
  32. data/lib/activejob/temporal/dependency_options.rb +134 -0
  33. data/lib/activejob/temporal/external_operation.rb +193 -0
  34. data/lib/activejob/temporal/health_check_server.rb +159 -0
  35. data/lib/activejob/temporal/http_line_reader.rb +36 -0
  36. data/lib/activejob/temporal/inspect.rb +184 -0
  37. data/lib/activejob/temporal/job_descriptor.rb +37 -0
  38. data/lib/activejob/temporal/job_payload_builder.rb +209 -0
  39. data/lib/activejob/temporal/job_payload_chain_builder.rb +106 -0
  40. data/lib/activejob/temporal/job_payload_child_workflows.rb +127 -0
  41. data/lib/activejob/temporal/job_payload_dependencies.rb +40 -0
  42. data/lib/activejob/temporal/job_payload_rate_limits.rb +53 -0
  43. data/lib/activejob/temporal/job_payload_workflow_interactions.rb +31 -0
  44. data/lib/activejob/temporal/job_tags.rb +40 -0
  45. data/lib/activejob/temporal/locales/en.yml +126 -0
  46. data/lib/activejob/temporal/logger.rb +214 -0
  47. data/lib/activejob/temporal/metrics_server.rb +150 -0
  48. data/lib/activejob/temporal/middleware/chain.rb +106 -0
  49. data/lib/activejob/temporal/middleware.rb +11 -0
  50. data/lib/activejob/temporal/observability/datadog.rb +167 -0
  51. data/lib/activejob/temporal/observability/opentelemetry.rb +107 -0
  52. data/lib/activejob/temporal/observability/prometheus.rb +271 -0
  53. data/lib/activejob/temporal/observability.rb +260 -0
  54. data/lib/activejob/temporal/payload.rb +415 -0
  55. data/lib/activejob/temporal/payload_encryption.rb +215 -0
  56. data/lib/activejob/temporal/payload_serializers/json.rb +23 -0
  57. data/lib/activejob/temporal/payload_serializers/marshal.rb +53 -0
  58. data/lib/activejob/temporal/payload_serializers/message_pack.rb +59 -0
  59. data/lib/activejob/temporal/payload_serializers.rb +37 -0
  60. data/lib/activejob/temporal/payload_storage.rb +103 -0
  61. data/lib/activejob/temporal/rails_environment_loader.rb +143 -0
  62. data/lib/activejob/temporal/rate_limit_options.rb +94 -0
  63. data/lib/activejob/temporal/rate_limiters/memory.rb +198 -0
  64. data/lib/activejob/temporal/reload_signal_queue.rb +40 -0
  65. data/lib/activejob/temporal/retry_handler_extractor.rb +361 -0
  66. data/lib/activejob/temporal/retry_mapper.rb +264 -0
  67. data/lib/activejob/temporal/schedulable.rb +60 -0
  68. data/lib/activejob/temporal/schedule.rb +181 -0
  69. data/lib/activejob/temporal/schedule_options.rb +105 -0
  70. data/lib/activejob/temporal/search_attributes.rb +173 -0
  71. data/lib/activejob/temporal/signal_query.rb +161 -0
  72. data/lib/activejob/temporal/signal_query_options.rb +106 -0
  73. data/lib/activejob/temporal/temporal_options.rb +114 -0
  74. data/lib/activejob/temporal/tls_file.rb +45 -0
  75. data/lib/activejob/temporal/transaction_safety.rb +39 -0
  76. data/lib/activejob/temporal/version.rb +7 -0
  77. data/lib/activejob/temporal/visibility_query.rb +13 -0
  78. data/lib/activejob/temporal/worker_client_reloader.rb +34 -0
  79. data/lib/activejob/temporal/worker_health.rb +117 -0
  80. data/lib/activejob/temporal/worker_pool.rb +408 -0
  81. data/lib/activejob/temporal/workflow_enqueuer.rb +271 -0
  82. data/lib/activejob/temporal/workflow_enqueuer_batch.rb +17 -0
  83. data/lib/activejob/temporal/workflow_id_builder.rb +155 -0
  84. data/lib/activejob/temporal/workflow_identity.rb +62 -0
  85. data/lib/activejob/temporal/workflows/aj_workflow.rb +282 -0
  86. data/lib/activejob/temporal/workflows/dead_letter_support.rb +134 -0
  87. data/lib/activejob/temporal/workflows/dead_letter_workflow.rb +114 -0
  88. data/lib/activejob/temporal/workflows/workflow_chaining.rb +194 -0
  89. data/lib/activejob/temporal/workflows/workflow_child_workflows.rb +140 -0
  90. data/lib/activejob/temporal/workflows/workflow_continue_as_new.rb +44 -0
  91. data/lib/activejob/temporal/workflows/workflow_dependencies.rb +115 -0
  92. data/lib/activejob/temporal/workflows/workflow_execution_steps.rb +22 -0
  93. data/lib/activejob/temporal/workflows/workflow_interactions.rb +215 -0
  94. data/lib/activejob/temporal/workflows/workflow_local_activities.rb +29 -0
  95. data/lib/activejob/temporal/workflows/workflow_nexus.rb +15 -0
  96. data/lib/activejob/temporal/workflows/workflow_versioning.rb +21 -0
  97. data/lib/activejob/temporal.rb +297 -0
  98. data/lib/activejob-temporal.rb +3 -0
  99. metadata +423 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bd1d95e1a889d180f79f3d2eeb2ec31d5c5b4338776cc41656b9077cbdd96da7
4
+ data.tar.gz: 66c78d747c740b51e0320ca131d480160ef37af78496cf77632c68bfc1b7915d
5
+ SHA512:
6
+ metadata.gz: 2d87ef5a4b8a31de5990da5a9effd53bc7e9b1c4a59ad185d632a31d73b23c9b566358785ae5e469a3c96322b092b7b46b6387dd08d9b245d044da102690eafd
7
+ data.tar.gz: b51db2d419c9301934241e0bb4b308ea7b398ecf3ba964cc8f7c82e73d75dfbf829839e79d1a42347dfe539b3d2cf289a74dff1516299806b0874face1e13449
data/CHANGELOG.md ADDED
@@ -0,0 +1,130 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ <!-- github_changelog_generator:start -->
11
+
12
+ **Implemented enhancements:**
13
+
14
+ - TASK.014 - Signals and Queries support [\#14](https://github.com/schovi/activejob-temporal/issues/14)
15
+ - TASK.050 - Add child workflows [\#61](https://github.com/schovi/activejob-temporal/issues/61)
16
+ - TASK.017 - Worker pool management [\#17](https://github.com/schovi/activejob-temporal/issues/17)
17
+ - TASK.035 - Add rate limiting support [\#22](https://github.com/schovi/activejob-temporal/issues/22)
18
+ - TASK.022 - Add payload serialization options [\#19](https://github.com/schovi/activejob-temporal/issues/19)
19
+ - TASK.056 - Immediate Ruby 4+ migration task [\#56](https://github.com/schovi/activejob-temporal/issues/56)
20
+ - TASK.053 - Immediate Ruby 4+ migration follow-up [\#53](https://github.com/schovi/activejob-temporal/issues/53)
21
+ - TASK.052 - Reconfirm Ruby 4+ migration baseline [\#52](https://github.com/schovi/activejob-temporal/issues/52)
22
+ - TASK.051 - Enforce Ruby 4+ migration baseline [\#51](https://github.com/schovi/activejob-temporal/issues/51)
23
+ - TASK.050 - Migrate repository to Ruby 4+ [\#50](https://github.com/schovi/activejob-temporal/issues/50)
24
+ - TASK.043 - Add conditional enqueueing support [\#44](https://github.com/schovi/activejob-temporal/issues/44)
25
+ - TASK.039 - Add health check endpoint for workers [\#42](https://github.com/schovi/activejob-temporal/issues/42)
26
+ - TASK.023 - Add configuration validation levels [\#28](https://github.com/schovi/activejob-temporal/issues/28)
27
+ - TASK.021 - Extract WorkflowIdBuilder class [\#27](https://github.com/schovi/activejob-temporal/issues/27)
28
+ - TASK.019 - Replace method\_missing with explicit methods in Configuration [\#25](https://github.com/schovi/activejob-temporal/issues/25)
29
+ - TASK.042 - Add job tagging for search [\#24](https://github.com/schovi/activejob-temporal/issues/24)
30
+ - TASK.018 - Job prioritization via task queue routing [\#18](https://github.com/schovi/activejob-temporal/issues/18)
31
+ - TASK.016 - Built-in metrics integration [\#16](https://github.com/schovi/activejob-temporal/issues/16)
32
+ - TASK.015 - Recurring jobs via Temporal Schedules [\#15](https://github.com/schovi/activejob-temporal/issues/15)
33
+ - TASK.011 - Middleware system for cross-cutting concerns [\#11](https://github.com/schovi/activejob-temporal/issues/11)
34
+ - TASK.010 - Job status inspection API [\#10](https://github.com/schovi/activejob-temporal/issues/10)
35
+ - TASK.009 - Batch cancellation API [\#9](https://github.com/schovi/activejob-temporal/issues/9)
36
+ - TASK.008 - Add workflow ID customization hook [\#8](https://github.com/schovi/activejob-temporal/issues/8)
37
+ - TASK.007 - Extract configuration to separate file [\#7](https://github.com/schovi/activejob-temporal/issues/7)
38
+ - TASK.006 - Add payload size monitoring warnings [\#6](https://github.com/schovi/activejob-temporal/issues/6)
39
+
40
+ **Fixed bugs:**
41
+
42
+ - TASK.020 - Add RetryMapper fallback strategy for ActiveJob internals [\#26](https://github.com/schovi/activejob-temporal/issues/26)
43
+ - TASK.001 - Add explicit concurrent-ruby dependency [\#1](https://github.com/schovi/activejob-temporal/issues/1)
44
+
45
+ **Closed issues:**
46
+
47
+ - TASK.040 - Add Prometheus metrics exporter [\#43](https://github.com/schovi/activejob-temporal/issues/43)
48
+ - TASK.038 - Add systemd unit file example [\#41](https://github.com/schovi/activejob-temporal/issues/41)
49
+ - TASK.037 - Add Docker example with compose [\#40](https://github.com/schovi/activejob-temporal/issues/40)
50
+ - TASK.030 - Add performance benchmarks [\#36](https://github.com/schovi/activejob-temporal/issues/36)
51
+ - TASK.026 - Add gem comparison guide \(vs Sidekiq, GoodJob, etc\) [\#31](https://github.com/schovi/activejob-temporal/issues/31)
52
+ - TASK.025 - Add performance tuning guide [\#30](https://github.com/schovi/activejob-temporal/issues/30)
53
+ - TASK.024 - Add troubleshooting guide [\#29](https://github.com/schovi/activejob-temporal/issues/29)
54
+ - TASK.005 - Add retry policy documentation examples [\#5](https://github.com/schovi/activejob-temporal/issues/5)
55
+ - TASK.003 - Move SimpleCov configuration to standard location [\#3](https://github.com/schovi/activejob-temporal/issues/3)
56
+ - TASK.002 - Document algorithmic wait limitation in migration guide [\#2](https://github.com/schovi/activejob-temporal/issues/2)
57
+
58
+ <!-- github_changelog_generator:end -->
59
+
60
+ ### Changed
61
+ - Replace Prometheus-specific metrics configuration with optional observability adapters backed by Rails notifications.
62
+ - Reconfirm Ruby 4.0+ as the active repository baseline for local tooling, CI, dependency setup, and validation.
63
+ - Migrate the supported Ruby baseline and CI validation target to Ruby 4.0+.
64
+ - Pin the repository Ruby version for local development and document Ruby 4.0.3 validation commands.
65
+ - Support Temporal Ruby SDK 1.4.x for Ruby 4 compatibility, contract-tested against 1.4.0 and 1.4.1.
66
+ - Extract configuration state management into a dedicated `Configurable` concern.
67
+ - Extract deterministic workflow ID construction into `WorkflowIdBuilder`.
68
+ - Move SimpleCov configuration to `.simplecov` with coverage groups.
69
+ - Document algorithmic wait strategy limitations for retry migration.
70
+ - Serialize global activity timeout defaults into workflow payloads so workflows do not read mutable process configuration during replay.
71
+
72
+ ### Fixed
73
+ - Declare `concurrent-ruby` as an explicit runtime dependency.
74
+
75
+ ### Added
76
+ - Optional Prometheus, OpenTelemetry, and Datadog observability adapters with trace context propagation through job payloads.
77
+ - Signal/query APIs via `ActiveJob::Temporal.signal` and `ActiveJob::Temporal.query`, with built-in pause/resume workflow state and deterministic custom job handlers.
78
+ - Child workflow orchestration via `set(child_workflows:)`, with parent-owned child workflow IDs, cancellation propagation, result collection, and chain handoff.
79
+ - Global and per-job rate limiting with a pluggable limiter backend and durable workflow waits.
80
+ - Supervised worker pools via `ActiveJob::Temporal::WorkerPool` and `temporal-worker --pool-size`.
81
+ - Configurable payload serializer envelopes for MessagePack and Marshal while keeping JSON as the default wire format.
82
+ - Changelog generation configuration and `rake changelog:generate` release task.
83
+ - Structured audit logging for job enqueue, execution, failure, and cancellation lifecycle events.
84
+ - Optional AES-256-GCM payload encryption with key rotation support for job execution payloads.
85
+ - Codecov coverage badge and CI coverage uploads.
86
+ - Configurable `workflow_id_generator` hook for custom Temporal workflow IDs.
87
+ - Batch cancellation APIs via `ActiveJob::Temporal.cancel_all` and `ActiveJob::Temporal.cancel_where`.
88
+ - Job status inspection APIs via `ActiveJob::Temporal.status` and status predicates.
89
+ - Conditional enqueueing via `perform_later_if` for callable or class-method conditions.
90
+ - Optional worker health check endpoint via `temporal-worker --health-check-port PORT`.
91
+ - Priority-based task queue routing via `config.priority_task_queues`.
92
+ - Optional Prometheus metrics adapter and worker scrape endpoint via `temporal-worker --metrics-port PORT`.
93
+ - Configuration validation levels via `validation_level` for strict, warning-only, or skipped validation.
94
+ - Custom job search tags via `set(tags:)` and the `ajTags` Temporal search attribute.
95
+ - Recurring job declarations and explicit Temporal Schedule registration via `schedule` and `create_temporal_schedule`.
96
+ - Systemd worker deployment examples for VM and bare-metal hosts.
97
+ - Docker Compose example configuration for the basic Rails app.
98
+ - Structured payload size logs at 80%, 90%, and over-limit thresholds.
99
+ - Retry policy guide with ActiveJob-to-Temporal mapping examples.
100
+ - Per-job timeout configuration via `temporal_options` class method
101
+ - Support for `start_to_close_timeout`, `heartbeat_timeout`, `schedule_to_start_timeout`, and `schedule_to_close_timeout`
102
+ - Timeout values accept both integers (seconds) and `ActiveSupport::Duration` objects (e.g., `2.hours`, `30.seconds`)
103
+ - Per-job timeouts override global configuration defaults
104
+ - Global timeout configuration options:
105
+ - `default_heartbeat_timeout` for long-running activities with heartbeat monitoring
106
+ - `default_schedule_to_start_timeout` to control max wait before activity starts
107
+ - `default_schedule_to_close_timeout` for total time including all retries
108
+ - Comprehensive test coverage for timeout configuration (17 new unit tests, 3 integration tests)
109
+
110
+ ## [0.1.0] - 2025-10-29
111
+
112
+ ### Added
113
+ - ActiveJob adapter backed by Temporal workflows as a drop-in replacement for existing adapters
114
+ - Immediate job execution via `perform_later`
115
+ - Scheduled job execution with `set(wait:)` and `set(wait_until:)`
116
+ - Automatic retry policy mapping from `retry_on` declarations with exponential backoff
117
+ - Automatic discard policy handling from `discard_on` declarations
118
+ - Job cancellation API via `ActiveJob::Temporal.cancel(JobClass, job_id)`
119
+ - Search attributes for filtering and debugging jobs in Temporal UI (job class, queue, job ID, tenant ID, enqueue timestamp)
120
+ - Transactional enqueue support with automatic deferral until database transaction commits
121
+ - GlobalID serialization support for ActiveRecord models and other GlobalID-compatible objects
122
+ - Configurable activity timeouts and retry policies (global and per-job)
123
+ - Temporal worker executable (`bin/temporal-worker`) for running workers
124
+ - Structured JSON logging for observability integration
125
+ - Comprehensive documentation including README, API documentation (YARD), migration guide, and example Rails application
126
+
127
+ ### Security
128
+ - Payload size limit of 250KB enforced to prevent denial-of-service attacks from oversized job payloads
129
+
130
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Temporal Technologies, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # activejob-temporal
2
+
3
+ > Temporal-powered adapter for Rails ActiveJob.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/activejob-temporal.svg)](https://badge.fury.io/rb/activejob-temporal)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![CI](https://github.com/schovi/activejob-temporal/actions/workflows/ci.yml/badge.svg)](https://github.com/schovi/activejob-temporal/actions/workflows/ci.yml)
8
+ [![codecov](https://codecov.io/gh/schovi/activejob-temporal/branch/main/graph/badge.svg)](https://codecov.io/gh/schovi/activejob-temporal)
9
+
10
+ This gem is under active development. Expect breaking changes until v1.0.0.
11
+
12
+ ## What It Does
13
+
14
+ `activejob-temporal` runs Rails ActiveJob work through [Temporal](https://temporal.io) workflows and activities. It is intended for jobs where durable execution, crash recovery, long-running retries, cancellation visibility, and Temporal UI history matter more than queue simplicity.
15
+
16
+ Use a traditional ActiveJob backend when the work is short, simple, and does not justify operating Temporal.
17
+
18
+ ## Requirements
19
+
20
+ - Ruby >= 4.0
21
+ - Rails 8.1 through ActiveJob 8.1
22
+ - Temporal cluster, either self-hosted or [Temporal Cloud](https://temporal.io/cloud)
23
+
24
+ CI validates Ruby 4.0.0 and the latest Ruby 4.0 patch against Rails 8.1.
25
+ Temporal Ruby SDK 1.4.x compatibility is contract-tested against 1.4.0 and the latest 1.4.x release.
26
+
27
+ ## Install
28
+
29
+ ```ruby
30
+ gem "activejob-temporal"
31
+ ```
32
+
33
+ ```bash
34
+ bundle install
35
+ ```
36
+
37
+ Then configure ActiveJob:
38
+
39
+ ```ruby
40
+ # config/application.rb or config/environments/production.rb
41
+ config.active_job.queue_adapter = :temporal
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ Create a Temporal initializer:
47
+
48
+ ```ruby
49
+ # config/initializers/activejob_temporal.rb
50
+ ActiveJob::Temporal.configure do |config|
51
+ config.target = ENV.fetch("ACTIVEJOB_TEMPORAL_TARGET", "127.0.0.1:7233")
52
+ config.namespace = ENV.fetch("ACTIVEJOB_TEMPORAL_NAMESPACE", "default")
53
+ config.task_queue_prefix = ENV.fetch("ACTIVEJOB_TEMPORAL_TASK_QUEUE_PREFIX", nil)
54
+
55
+ config.default_activity_timeout = 15.minutes
56
+ config.default_retry_initial_interval = 30.seconds
57
+ config.default_retry_backoff = 2.0
58
+ config.default_retry_max_attempts = 1
59
+ end
60
+ ```
61
+
62
+ Register the built-in search attributes once per Temporal cluster:
63
+
64
+ ```bash
65
+ tctl admin cluster add-search-attributes \
66
+ --name ajClass --type Keyword \
67
+ --name ajQueue --type Keyword \
68
+ --name ajJobId --type Keyword \
69
+ --name ajEnqueuedAt --type Datetime \
70
+ --name ajTenantId --type Int \
71
+ --name ajTags --type KeywordList
72
+ ```
73
+
74
+ Temporal Cloud deployments may use the `temporal` CLI instead of `tctl`; keep the same attribute names and types.
75
+
76
+ Define and enqueue normal ActiveJob jobs:
77
+
78
+ ```ruby
79
+ class SendInvoiceJob < ApplicationJob
80
+ queue_as :billing
81
+
82
+ retry_on SomeTransientError, wait: 60.seconds, attempts: 5
83
+ discard_on SomeFatalError
84
+
85
+ def perform(invoice_id)
86
+ invoice = Invoice.find(invoice_id)
87
+ InvoiceMailer.invoice_ready(invoice).deliver_now
88
+ end
89
+ end
90
+
91
+ SendInvoiceJob.perform_later(invoice.id)
92
+ SendInvoiceJob.set(wait: 5.minutes).perform_later(invoice.id)
93
+ SendInvoiceJob.set(tags: [:urgent, :customer_123]).perform_later(invoice.id)
94
+ ```
95
+
96
+ Start a worker for every task queue you use:
97
+
98
+ ```bash
99
+ ACTIVEJOB_TEMPORAL_TARGET=localhost:7233 \
100
+ ACTIVEJOB_TEMPORAL_NAMESPACE=default \
101
+ ACTIVEJOB_TEMPORAL_TASK_QUEUE=billing \
102
+ bundle exec temporal-worker
103
+ ```
104
+
105
+ Open Temporal UI and look for workflows named `ajwf:SendInvoiceJob:<job_id>`.
106
+
107
+ ## Common Capabilities
108
+
109
+ | Need | API | Detailed guide |
110
+ | --- | --- | --- |
111
+ | Delay a single job | `MyJob.set(wait: 5.minutes).perform_later(...)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#scheduled-jobs) |
112
+ | Register recurring cron work | `schedule cron: "0 2 * * *"` and `create_temporal_schedule` | [Recurring Jobs](https://github.com/schovi/activejob-temporal/blob/main/docs/recurring_jobs.md) |
113
+ | Enqueue only when work exists | `perform_later_if(condition, *args)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#conditional-enqueueing) |
114
+ | Enqueue many prepared jobs | `ActiveJob::Temporal.enqueue_batch(jobs)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#bulk-enqueueing) |
115
+ | Run sequential jobs in one workflow | `set(chain: [NextJob])` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#job-chaining) |
116
+ | Start child ActiveJob workflows | `set(child_workflows: [ChildJob])` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#child-workflows) |
117
+ | Call external Temporal activities or workflows | `ActiveJob::Temporal.activity(...)`, `ActiveJob::Temporal.workflow(...)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#external-temporal-steps) |
118
+ | Wait for separately enqueued jobs | `set(depends_on: parent_job)` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#job-dependencies) |
119
+ | Map ActiveJob retries to Temporal | `retry_on`, `discard_on` | [Retry Policy Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/retry_policies.md) |
120
+ | Park exhausted failures | `config.dead_letter_queue = "failed_jobs"` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#dead-letter-queue) |
121
+ | Tune activity timeouts | `temporal_options start_to_close_timeout: ...` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#per-job-timeouts) |
122
+ | Add throughput limits | `rate_limit 100, per: :second` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#rate-limit-configuration) |
123
+ | Cancel or inspect jobs | `cancel`, `cancel_all`, `status`, `running?` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#cancellation-and-status) |
124
+ | Pause, resume, query, or update workflow state | `signal`, `query`, `update` | [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md#signals-queries-and-updates) |
125
+ | Add runtime middleware | `config.add_middleware MiddlewareClass` | [Middleware](https://github.com/schovi/activejob-temporal/blob/main/docs/middleware.md) |
126
+ | Expose Prometheus metrics | `config.observability.use :prometheus` | [Metrics Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/metrics.md) |
127
+ | Encrypt job payloads | `encrypt_payload = true` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#payload-encryption) |
128
+ | Store large payloads externally | `payload_storage_adapter = MyPayloadStorage.new` | [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md#payload-size-limits) |
129
+
130
+ Baseline behavior also includes transaction-aware enqueueing through ActiveJob, GlobalID-compatible argument serialization, structured JSON logs, searchable `set(tags:)` metadata, and JSON payloads with opt-in MessagePack or Marshal envelopes.
131
+
132
+ ## Configuration
133
+
134
+ The full configuration surface lives in [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md). The machine-readable schema is [docs/config_schema.yaml](https://github.com/schovi/activejob-temporal/blob/main/docs/config_schema.yaml).
135
+
136
+ The most common settings are:
137
+
138
+ ```ruby
139
+ ActiveJob::Temporal.configure do |config|
140
+ config.target = "temporal.example.com:7233"
141
+ config.namespace = "production"
142
+ config.task_queue_prefix = "rails-"
143
+ config.priority_task_queues = { 10 => "high_priority", 90 => "low_priority" }
144
+ config.default_activity_timeout = 30.seconds
145
+ config.default_retry_initial_interval = 10.seconds
146
+ config.default_retry_backoff = 1.5
147
+ config.default_retry_max_attempts = 5
148
+ end
149
+ ```
150
+
151
+ Workers can also read environment variables such as `ACTIVEJOB_TEMPORAL_TARGET`, `ACTIVEJOB_TEMPORAL_NAMESPACE`, `ACTIVEJOB_TEMPORAL_TASK_QUEUE`, `ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES`, `ACTIVEJOB_TEMPORAL_METRICS_PORT`, and TLS certificate settings. See [Worker Setup](https://github.com/schovi/activejob-temporal/blob/main/docs/worker_setup.md) for the worker-focused list.
152
+
153
+ ## Documentation
154
+
155
+ Start with [docs/README.md](https://github.com/schovi/activejob-temporal/blob/main/docs/README.md) for the complete documentation map.
156
+
157
+ High-use guides:
158
+
159
+ - [Usage Patterns](https://github.com/schovi/activejob-temporal/blob/main/docs/usage_patterns.md)
160
+ - [Configuration Reference](https://github.com/schovi/activejob-temporal/blob/main/docs/configuration_reference.md)
161
+ - [Worker Setup](https://github.com/schovi/activejob-temporal/blob/main/docs/worker_setup.md)
162
+ - [Troubleshooting](https://github.com/schovi/activejob-temporal/blob/main/docs/troubleshooting.md)
163
+ - [Performance Tuning](https://github.com/schovi/activejob-temporal/blob/main/docs/performance_tuning.md)
164
+ - [Comparison Guide](https://github.com/schovi/activejob-temporal/blob/main/docs/comparison.md)
165
+ - [Security](https://github.com/schovi/activejob-temporal/blob/main/docs/security.md)
166
+
167
+ See [examples/basic_rails_app](https://github.com/schovi/activejob-temporal/tree/main/examples/basic_rails_app) for a Docker Compose Rails app with Temporal, Temporal UI, search attribute setup, workers, seeded GlobalID records, and tests.
168
+
169
+ ## Contributing
170
+
171
+ Install dependencies and run the focused local checks:
172
+
173
+ ```bash
174
+ rvm 4.0.3 do bundle install
175
+ rvm 4.0.3 do bundle exec rake spec:unit
176
+ rvm 4.0.3 do bundle exec rubocop
177
+ rvm 4.0.3 do bundle exec rake build
178
+ ```
179
+
180
+ For changes near configured mutation subjects, also run:
181
+
182
+ ```bash
183
+ rvm 4.0.3 do bundle exec rake mutation
184
+ ```
185
+
186
+ Keep docs updated when behavior changes. The scoped mutation task runs on Ruby 4. Mutant 0.16 may warn about an older parser dependency; keep using the Ruby 4 toolchain and treat parser failures on new Ruby syntax as a mutation tooling limitation.
187
+
188
+ Bug reports and feature requests belong in [GitHub issues](https://github.com/schovi/activejob-temporal/issues).
189
+
190
+ ## License
191
+
192
+ MIT. See [LICENSE](LICENSE).
193
+
194
+ ## Versioning
195
+
196
+ This project follows [Semantic Versioning](https://semver.org/). See [CHANGELOG](CHANGELOG.md) for release history.
197
+
198
+ Current development version: 0.1.0. Release commits must be tagged before publishing to RubyGems.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/activejob/temporal/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "activejob-temporal"
7
+ spec.version = ActiveJob::Temporal::VERSION
8
+ spec.authors = ["Temporal Technologies", "Ruby Community"]
9
+ spec.email = ["ruby@temporal.io"]
10
+
11
+ spec.summary = "Rails ActiveJob adapter backed by Temporal Workflows"
12
+ spec.description = <<~DESC
13
+ activejob-temporal bridges Rails ActiveJob with Temporal's durable execution engine.
14
+ It provides a drop-in ActiveJob adapter, Temporal workflows, and supporting tooling
15
+ so Rails apps gain fault-tolerant scheduling, retries, and observability with minimal changes.
16
+ DESC
17
+ spec.homepage = "https://github.com/schovi/activejob-temporal"
18
+ spec.license = "MIT"
19
+ spec.required_ruby_version = ">= 4.0"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/main"
23
+ spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/docs/README.md"
24
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
25
+ spec.metadata["rubygems_mfa_required"] = "true"
26
+
27
+ spec.files = Dir.chdir(__dir__) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ f.start_with?("spec/", "docs/", "examples/", "tmp/", "tools/", "gemfiles/", ".codemachine/", ".github/") ||
30
+ f == "CLAUDE.md" ||
31
+ f == "CONTRIBUTING.md" ||
32
+ f.match?(%r{^(\.|docker-compose\.yml|coverage/|Gemfile|Rakefile)})
33
+ end
34
+ end
35
+ spec.bindir = "bin"
36
+ spec.executables = ["temporal-worker"]
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_dependency "activejob", ">= 8.1", "< 9"
40
+ spec.add_dependency "activemodel", ">= 8.1", "< 9"
41
+ spec.add_dependency "concurrent-ruby", "~> 1.1"
42
+ spec.add_dependency "globalid", ">= 0.3"
43
+ spec.add_dependency "listen", "~> 3.9"
44
+ spec.add_dependency "temporalio", ">= 1.4.0", "< 1.5"
45
+
46
+ spec.add_development_dependency "benchmark-ips", "~> 2.14"
47
+ spec.add_development_dependency "bundler-audit", "~> 0.9"
48
+ spec.add_development_dependency "github_changelog_generator", "~> 1.18"
49
+ spec.add_development_dependency "msgpack", "~> 1.8"
50
+ spec.add_development_dependency "mutant-rspec", "~> 0.16"
51
+ spec.add_development_dependency "prometheus-client", "~> 4.2"
52
+ spec.add_development_dependency "rake", "~> 13.2"
53
+ spec.add_development_dependency "rspec", "~> 3.12"
54
+ spec.add_development_dependency "rubocop", "~> 1.50"
55
+ spec.add_development_dependency "simplecov", "~> 0.22"
56
+ spec.add_development_dependency "simplecov-lcov", "~> 0.9"
57
+ spec.add_development_dependency "yard", "~> 0.9", ">= 0.9.42"
58
+ end