active_durable 0.5.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +131 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +630 -0
  5. data/Rakefile +12 -0
  6. data/app/controllers/active_durable/application_controller.rb +25 -0
  7. data/app/controllers/active_durable/executions_controller.rb +59 -0
  8. data/app/helpers/active_durable/dashboard_helper.rb +163 -0
  9. data/app/views/active_durable/executions/index.html.erb +90 -0
  10. data/app/views/active_durable/executions/show.html.erb +197 -0
  11. data/app/views/layouts/active_durable/application.html.erb +422 -0
  12. data/config/routes.rb +13 -0
  13. data/lib/active_durable/configuration.rb +59 -0
  14. data/lib/active_durable/engine.rb +24 -0
  15. data/lib/active_durable/errors.rb +61 -0
  16. data/lib/active_durable/execution.rb +43 -0
  17. data/lib/active_durable/flow.rb +277 -0
  18. data/lib/active_durable/flow_parallel.rb +200 -0
  19. data/lib/active_durable/lease.rb +50 -0
  20. data/lib/active_durable/notebook.rb +100 -0
  21. data/lib/active_durable/open_telemetry.rb +94 -0
  22. data/lib/active_durable/operations.rb +111 -0
  23. data/lib/active_durable/parallel.rb +54 -0
  24. data/lib/active_durable/record.rb +13 -0
  25. data/lib/active_durable/registry.rb +89 -0
  26. data/lib/active_durable/retry_policy.rb +42 -0
  27. data/lib/active_durable/run_job.rb +12 -0
  28. data/lib/active_durable/runner.rb +157 -0
  29. data/lib/active_durable/serializer.rb +40 -0
  30. data/lib/active_durable/signal_record.rb +20 -0
  31. data/lib/active_durable/step.rb +34 -0
  32. data/lib/active_durable/sweep_job.rb +12 -0
  33. data/lib/active_durable/sweeper.rb +22 -0
  34. data/lib/active_durable/testing.rb +118 -0
  35. data/lib/active_durable/version.rb +5 -0
  36. data/lib/active_durable.rb +145 -0
  37. data/lib/generators/active_durable/install/install_generator.rb +27 -0
  38. data/lib/generators/active_durable/install/templates/create_active_durable_tables.rb.tt +60 -0
  39. data/lib/tasks/active_durable.rake +24 -0
  40. data/sig/active_durable.rbs +4 -0
  41. metadata +134 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 593d546eb6e8e46a70c03be3945934ed0c8a189937a5a27256478783cd345df0
