Durable execution on a Laravel database connection.
Read-only mirror. This repository is a subtree-split of gplanchat/durable-dev, published so Composer can require this package on its own. Issues and pull requests are disabled here — open them on the monorepo.
The tests are in the monorepo, not here. This split carries source only. What covers it is
tests/unit/Bridge/Illuminate/in the monorepo, run by itsunitsuite.Documentation: durable.rocks.
Four stores behind the four storage ports, written against Illuminate\Database\Connection — the
query builder, not Eloquent. No Doctrine anywhere in the tree.
| Port | This package |
|---|---|
EventStoreInterface |
IlluminateEventStore |
WorkflowMetadataStore |
IlluminateWorkflowMetadataStore |
ChildWorkflowParentLinkStoreInterface |
IlluminateChildWorkflowParentLinkStore |
WorkflowRunCatalogInterface (+ WorkflowRunProjectionInterface) |
IlluminateWorkflowRunCatalog |
DUR030 sells durable execution on one database with no cluster. It does not need, and Durable does not offer, a transaction shared with business code. Pass these stores a connection of their own, not the application's default one (DUR054): on a shared connection, the transactions the event store opens to fence a pass (DUR053) nest inside the application's, so a business rollback erases journal events and a claim stays invisible to the other workers until the business code commits.
An activity that writes and then dies runs again on replay. The answer is to make it idempotent.
Journal rows are append-only facts: execution_id, event_type, a JSON payload, recorded_at.
DB::table() writes them directly. An ActiveRecord model would add identity, events, casts and
timestamps over a row whose entire contract is that it is never mutated.
Every store extends its conformance suite from DUR041 — the same cases the in-memory reference
and the DBAL bridge run. The journal suite joins the replay tier: a real workflow with an
activity, a timer and two side effects runs against this store and against InMemoryEventStore,
and what replay reads back is compared. A payload deformed by a round trip breaks replay silently,
not at write time, and that comparison is what catches it.
tests/unit/Bridge/Illuminate/ — 44 cases, four ports
-
A queue, and jobs to put on it. The exclusion between two workers,
Queue\ResumeLock, is ingplanchat/durable-laravel, and so are the jobs. Nothing here decides which queue. -
The Durable service provider. Registering stores, binding ports, adding worker commands — that belongs to the Laravel integration package. A set of stores does not decide how an application wires them.
The provider this package does ship,
DurableIlluminateServiceProvider, registers nothing. It does the one thing no other package can do for it: tell Laravel where its migrations are.
Queue\ResumeLock, the per-execution lock that keeps two workers from resuming the same execution,
lives in gplanchat/durable-laravel (Gplanchat\Durable\Laravel\Queue\ResumeLock), which uses it on every
backend. If you wire these stores yourself, require that package for the lock.
composer require gplanchat/durable gplanchat/durable-bridge-illuminate
php artisan migrateThe four tables ship as a migration, loaded straight from the package — migrate is enough.
Publishing is for when you want to edit them, and from that point they are yours:
php artisan vendor:publish --tag=durable-migrationsKeep the published file's name. Laravel keys migrations by basename and lets
database/migrations win the tie — that shadowing is exactly what makes your copy the one that
runs. Rename it and the two become two migrations, both run, and the second fails on
table "durable_events" already exists with the database half migrated.
Schema\DurableSchema still creates the tables on demand. That is what a test and a worker booting
on an empty database use; an application uses the migration. Two ways to create the same four
tables is two chances to drift, so MigrationMatchesSchemaTest renders the DDL of both — against
MySQL's grammar, which carries lengths and indexes where SQLite discards them — and compares them
statement for statement.
MIT. See LICENSE.