PHPackages                             gplanchat/durable-bridge-illuminate - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. gplanchat/durable-bridge-illuminate

ActiveLibrary

gplanchat/durable-bridge-illuminate
===================================

Illuminate (Laravel) journal, metadata and run stores for gplanchat/durable — durable execution on the connection Laravel already owns

00PHP

Since Aug 28Pushed todayCompare

[ Source](https://github.com/gplanchat/durable-bridge-illuminate)[ Packagist](https://packagist.org/packages/gplanchat/durable-bridge-illuminate)[ RSS](/packages/gplanchat-durable-bridge-illuminate/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

gplanchat/durable-bridge-illuminate
===================================

[](#gplanchatdurable-bridge-illuminate)

Durable execution on the database connection **Laravel already owns**.

Four stores behind the four storage ports, written against `Illuminate\Database\Connection` — the query builder, not Eloquent. No Doctrine anywhere in the tree.

PortThis package`EventStoreInterface``IlluminateEventStore``WorkflowMetadataStore``IlluminateWorkflowMetadataStore``ChildWorkflowParentLinkStoreInterface``IlluminateChildWorkflowParentLinkStore``WorkflowRunCatalogInterface` (+ `WorkflowRunProjectionInterface`)`IlluminateWorkflowRunCatalog`Why the connection matters more than the idiom
----------------------------------------------

[](#why-the-connection-matters-more-than-the-idiom)

**DUR030** sells durable execution on one database with no cluster, and that only pays if the journal append and the business write land in **one transaction**. Otherwise the activity writes, the process dies before the journal records that it did, and replay runs it a second time — the exact failure durable execution exists to prevent.

A store on `DB::connection()` is inside `DB::transaction()` by construction. Handing Doctrine DBAL the PDO out of `DB::connection()->getPdo()` reaches the same guarantee and is a workaround; this is the plain answer.

Why the query builder and not Eloquent
--------------------------------------

[](#why-the-query-builder-and-not-eloquent)

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.

What proves it
--------------

[](#what-proves-it)

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

```

Not in this package
-------------------

[](#not-in-this-package)

- **A queue, and jobs to put on it.** `Queue\ResumeLock` is the exclusion, not the plumbing: it takes a closure, so a job, an artisan command or a hand-written worker can all use it. Nothing here decides which.
- **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.

One resume at a time
--------------------

[](#one-resume-at-a-time)

`Queue\ResumeLock` is the one thing no storage choice can supply. Two workers resuming the **same**execution both replay it, both believe they are discovering the commands it produces, and those commands go out twice. The journal does not prevent it — it faithfully records whatever it is handed, twice included.

```
$lock = new ResumeLock($cacheStore);          // any store implementing LockProvider
$lock->around($executionId, fn() => $runner->resume($executionId));
```

**It waits on its own rather than calling `Lock::block()`**, and that is deliberate: `block()` calls a **global** `now()`, which only a full Laravel application defines — `illuminate/support` publishes it under its own namespace only. A package that relies on it works inside an application and breaks in a standalone worker or a test, which is the worst of both: the failure only happens where nobody is looking.

**`LockProvider` is the only contract this lock needs, and it filters nothing.** An earlier version of this file claimed it forced the caller to pick a store that can actually lock, and that the `file` store did not implement it. **Both halves are wrong on Laravel 12**, and measuring said so: nine stores implement `LockProvider`, `file` among them — and it locks correctly across processes — while `NullStore` implements it too and its `NoLock::acquire()` returns `true` unconditionally.

Measured, twenty resumes of one execution across four `queue:work` processes:

storeoverlapping critical sectionsmax concurrency`database`01`file`01`array`**15 of 20****4**`null`**15 of 20****4**`array` excludes inside one process and not between two; `null` excludes nothing at all and says so to nobody. Both type-check. **So the type is not the guard — the choice is yours, and it is the one choice in this package that silently forks a journal when it is wrong.** Use `database`, `redis`, `memcached`, `dynamodb` or `file`.

Install
-------

[](#install)

```
composer require gplanchat/durable gplanchat/durable-bridge-illuminate
php artisan migrate
```

The 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-migrations
```

**Keep 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`](LICENSE).

###  Health Score

20

—

LowBetter than 12% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/144109625f8029112d56f43d08120cf03a0918ea3b4fd59ab53f09b02f630b00?d=identicon)[gplanchat](/maintainers/gplanchat)

---

Top Contributors

[![gplanchat](https://avatars.githubusercontent.com/u/152367?v=4)](https://github.com/gplanchat "gplanchat (8 commits)")

### Embed Badge

![Health badge](/badges/gplanchat-durable-bridge-illuminate/health.svg)

```
[![Health](https://phpackages.com/badges/gplanchat-durable-bridge-illuminate/health.svg)](https://phpackages.com/packages/gplanchat-durable-bridge-illuminate)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
