Skip to content

[bundle] Two misconfigurations that break durability are silent; both are checkable at compile time #259

Description

@gabiudrescu

Why this matters more than the other issues

Everything else on my list announces itself. These two do not: the app boots, the dashboard is green, a workflow runs to completion in development - and the guarantee the library exists to provide is gone.

1. Durable messages left on sync

ResumeWorkflowMessage and the four others have no routing by default. With no entry in framework.messenger.routing, they are handled inline on the bus, which means a workflow replays inside the web request that started it. The request ends, the process is recycled, and a suspended execution dies with it - the exact failure the library removes.

I checked, and the bundle never sets a TransportNamesStamp itself (grep TransportNamesStamp vendor/gplanchat/ shows only the profiler middleware reading it), so this is entirely on the application's configuration.

It looks fine while you test it, because a workflow that completes in one pass never has to survive anything.

Check: at compile time, resolve the five message classes against framework.messenger.routing. Fail the build when any of them is unrouted or routed to a sync:// transport. The message writes itself:

Gplanchat\Durable\Transport\ResumeWorkflowMessage is not routed to an asynchronous
transport. A workflow would replay inside the web request that started it and die with the
process. Route it to a Doctrine, AMQP or Redis transport.

2. A lock store that is not shared between processes

SingleResumeLockMiddleware serialises resumes with symfony/lock. The stock Sylius/Symfony default is LOCK_DSN=flock, which is per-container. Two workers in two containers then both hold durable-resume-{executionId} and replay the same journal at the same time.

Nothing complains. You find out when an activity runs twice.

Check: refuse FlockStore and SemaphoreStore when the DBAL bridge is enabled, unless an explicit opt-out is set for single-process development.

The configured lock store is per-process (FlockStore). Two workers could resume the same
execution at once. Use a shared store - doctrine, redis, postgresql - or set
durable.dbal.allow_local_lock: true if you run exactly one worker.

Context

Both bit me on a first install. The second one bit twice: my first fix, LOCK_DSN=doctrine://default, is not a DSN symfony/lock accepts, and the failure surfaced three commands later inside an unrelated fixture load - Unsupported Connection: "doctrine://default" thrown from StoreFactory - with nothing tying it back to durable. A note in the README that the value is a DBAL URL, not a connection alias, would have saved that too.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions