activejob-temporal 0.1.0 → 0.2.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/AGENTS.md +1 -0
- data/CHANGELOG.md +18 -0
- data/CLAUDE.md +274 -0
- data/CONTRIBUTING.md +69 -0
- data/README.md +34 -33
- data/activejob-temporal.gemspec +8 -12
- data/api/job_payload_schema.json +51 -6
- data/bin/temporal-worker +3 -8
- data/gemfiles/activejob_contract.gemfile +8 -0
- data/gemfiles/temporalio_contract.gemfile +7 -0
- data/lib/activejob/temporal/activities/aj_runner_activity.rb +75 -19
- data/lib/activejob/temporal/activities/dependency_status_activity.rb +36 -19
- data/lib/activejob/temporal/adapter.rb +4 -3
- data/lib/activejob/temporal/batch_enqueuer.rb +42 -33
- data/lib/activejob/temporal/bind_policy.rb +1 -1
- data/lib/activejob/temporal/cancel.rb +66 -29
- data/lib/activejob/temporal/certificate_watcher.rb +41 -6
- data/lib/activejob/temporal/client.rb +4 -3
- data/lib/activejob/temporal/conditional_enqueue.rb +2 -1
- data/lib/activejob/temporal/configurable.rb +56 -13
- data/lib/activejob/temporal/configuration.rb +139 -7
- data/lib/activejob/temporal/configured_job_compatibility.rb +91 -9
- data/lib/activejob/temporal/connection_worker_pool.rb +18 -1
- data/lib/activejob/temporal/dead_letter_queue.rb +64 -18
- data/lib/activejob/temporal/dependency_options.rb +126 -16
- data/lib/activejob/temporal/health_check_server.rb +14 -17
- data/lib/activejob/temporal/http_line_reader.rb +20 -2
- data/lib/activejob/temporal/http_request_failure_handling.rb +41 -0
- data/lib/activejob/temporal/inspect.rb +17 -7
- data/lib/activejob/temporal/job_id_validation.rb +41 -0
- data/lib/activejob/temporal/job_payload_dependencies.rb +23 -0
- data/lib/activejob/temporal/locales/en.yml +15 -3
- data/lib/activejob/temporal/metrics_server.rb +15 -18
- data/lib/activejob/temporal/middleware/chain.rb +7 -0
- data/lib/activejob/temporal/observability.rb +14 -2
- data/lib/activejob/temporal/payload.rb +64 -25
- data/lib/activejob/temporal/payload_encryption.rb +9 -1
- data/lib/activejob/temporal/payload_serializers.rb +3 -0
- data/lib/activejob/temporal/payload_storage.rb +4 -4
- data/lib/activejob/temporal/rails_environment_loader.rb +1 -8
- data/lib/activejob/temporal/reload_signal_queue.rb +19 -19
- data/lib/activejob/temporal/retry_handler_extractor.rb +18 -3
- data/lib/activejob/temporal/schedulable.rb +5 -7
- data/lib/activejob/temporal/schedule.rb +2 -2
- data/lib/activejob/temporal/signal_query.rb +35 -23
- data/lib/activejob/temporal/temporal_options.rb +26 -1
- data/lib/activejob/temporal/tls_file.rb +16 -16
- data/lib/activejob/temporal/transaction_safety.rb +102 -1
- data/lib/activejob/temporal/version.rb +1 -1
- data/lib/activejob/temporal/visibility_query.rb +16 -1
- data/lib/activejob/temporal/worker_pool.rb +13 -0
- data/lib/activejob/temporal/worker_runtime.rb +16 -0
- data/lib/activejob/temporal/workflow_enqueuer.rb +2 -1
- data/lib/activejob/temporal/workflow_types.rb +10 -0
- data/lib/activejob/temporal/workflows/aj_workflow.rb +17 -4
- data/lib/activejob/temporal/workflows/workflow_dependencies.rb +148 -15
- data/lib/activejob/temporal.rb +2 -13
- data/test/mutant_unit_test.rb +13 -0
- metadata +43 -48
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9ea3a46138c59ad643942b6de5431013e9679812845aa2df1e465dfd368120c
|
|
4
|
+
data.tar.gz: 63232ab58e9ae604053cf68751ba803f5a66e5ad416f6762ad5ee74ea7eef4a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02c60806fecabadd3744ca003ab45a7ef4331c7f48a361bf3045d22f18f2386278b9c47b729f8d8bd4ad7d17605b3526955f0bd0bf4c2e3a75a9639ec4998620
|
|
7
|
+
data.tar.gz: 4b3a8d2a876fc3874be63c8c400d7a991c4f4a89d3579c56941eff32e1530a6658bdf338570ffa0c5705872618b39044c5c324646d01c4bbf8317f37def25b04
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
./CLAUDE.md
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-08-06
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- Reject payload-declared serializers not permitted by the configured `payload_serializer`, closing a `Marshal.load` code-execution path from attacker-supplied workflow input.
|
|
14
|
+
- Derive the AES-GCM payload encryption context from the activity context instead of the payload, so captured ciphertexts cannot be replayed into other workflows or namespaces.
|
|
15
|
+
- Reject legacy V1 (context-unbound) encrypted payloads by default; `allow_legacy_encrypted_payloads` opts back in during migration.
|
|
16
|
+
- Validate values interpolated into Temporal visibility queries with a strict allowlist, preventing query-structure injection through job IDs and class names in cancel/inspect APIs.
|
|
17
|
+
- Redact encryption keys and TLS settings from `Configuration#inspect`.
|
|
18
|
+
- Cap request line length and header count and enforce a whole-request deadline in the health check and metrics HTTP servers.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Retry certificate reloads after a failure so cert+key rotation pairs written within the debounce window are not dropped.
|
|
22
|
+
- Accept symlinked TLS certificate paths (resolved via `File.realpath`), unblocking Kubernetes secret volume mounts.
|
|
23
|
+
- Rescue `SystemCallError` in health check and metrics accept loops so errors like `EMFILE` no longer kill the listener silently.
|
|
24
|
+
- Reject overlapping worker pool health check and metrics port ranges at validation time instead of crash-looping the colliding child.
|
|
25
|
+
- Raise the documented `TemporalConnectionError` from client connect failures.
|
|
26
|
+
- Surface dead letter queue query failures instead of silently dropping entries, and fetch entries concurrently.
|
|
27
|
+
|
|
10
28
|
<!-- github_changelog_generator:start -->
|
|
11
29
|
|
|
12
30
|
**Implemented enhancements:**
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**activejob-temporal** is a Ruby gem providing a production-ready ActiveJob adapter backed by Temporal's durable execution engine. It enables Rails applications to leverage Temporal's reliability, observability, and fault-tolerance while maintaining full ActiveJob compatibility.
|
|
8
|
+
|
|
9
|
+
### Architecture Pattern
|
|
10
|
+
|
|
11
|
+
The gem uses a **Temporal Workflow + Activity** pattern:
|
|
12
|
+
1. **Adapter** (`lib/activejob/temporal/adapter.rb`): Translates ActiveJob's `perform_later` calls into Temporal workflow starts
|
|
13
|
+
2. **Workflow** (`lib/activejob/temporal/workflows/aj_workflow.rb`): Orchestrates job execution with durable timers for scheduled jobs
|
|
14
|
+
3. **Activity** (`lib/activejob/temporal/activities/aj_runner_activity.rb`): Executes the actual job logic
|
|
15
|
+
4. **Supporting Services**: Payload serialization, retry mapping, search attributes, logging
|
|
16
|
+
|
|
17
|
+
Each enqueued job becomes a workflow execution, with optional activities for the job logic and retry handling managed through Temporal's native mechanisms.
|
|
18
|
+
|
|
19
|
+
## Key Components
|
|
20
|
+
|
|
21
|
+
- **Configuration** (`lib/activejob/temporal.rb`): DSL-style setup with automatic validation (validates at end of configure block)
|
|
22
|
+
- **Client** (`lib/activejob/temporal/client.rb`): Temporal gRPC client builder with target/namespace/credentials handling
|
|
23
|
+
- **RetryMapper** (`lib/activejob/temporal/retry_mapper.rb`): Introspects ActiveJob `retry_on`/`discard_on` and converts to Temporal retry policies
|
|
24
|
+
- **Payload** (`lib/activejob/temporal/payload.rb`): Job argument serialization/deserialization with size validation
|
|
25
|
+
- **SearchAttributes** (`lib/activejob/temporal/search_attributes.rb`): Generates metadata for Temporal UI filtering
|
|
26
|
+
- **Logger** (`lib/activejob/temporal/logger.rb`): Structured logging with context awareness
|
|
27
|
+
- **Cancel** (`lib/activejob/temporal/cancel.rb`): Job cancellation API and workflow termination logic
|
|
28
|
+
|
|
29
|
+
## Development Commands
|
|
30
|
+
|
|
31
|
+
### Testing
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Run all tests (unit + integration)
|
|
35
|
+
bundle exec rake spec
|
|
36
|
+
|
|
37
|
+
# Unit tests only (faster feedback)
|
|
38
|
+
bundle exec rake spec:unit
|
|
39
|
+
|
|
40
|
+
# Integration tests only (requires Temporal running)
|
|
41
|
+
bundle exec rake spec:integration
|
|
42
|
+
|
|
43
|
+
# Run a specific test file
|
|
44
|
+
bundle exec ruby -Ilib -Ispec spec/unit/adapter_spec.rb
|
|
45
|
+
|
|
46
|
+
# Run a specific test example
|
|
47
|
+
bundle exec ruby -Ilib -Ispec spec/unit/adapter_spec.rb --name /example name/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Tests are organized by type in `spec/unit/` and `spec/integration/`. Integration tests use real Temporal workers to validate end-to-end behavior. See `spec/support/temporal_test_server.rb` for test Temporal setup.
|
|
51
|
+
|
|
52
|
+
### Linting and Code Quality
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Check for Rubocop violations
|
|
56
|
+
bundle exec rake rubocop
|
|
57
|
+
|
|
58
|
+
# Auto-fix violations
|
|
59
|
+
bundle exec rake rubocop:autocorrect
|
|
60
|
+
|
|
61
|
+
# JSON output for CI
|
|
62
|
+
./tools/lint.sh
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
All files must have `# frozen_string_literal: true` at the top.
|
|
66
|
+
|
|
67
|
+
### Documentation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Generate YARD documentation
|
|
71
|
+
bundle exec rake yard
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Use YARD annotations extensively:
|
|
75
|
+
- `@param` for arguments (include types)
|
|
76
|
+
- `@return` for return values
|
|
77
|
+
- `@raise` for exceptions
|
|
78
|
+
- `@example` for usage patterns
|
|
79
|
+
- `@note` for important caveats
|
|
80
|
+
- `@api private` for internal methods
|
|
81
|
+
|
|
82
|
+
### Temporal Setup (Local Development)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Start Temporal server, UI, and PostgreSQL
|
|
86
|
+
docker-compose up
|
|
87
|
+
|
|
88
|
+
# Temporal UI: http://localhost:8080
|
|
89
|
+
# Server: localhost:7233
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Running a Worker
|
|
93
|
+
|
|
94
|
+
The `temporal-worker` executable uses smart Rails auto-detection:
|
|
95
|
+
|
|
96
|
+
#### From a Rails Application
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cd your-rails-app
|
|
100
|
+
ACTIVEJOB_TEMPORAL_TARGET=localhost:7233 \
|
|
101
|
+
ACTIVEJOB_TEMPORAL_NAMESPACE=default \
|
|
102
|
+
ACTIVEJOB_TEMPORAL_TASK_QUEUE=default \
|
|
103
|
+
bundle exec temporal-worker
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The worker auto-detects the Rails app in the current directory and loads your environment.
|
|
107
|
+
|
|
108
|
+
#### From the Gem Directory (Development)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
ACTIVEJOB_TEMPORAL_TARGET=localhost:7233 \
|
|
112
|
+
ACTIVEJOB_TEMPORAL_NAMESPACE=default \
|
|
113
|
+
ACTIVEJOB_TEMPORAL_TASK_QUEUE=default \
|
|
114
|
+
bin/temporal-worker
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
When run from the gem directory (not a Rails app), it works without Rails.
|
|
118
|
+
|
|
119
|
+
#### Specifying a Custom Path
|
|
120
|
+
|
|
121
|
+
If you need to run the worker from a different directory, use `RAILS_ROOT`:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
RAILS_ROOT=/path/to/app \
|
|
125
|
+
bundle exec temporal-worker
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The worker will load the Rails app at that path.
|
|
129
|
+
|
|
130
|
+
#### Performance Tuning
|
|
131
|
+
|
|
132
|
+
Control concurrency for different environments:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# High-throughput production
|
|
136
|
+
ACTIVEJOB_TEMPORAL_TARGET=temporal.prod:7233 \
|
|
137
|
+
ACTIVEJOB_TEMPORAL_NAMESPACE=production \
|
|
138
|
+
ACTIVEJOB_TEMPORAL_TASK_QUEUE=jobs \
|
|
139
|
+
ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES=500 \
|
|
140
|
+
ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_WORKFLOW_TASKS=50 \
|
|
141
|
+
bundle exec temporal-worker
|
|
142
|
+
|
|
143
|
+
# Low-resource development
|
|
144
|
+
ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_ACTIVITIES=20 \
|
|
145
|
+
ACTIVEJOB_TEMPORAL_MAX_CONCURRENT_WORKFLOW_TASKS=2 \
|
|
146
|
+
bundle exec temporal-worker
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
See `docs/worker_setup.md` for detailed tuning guidance.
|
|
150
|
+
|
|
151
|
+
### Build and Release
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Build gem
|
|
155
|
+
gem build activejob-temporal.gemspec
|
|
156
|
+
|
|
157
|
+
# Full validation (Rubocop + tests)
|
|
158
|
+
bundle exec rake default
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Code Conventions
|
|
162
|
+
|
|
163
|
+
### Structure and Style
|
|
164
|
+
|
|
165
|
+
- **Module composition** over inheritance; use modules for mixins
|
|
166
|
+
- **Explicit requires** at top of files
|
|
167
|
+
- **Frozen string literals** on every file: `# frozen_string_literal: true`
|
|
168
|
+
- **Proper namespacing**: `ActiveJob::Temporal::Workflows::AjWorkflow`
|
|
169
|
+
- **Visibility markers**: Use `private` or `private_class_method` explicitly
|
|
170
|
+
|
|
171
|
+
### Naming
|
|
172
|
+
|
|
173
|
+
- **Classes**: PascalCase (`TemporalAdapter`, `AjWorkflow`)
|
|
174
|
+
- **Methods**: snake_case (`build_workflow_id`, `from_job`)
|
|
175
|
+
- **Constants**: SCREAMING_SNAKE_CASE (used sparingly)
|
|
176
|
+
|
|
177
|
+
### Error Handling
|
|
178
|
+
|
|
179
|
+
The gem uses a custom exception hierarchy rooted at `ActiveJob::Temporal::Error`:
|
|
180
|
+
- `ConfigurationError`: Invalid or missing configuration
|
|
181
|
+
- `WorkflowNotFoundError`: Job not found in Temporal
|
|
182
|
+
- `TemporalConnectionError`: Network or server issues
|
|
183
|
+
|
|
184
|
+
**Pattern**: Always provide human-readable error messages with context. Wrap external Temporal errors into domain-specific exceptions for cleaner error handling in client code.
|
|
185
|
+
|
|
186
|
+
### Configuration Pattern
|
|
187
|
+
|
|
188
|
+
Configuration uses a DSL pattern with **automatic validation**:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
ActiveJob::Temporal.configure do |config|
|
|
192
|
+
config.target = "localhost:7233"
|
|
193
|
+
config.namespace = "default"
|
|
194
|
+
# Additional settings...
|
|
195
|
+
# Validation happens automatically at the end of this block!
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Configuration is automatically validated at the end of the `configure` block. By default validation failures raise `ConfigurationError` immediately. `config.validation_level` can downgrade failures to warnings with `:warn` or skip validation with `:none`. You can also manually validate by calling `ActiveJob::Temporal.config.validate!` if you modify the config directly outside the block.
|
|
200
|
+
|
|
201
|
+
Environment variables with defaults are used for sensitive values (e.g., `ENV.fetch("TEMPORAL_TARGET", default)`).
|
|
202
|
+
|
|
203
|
+
## Testing Patterns
|
|
204
|
+
|
|
205
|
+
### Test Organization
|
|
206
|
+
|
|
207
|
+
- **Unit tests** (`spec/unit/`): Isolated component tests with mocking/stubbing
|
|
208
|
+
- **Integration tests** (`spec/integration/`): End-to-end tests using real Temporal
|
|
209
|
+
- **Fixtures** (`spec/fixtures/sample_jobs.rb`): Sample job classes with various retry/discard configurations
|
|
210
|
+
|
|
211
|
+
### SimpleCov and Coverage
|
|
212
|
+
|
|
213
|
+
Tests use SimpleCov with branch coverage enabled. Target coverage is ≥90%. Run tests to generate coverage reports.
|
|
214
|
+
|
|
215
|
+
### Test Isolation
|
|
216
|
+
|
|
217
|
+
Use `around` hooks and shared contexts to reset state between tests. Integration tests manage Temporal workers within test lifecycle.
|
|
218
|
+
|
|
219
|
+
### Common Patterns
|
|
220
|
+
|
|
221
|
+
- Mock Temporal client responses for unit tests
|
|
222
|
+
- Use real workers in integration tests
|
|
223
|
+
- Test both success and failure paths (retries, discards)
|
|
224
|
+
- Validate error messages and exception types
|
|
225
|
+
|
|
226
|
+
## Workflow Implementation Notes
|
|
227
|
+
|
|
228
|
+
**Determinism**: Workflows must be deterministic—no I/O, randomness, or system time calls in the main workflow logic. Use activities for these operations.
|
|
229
|
+
|
|
230
|
+
**Durable Timers**: For scheduled jobs, workflows use Temporal's durable sleep mechanism. This persists across workflow replays and cluster restarts.
|
|
231
|
+
|
|
232
|
+
**RetryPolicy Mapping**: The `RetryMapper` introspects ActiveJob's `retry_on` and `discard_on` declarations and converts them to Temporal native retry policies. Understand this mapping when debugging job retries.
|
|
233
|
+
|
|
234
|
+
## Git Commit Conventions
|
|
235
|
+
|
|
236
|
+
Use **Conventional Commits** format: `<type>(<scope>): <description>`
|
|
237
|
+
|
|
238
|
+
**Types**:
|
|
239
|
+
- `feat`: New features
|
|
240
|
+
- `fix`: Bug fixes
|
|
241
|
+
- `docs`: Documentation changes
|
|
242
|
+
- `chore`: Maintenance, configuration, tooling
|
|
243
|
+
- `test`: Test additions/updates
|
|
244
|
+
- `refactor`: Code structure improvements (avoid in this project—prefer feature/fix commits)
|
|
245
|
+
|
|
246
|
+
**Common scopes**:
|
|
247
|
+
- `config`: Configuration handling
|
|
248
|
+
- `payload`: Job serialization
|
|
249
|
+
- `cancel`: Cancellation logic
|
|
250
|
+
- `adapter`: ActiveJob adapter
|
|
251
|
+
- `workflows`: Workflow implementations
|
|
252
|
+
- `activities`: Activity implementations
|
|
253
|
+
- `docs`: Documentation
|
|
254
|
+
- `yard`: Inline documentation
|
|
255
|
+
- `tasks`: Task tracking/checklists
|
|
256
|
+
- `ci`: CI/CD pipeline
|
|
257
|
+
- `release`: Release processes
|
|
258
|
+
|
|
259
|
+
**Example**: `feat(config): add worker performance tuning options`
|
|
260
|
+
|
|
261
|
+
Keep commits small and focused. A feature might consist of several commits: implementation, tests, documentation.
|
|
262
|
+
|
|
263
|
+
## Architecture Decision Records
|
|
264
|
+
|
|
265
|
+
See `docs/adr/` for recorded architectural decisions and rationale behind major design choices.
|
|
266
|
+
|
|
267
|
+
## Additional Resources
|
|
268
|
+
|
|
269
|
+
- **Configuration Reference**: `docs/configuration_reference.md`
|
|
270
|
+
- **Comparison Guide**: `docs/comparison.md` (choosing the right ActiveJob backend)
|
|
271
|
+
- **Worker Setup**: `docs/worker_setup.md`
|
|
272
|
+
- **Release Process**: `docs/publishing.md`, `docs/release_checklist.md`
|
|
273
|
+
- **README**: Comprehensive user guide and quick start
|
|
274
|
+
- **Temporal Docs**: https://docs.temporal.io/ (for workflow/activity patterns, API reference)
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Ruby Baseline
|
|
4
|
+
|
|
5
|
+
Use Ruby 4.0.3 for local development and validation. The repository targets Ruby 4+ and does not require installing Ruby 3.
|
|
6
|
+
|
|
7
|
+
See [Ruby Baseline](docs/ruby_baseline.md) for the source-of-truth files, CI coverage, and external tooling notes.
|
|
8
|
+
|
|
9
|
+
Run validation commands through the Ruby 4 toolchain:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
rvm 4.0.3 do bundle install
|
|
13
|
+
rvm 4.0.3 do bundle exec rake spec:unit
|
|
14
|
+
rvm 4.0.3 do bundle exec rubocop
|
|
15
|
+
rvm 4.0.3 do bundle exec rake build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Mutation Testing
|
|
19
|
+
|
|
20
|
+
Mutation testing runs a scoped Mutant baseline against deterministic unit-level code:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
rvm 4.0.3 do bundle exec rake mutation
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The default subject list is intentionally small so the task stays fast and does not require a local Temporal server. Expand `.mutant.yml` as specs are hardened around additional code paths.
|
|
27
|
+
|
|
28
|
+
Mutant 0.16 supports Ruby 4, but its parser dependency may warn about an older parser version. Treat new Ruby syntax parse failures as a Mutant tooling limitation and keep the repository runtime baseline on Ruby 4.
|
|
29
|
+
|
|
30
|
+
## Dependency Updates
|
|
31
|
+
|
|
32
|
+
Dependabot checks root Bundler dependencies weekly and opens up to five update pull requests at a time.
|
|
33
|
+
|
|
34
|
+
Development dependency minor and patch updates are grouped into a single pull request. Review dependency pull requests like any other change: confirm CI passes, scan the changelog for breaking behavior, and keep major updates separate unless the dependency explicitly documents compatibility.
|
|
35
|
+
|
|
36
|
+
## Code Quality Tracking
|
|
37
|
+
|
|
38
|
+
Qlty, the successor to Code Climate Quality, reads the committed `.qlty/qlty.toml` analysis configuration.
|
|
39
|
+
|
|
40
|
+
The current configuration focuses maintainability tracking on the library code and excludes generated or packaged artifacts. RuboCop findings are monitored in Qlty while the existing GitHub Actions lint job remains the blocking local validation path.
|
|
41
|
+
|
|
42
|
+
Do not add a README maintainability badge until the repository has been added to Qlty and the generated project badge URL is available.
|
|
43
|
+
|
|
44
|
+
## Changelog
|
|
45
|
+
|
|
46
|
+
Generate release changelog updates from merged GitHub pull requests and closed issues:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
CHANGELOG_GITHUB_TOKEN=... rvm 4.0.3 do bundle exec rake changelog:generate
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The task also accepts `GITHUB_TOKEN` when `CHANGELOG_GITHUB_TOKEN` is not set. Review the generated `CHANGELOG.md` before committing release notes.
|
|
53
|
+
|
|
54
|
+
Generated unreleased sections are rebuilt from GitHub issues and pull requests. Existing released sections stay in place so curated release notes are not lost.
|
|
55
|
+
|
|
56
|
+
## Release Commits
|
|
57
|
+
|
|
58
|
+
Release automation is not enabled yet.
|
|
59
|
+
|
|
60
|
+
Use conventional commit prefixes where they describe the change:
|
|
61
|
+
|
|
62
|
+
- `feat:` for user-visible features
|
|
63
|
+
- `fix:` for bug fixes
|
|
64
|
+
- `docs:` for documentation-only changes
|
|
65
|
+
- `test:` for test-only changes
|
|
66
|
+
- `ci:` for GitHub Actions or CI changes
|
|
67
|
+
- `chore:` for maintenance that does not affect runtime behavior
|
|
68
|
+
|
|
69
|
+
Do not rely on commit messages to publish a release until [Publishing](docs/publishing.md) says release automation is enabled.
|
data/README.md
CHANGED
|
@@ -18,11 +18,10 @@ Use a traditional ActiveJob backend when the work is short, simple, and does not
|
|
|
18
18
|
## Requirements
|
|
19
19
|
|
|
20
20
|
- Ruby >= 4.0
|
|
21
|
-
- Rails
|
|
21
|
+
- Rails >= 7.2 through ActiveJob 8.x
|
|
22
22
|
- Temporal cluster, either self-hosted or [Temporal Cloud](https://temporal.io/cloud)
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
Temporal Ruby SDK 1.4.x compatibility is contract-tested against 1.4.0 and the latest 1.4.x release.
|
|
24
|
+
Temporal Ruby SDK 1.4.x compatibility is contract-tested against 1.4.0 and 1.4.1.
|
|
26
25
|
|
|
27
26
|
## Install
|
|
28
27
|
|
|
@@ -104,34 +103,36 @@ bundle exec temporal-worker
|
|
|
104
103
|
|
|
105
104
|
Open Temporal UI and look for workflows named `ajwf:SendInvoiceJob:<job_id>`.
|
|
106
105
|
|
|
106
|
+
`require "activejob/temporal"` loads the Rails adapter, enqueue path, schedules, lookup APIs, and shared configuration. Worker-only runtime code is loaded by `temporal-worker`; custom worker boot code should require `activejob/temporal/worker_runtime` before registering workflows or activities.
|
|
107
|
+
|
|
107
108
|
## Common Capabilities
|
|
108
109
|
|
|
109
110
|
| Need | API | Detailed guide |
|
|
110
111
|
| --- | --- | --- |
|
|
111
|
-
| Delay a single job | `MyJob.set(wait: 5.minutes).perform_later(...)` | [Usage Patterns](
|
|
112
|
-
| Register recurring cron work | `
|
|
113
|
-
| Enqueue only when work exists | `perform_later_if(condition, *args)` | [Usage Patterns](
|
|
114
|
-
| Enqueue many prepared jobs | `ActiveJob::Temporal.enqueue_batch(jobs)` | [Usage Patterns](
|
|
115
|
-
| Run sequential jobs in one workflow | `set(chain: [NextJob])` | [Usage Patterns](
|
|
116
|
-
| Start child ActiveJob workflows | `set(child_workflows: [ChildJob])` | [Usage Patterns](
|
|
117
|
-
| Call external Temporal activities or workflows | `ActiveJob::Temporal.activity(...)`, `ActiveJob::Temporal.workflow(...)` | [Usage Patterns](
|
|
118
|
-
| Wait for separately enqueued jobs | `set(depends_on: parent_job)` | [Usage Patterns](
|
|
119
|
-
| Map ActiveJob retries to Temporal | `retry_on`, `discard_on` | [Retry Policy Guide](
|
|
120
|
-
| Park exhausted failures | `config.dead_letter_queue = "failed_jobs"` | [Configuration Reference](
|
|
121
|
-
| Tune activity timeouts | `temporal_options start_to_close_timeout: ...` | [Usage Patterns](
|
|
122
|
-
| Add throughput limits | `rate_limit 100, per: :second` | [Configuration Reference](
|
|
123
|
-
| Cancel or inspect jobs | `cancel`, `cancel_all`, `status`, `running?` | [Usage Patterns](
|
|
124
|
-
| Pause, resume, query, or update workflow state | `signal`, `query`, `update` | [Usage Patterns](
|
|
125
|
-
| Add runtime middleware | `config.add_middleware MiddlewareClass` | [Middleware](
|
|
126
|
-
| Expose Prometheus metrics | `config.observability.use :prometheus` | [Metrics Guide](
|
|
127
|
-
| Encrypt job payloads | `encrypt_payload = true` | [Configuration Reference](
|
|
128
|
-
| Store large payloads externally | `payload_storage_adapter = MyPayloadStorage.new` | [Configuration Reference](
|
|
112
|
+
| Delay a single job | `MyJob.set(wait: 5.minutes).perform_later(...)` | [Usage Patterns](docs/usage_patterns.md#scheduled-jobs) |
|
|
113
|
+
| Register recurring cron work | `temporal_schedule cron: "0 2 * * *"` and `create_temporal_schedule` | [Recurring Jobs](docs/recurring_jobs.md) |
|
|
114
|
+
| Enqueue only when work exists | `perform_later_if(condition, *args)` | [Usage Patterns](docs/usage_patterns.md#conditional-enqueueing) |
|
|
115
|
+
| Enqueue many prepared jobs | `ActiveJob::Temporal.enqueue_batch(jobs)` | [Usage Patterns](docs/usage_patterns.md#bulk-enqueueing) |
|
|
116
|
+
| Run sequential jobs in one workflow | `set(chain: [NextJob])` | [Usage Patterns](docs/usage_patterns.md#job-chaining) |
|
|
117
|
+
| Start child ActiveJob workflows | `set(child_workflows: [ChildJob])` | [Usage Patterns](docs/usage_patterns.md#child-workflows) |
|
|
118
|
+
| Call external Temporal activities or workflows | `ActiveJob::Temporal.activity(...)`, `ActiveJob::Temporal.workflow(...)` | [Usage Patterns](docs/usage_patterns.md#external-temporal-steps) |
|
|
119
|
+
| Wait for separately enqueued jobs | `set(depends_on: parent_job)` | [Usage Patterns](docs/usage_patterns.md#job-dependencies) |
|
|
120
|
+
| Map ActiveJob retries to Temporal | `retry_on`, `discard_on` | [Retry Policy Guide](docs/retry_policies.md) |
|
|
121
|
+
| Park exhausted failures | `config.dead_letter_queue = "failed_jobs"` | [Configuration Reference](docs/configuration_reference.md#dead-letter-queue) |
|
|
122
|
+
| Tune activity timeouts | `temporal_options start_to_close_timeout: ...` | [Usage Patterns](docs/usage_patterns.md#per-job-timeouts) |
|
|
123
|
+
| Add throughput limits | `rate_limit 100, per: :second` | [Configuration Reference](docs/configuration_reference.md#rate-limit-configuration) |
|
|
124
|
+
| Cancel or inspect jobs | `cancel`, `cancel_all`, `status`, `running?` | [Usage Patterns](docs/usage_patterns.md#cancellation-and-status) |
|
|
125
|
+
| Pause, resume, query, or update workflow state | `signal`, `query`, `update` | [Usage Patterns](docs/usage_patterns.md#signals-queries-and-updates) |
|
|
126
|
+
| Add runtime middleware | `config.add_middleware MiddlewareClass` | [Middleware](docs/middleware.md) |
|
|
127
|
+
| Expose Prometheus metrics | `config.observability.use :prometheus` | [Metrics Guide](docs/metrics.md) |
|
|
128
|
+
| Encrypt job payloads | `encrypt_payload = true` | [Configuration Reference](docs/configuration_reference.md#payload-encryption) |
|
|
129
|
+
| Store large payloads externally | `payload_storage_adapter = MyPayloadStorage.new` | [Configuration Reference](docs/configuration_reference.md#payload-size-limits) |
|
|
129
130
|
|
|
130
131
|
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
|
|
|
132
133
|
## Configuration
|
|
133
134
|
|
|
134
|
-
The full configuration surface lives in [Configuration Reference](
|
|
135
|
+
The full configuration surface lives in [Configuration Reference](docs/configuration_reference.md). The machine-readable schema is [docs/config_schema.yaml](docs/config_schema.yaml).
|
|
135
136
|
|
|
136
137
|
The most common settings are:
|
|
137
138
|
|
|
@@ -148,23 +149,23 @@ ActiveJob::Temporal.configure do |config|
|
|
|
148
149
|
end
|
|
149
150
|
```
|
|
150
151
|
|
|
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](
|
|
152
|
+
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](docs/worker_setup.md) for the worker-focused list.
|
|
152
153
|
|
|
153
154
|
## Documentation
|
|
154
155
|
|
|
155
|
-
Start with [docs/README.md](
|
|
156
|
+
Start with [docs/README.md](docs/README.md) for the complete documentation map.
|
|
156
157
|
|
|
157
158
|
High-use guides:
|
|
158
159
|
|
|
159
|
-
- [Usage Patterns](
|
|
160
|
-
- [Configuration Reference](
|
|
161
|
-
- [Worker Setup](
|
|
162
|
-
- [Troubleshooting](
|
|
163
|
-
- [Performance Tuning](
|
|
164
|
-
- [Comparison Guide](
|
|
165
|
-
- [Security](
|
|
160
|
+
- [Usage Patterns](docs/usage_patterns.md)
|
|
161
|
+
- [Configuration Reference](docs/configuration_reference.md)
|
|
162
|
+
- [Worker Setup](docs/worker_setup.md)
|
|
163
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
164
|
+
- [Performance Tuning](docs/performance_tuning.md)
|
|
165
|
+
- [Comparison Guide](docs/comparison.md)
|
|
166
|
+
- [Security](docs/security.md)
|
|
166
167
|
|
|
167
|
-
See [examples/basic_rails_app](
|
|
168
|
+
See [examples/basic_rails_app](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
|
|
|
169
170
|
## Contributing
|
|
170
171
|
|
|
@@ -195,4 +196,4 @@ MIT. See [LICENSE](LICENSE).
|
|
|
195
196
|
|
|
196
197
|
This project follows [Semantic Versioning](https://semver.org/). See [CHANGELOG](CHANGELOG.md) for release history.
|
|
197
198
|
|
|
198
|
-
Current
|
|
199
|
+
Current version: 0.1.0
|
data/activejob-temporal.gemspec
CHANGED
|
@@ -19,16 +19,13 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.required_ruby_version = ">= 4.0"
|
|
20
20
|
|
|
21
21
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
-
spec.metadata["source_code_uri"] =
|
|
23
|
-
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/docs/README.md"
|
|
22
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
24
23
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
25
24
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
26
25
|
|
|
27
26
|
spec.files = Dir.chdir(__dir__) do
|
|
28
27
|
`git ls-files -z`.split("\x0").reject do |f|
|
|
29
|
-
f.start_with?("spec/", "docs/", "examples/", "tmp/", "tools/", "
|
|
30
|
-
f == "CLAUDE.md" ||
|
|
31
|
-
f == "CONTRIBUTING.md" ||
|
|
28
|
+
f.start_with?("spec/", "docs/", "examples/", "tmp/", "tools/", ".codemachine/", ".github/") ||
|
|
32
29
|
f.match?(%r{^(\.|docker-compose\.yml|coverage/|Gemfile|Rakefile)})
|
|
33
30
|
end
|
|
34
31
|
end
|
|
@@ -36,23 +33,22 @@ Gem::Specification.new do |spec|
|
|
|
36
33
|
spec.executables = ["temporal-worker"]
|
|
37
34
|
spec.require_paths = ["lib"]
|
|
38
35
|
|
|
39
|
-
spec.add_dependency "activejob", ">=
|
|
40
|
-
spec.add_dependency "activemodel", ">=
|
|
36
|
+
spec.add_dependency "activejob", ">= 7.2", "< 9"
|
|
37
|
+
spec.add_dependency "activemodel", ">= 7.2", "< 9"
|
|
41
38
|
spec.add_dependency "concurrent-ruby", "~> 1.1"
|
|
42
39
|
spec.add_dependency "globalid", ">= 0.3"
|
|
43
|
-
spec.add_dependency "listen", "~> 3.9"
|
|
44
40
|
spec.add_dependency "temporalio", ">= 1.4.0", "< 1.5"
|
|
45
41
|
|
|
46
42
|
spec.add_development_dependency "benchmark-ips", "~> 2.14"
|
|
47
|
-
spec.add_development_dependency "bundler-audit", "~> 0.9"
|
|
48
43
|
spec.add_development_dependency "github_changelog_generator", "~> 1.18"
|
|
44
|
+
spec.add_development_dependency "listen", "~> 3.9"
|
|
45
|
+
spec.add_development_dependency "minitest", ">= 5.11", "< 7"
|
|
49
46
|
spec.add_development_dependency "msgpack", "~> 1.8"
|
|
50
|
-
spec.add_development_dependency "mutant-
|
|
47
|
+
spec.add_development_dependency "mutant-minitest", "~> 0.16"
|
|
51
48
|
spec.add_development_dependency "prometheus-client", "~> 4.2"
|
|
52
49
|
spec.add_development_dependency "rake", "~> 13.2"
|
|
53
|
-
spec.add_development_dependency "rspec", "~> 3.12"
|
|
54
50
|
spec.add_development_dependency "rubocop", "~> 1.50"
|
|
55
51
|
spec.add_development_dependency "simplecov", "~> 0.22"
|
|
56
52
|
spec.add_development_dependency "simplecov-lcov", "~> 0.9"
|
|
57
|
-
spec.add_development_dependency "yard", "~> 0.9"
|
|
53
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
|
58
54
|
end
|