4
+ data.tar.gz: 3226b77ada034c6bf49830dfdb30457904e14104b430b4cfb0a9def3d3de4124
5
+ SHA512:
6
+ metadata.gz: f0f0adcff14966f0b8cf7710218a6a6df9b27152593ea4d0d29c90f1968967817b467c33b9803dac0b7aaec6ff105c291a50ecf1335f15882b1969c4d7f7d2bd
7
+ data.tar.gz: 1ecba4e399e4f9a247ea9717e9727a50c86d7389aa41a4e53fe3aa9817a100e04a91ecfdc7a2dc51333b24ae9b94a666f1a3c2d5e1987132a48949970a210592
data/CHANGELOG.md ADDED
@@ -0,0 +1,131 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [Semantic Versioning](https://semver.org/). Before 1.0 the database schema may change between minor versions:
6
+ regenerate the migration when upgrading.
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Changed
11
+
12
+ - README: the quick start recipe reads top to bottom, with one-line undos and the Stripe calls in a `Payments`
13
+ module where charging and refunding sit side by side. A new section, "In a Rails app", sets up a Rails app step by
14
+ step: install, job backend, sweeper, the initializer with every setting, routes, where each piece of code goes
15
+ (recipe, service, controller, model, view), how the job is managed when the gem owns it, tests, and a checklist
16
+ before going to production. The settings moved there from "Observability".
17
+ - Specs cover undos without arguments (`-> { ... }`), a `Method` as an undo, and `flow.abort!` inside a step, which
18
+ skips the step's remaining retries.
19
+
20
+ ## [0.5.0] - 2026-10-06
21
+
22
+ More Ruby and Rails versions, and a README in two languages.
23
+
24
+ ### Changed
25
+
26
+ - Supports Ruby 3.1+ and Rails 6.1+ (was Ruby 3.3+ and Rails 7.2+), with every feature on every version. The
27
+ notebook no longer relies on `ActiveRecord.after_all_transactions_commit`: `flow.transaction` steps and their
28
+ undos run inside `Notebook#transaction`, which remembers their writes only once the transaction commits.
29
+ - CI covers Ruby 3.1, 3.2, 3.3, 3.4 and 4.0 against Rails 6.1, 7.0, 7.1, 7.2, 8.0 and 8.1 and the three
30
+ databases (84 jobs). MySQL runs on mysql2 for Rails 6.1 and 7.0, and on trilogy from 7.1. Each job resolves its
31
+ own gems from `gemfiles/rails-X.Y.gemfile`; per-Rails lockfiles are no longer committed, so they can never fall
32
+ behind the gem version (which broke the 0.4.0 CI run).
33
+ - The README is rewritten, with animated explanations (`docs/assets/*.svg`, built by `docs/assets/generate.rb`),
34
+ dashboard screenshots, a state diagram and a compatibility table.
35
+ - The README also exists in Spanish (`README.es.md`, with its own animations). `CONTRIBUTING.md` and `CLAUDE.md`
36
+ ask to update both, and `spec/readme_spec.rb` fails when they drift apart.
37
+ - Dashboard: long step names wrap at underscores, and notebook results get room to breathe.
38
+
39
+ ### Fixed
40
+
41
+ - Dashboard: on Rails 6.1 and 7.0, `Rails.env.local?` silently answered false, which would have closed the
42
+ dashboard in development too. It now checks for development and test explicitly.
43
+ - Migration: datetime columns are created with microsecond precision on every Rails version.
44
+ - The packaged gem no longer includes `gemfiles/`.
45
+
46
+ ## [0.4.0] - 2026-10-05
47
+
48
+ The dashboard, redrawn.
49
+
50
+ ### Changed
51
+
52
+ - The dashboard is redrawn full screen: a rail with statuses and recipes, and every saga shown as a row of blocks.
53
+ Each block's animation tells its state (running beats, a waiting step pings like a radar, a sleeping one fills
54
+ a ring until it wakes, a failed one shakes, undone ones are striped and wired backwards), with a visible point
55
+ of no return, live countdowns, and a live mode that refreshes the list and flashes the rows that changed.
56
+ Animations stop when the system asks for reduced motion.
57
+ - The gemspec links to the GitHub repository (homepage, source, changelog and issues).
58
+
59
+ ## [0.3.0] - 2026-10-05
60
+
61
+ Third version: everything the design called "later", except the Rust gem.
62
+
63
+ ### Added
64
+
65
+ - `flow.parallel`: branches that run in threads, each checkpointed with its own ticket and retries. Unfinished
66
+ branches resume after a crash; completed ones are undone in the order they finished. Branch threads run inside
67
+ the Rails executor and keep OpenTelemetry context.
68
+ - Recipe versions: `Durable.define(name, version:)`, executions keep the version they started with,
69
+ `ActiveDurable.versions_in_use` and `rake active_durable:versions`.
70
+ - `ActiveDurable::OpenTelemetry.install!`: nested spans for executions, steps, compensations and undos.
71
+ - `ActiveDurable.branch_wrappers` to carry other thread-local context into parallel branches.
72
+
73
+ ### Fixed
74
+
75
+ - The dashboard was open in every environment except production (staging included). Without
76
+ `config.dashboard_authorize` it now opens only in development and test.
77
+
78
+ ### Changed
79
+
80
+ - MySQL 8+ and SQLite 3 are supported and run the full suite, like PostgreSQL. Tested on Rails 7.2, 8.0 and 8.1.
81
+ - The notebook is safe to write from several threads, and only remembers writes whose transaction committed.
82
+ - The registry looks the recipe constant up on every use, so an edited recipe is picked up after a code reload
83
+ in development.
84
+ - One Gemfile per Rails version (`gemfiles/`) with lockfiles for Linux, used by the CI matrix.
85
+
86
+ ## [0.2.0] - 2026-10-05
87
+
88
+ Second version: see it and operate it.
89
+
90
+ ### Added
91
+
92
+ - `ActiveDurable.retry`, `ActiveDurable.compensate` and `ActiveDurable.rerun` (see `ActiveDurable::Operations`).
93
+ They refuse executions a worker holds and rotate the lease token.
94
+ - Reruns: a new execution (`<id>~rerun-N`, `forked_from`) that reuses the completed steps before a chosen step.
95
+ A blocked original becomes `superseded`.
96
+ - The dashboard engine (`mount ActiveDurable::Engine => "/durable"`): executions by status and recipe, notebooks
97
+ with tickets, undos and signals, and the three operations. It works in API-only apps with its own cookie
98
+ session and CSRF protection, and is closed in production until `config.dashboard_authorize` is set.
99
+ - Events: `completed`, `compensated`, `blocked`, `retried`, `compensation_requested` and `rerun`
100
+ (`*.active_durable`).
101
+ - `bin/demo` to browse the dashboard with sample sagas.
102
+
103
+ ### Changed
104
+
105
+ - Requires Ruby 3.3+ (3.2 is end of life) and Rails 7.2+.
106
+ - A compensation that reaches a completed `flow.pivot` blocks instead of undoing past the point of no return.
107
+ - Schema: `durable_executions.forked_from` (regenerate the migration).
108
+
109
+ ## [0.1.0] - 2026-10-05
110
+
111
+ First version: nothing gets lost.
112
+
113
+ ### Added
114
+
115
+ - `Durable.define` and `Durable.start`: named recipes and executions stored in `durable_executions`. The execution
116
+ row is the outbox: the job is enqueued after commit.
117
+ - The notebook (`durable_steps`): steps are checkpointed and skipped on replay.
118
+ - `flow.step` with a stable ticket per step for idempotency keys, and retries with configurable backoff.
119
+ - `flow.transaction`: a database-only step committed together with its checkpoint (exactly once).
120
+ - `flow.pivot`: the point of no return. Before it failures compensate; after it steps are retried, then blocked.
121
+ - Compensation in reverse order, checkpointed per undo and resumable after a crash. `undo_on_failure:` for steps
122
+ whose failure may hide a success.
123
+ - `flow.sleep`, `flow.wait_for` and `Durable.signal` (`durable_signals`), without holding a worker.
124
+ - A lease with a fencing token: one worker per execution, and a worker that lost its lease cannot write.
125
+ - The recipe-changed alarm (`ActiveDurable::RecipeChanged`) and checks for duplicated step names and non-JSON
126
+ results.
127
+ - `ActiveDurable::Sweeper` / `SweepJob` / `rake active_durable:sweep` for lost jobs and expired leases.
128
+ - `ActiveDurable::Testing.crash_everywhere` and `ActiveDurable::Testing.drain`.
129
+ - `rails generate active_durable:install`.
130
+ - `ActiveSupport::Notifications` events: `execution.active_durable`, `step.active_durable`,
131
+ `compensation.active_durable` and `undo.active_durable`.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 William Romero
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
13
+ all 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
21
+ THE SOFTWARE.