quazardous / gramphp-bundle
Symfony integration for gramphp: graphs declared in configuration as journal services over the Doctrine connection, the tables in the Doctrine schema, janitor and drawing commands.
Package info
github.com/quazardous/gramphp-bundle
Type:symfony-bundle
pkg:composer/quazardous/gramphp-bundle
Requires
- php: >=8.2
- doctrine/dbal: ^4.0
- doctrine/doctrine-bundle: ^2.11 || ^3.0
- quazardous/gramphp: ^0.1
- symfony/config: ^6.4 || ^7.0 || ^8.0
- symfony/console: ^6.4 || ^7.0 || ^8.0
- symfony/dependency-injection: ^6.4 || ^7.0 || ^8.0
- symfony/framework-bundle: ^6.4 || ^7.0 || ^8.0
- symfony/http-kernel: ^6.4 || ^7.0 || ^8.0
Requires (Dev)
- doctrine/orm: ^3.0
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^11.4
Suggests
- doctrine/orm: so that the gramphp tables join the ORM schema: doctrine:schema:update and doctrine:migrations:diff see them
Provides
None
Conflicts
- symfony/doctrine-bridge: <6.4.0
Replaces
None
README
The Symfony integration of gramphp — a small workflow graph for work queues that already live in your database. Graphs declared in configuration become journal services over your Doctrine connection; the tables join your migrations; the janitor runs as commands.
composer require quazardous/gramphp-bundle
Symfony 6.4, 7 or 8; DoctrineBundle 2 or 3; MariaDB (or MySQL 8).
Configure
# config/packages/gramphp.yaml gramphp: connection: default # the Doctrine DBAL connection subject_type: int # your ids: int or string graphs: orders: file: '%kernel.project_dir%/config/graphs/orders.json' # the subjects the janitor looks at — first column the subject, ORDER BY the priority candidates: 'SELECT id FROM orders WHERE archived = 0 ORDER BY id'
A graph file is gramphp's canonical form — grampy's format, the same file in
Python and PHP — as Graph::toJson() writes it. definition: takes the same
form inline.
gramphp's MariaDB driver needs READ COMMITTED. Set it when the connection opens:
# config/packages/doctrine.yaml doctrine: dbal: options: !php/const PDO::MYSQL_ATTR_INIT_COMMAND: 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED'
Use
Each graph is a NodeJournal service, gramphp.journal.<name>, autowired by
argument name — or by type when there is a single graph:
use Quazardous\GramPHP\Driver\Mariadb\Query; use Quazardous\GramPHP\Driver\Mariadb\Transaction; use Quazardous\GramPHP\NodeJournal; final class ShipWorker { public function __construct( private readonly NodeJournal $ordersJournal, private readonly Transaction $transaction, // retries a unit on a deadlock ) {} public function __invoke(): void { $lease = $this->transaction->run(fn() => $this->ordersJournal->claim( 'ship', 50, new Query('SELECT id FROM orders WHERE paid = 1 ORDER BY id'), )); foreach ($lease as $orderId) { // … ship it … } $this->transaction->run(fn() => $this->ordersJournal->conclude('ship', $lease, $lease->token)); } }
The journals run on your own connection: node rows and your writes can go in one transaction.
The tables
With the ORM, the gramphp tables join the schema it generates:
doctrine:migrations:diff creates them, and never proposes to drop them. A
test checks that a diff on tables gramphp's own DDL created proposes nothing.
Without the ORM, bin/console gramphp:schema --dump-sql prints the
CREATE TABLE statements for your migration; --force runs them.
The janitor, the monitoring, the drawing
| command | does |
|---|---|
gramphp:expire [graphs…] |
gives back the leases held longer than their node allows |
gramphp:settle [graphs…] --limit=500 |
concludes waits, lets due arrivals through their lanes, skips optional nodes past their grace — on the graph's candidates, in passes of --limit |
gramphp:prune-history --before='-30 days' |
deletes old history, except what a retry or loop bound counts |
gramphp:snapshot [graphs…] --format=table|json |
where each node stands: counts, oldest running, next retry due, ready and since when |
gramphp:diagram <graph> --format=mermaid|state|dot --counts |
draws the graph, with live counts |
Schedule the janitor with cron or Symfony Scheduler; feed snapshot to your
monitoring.
Development
Everything runs in Docker:
make build install make test # functional tests against MariaDB make check # PHPStan (max), coding style, tests — what CI runs
License
MIT.