Add configurable storage connection for workflow models and migrations#391
Open
discovery-ukraine wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add configurable storage connection for workflow models and migrations
Summary
This adds an optional
workflows.storage.connectionconfig key that routes allworkflow persistence — every Eloquent model under
src/Models/andsrc/V2/Models/, andevery migration under
src/migrations/— to a dedicated database connection. The keydefaults to
null, which resolves to the application's default connection, so existingdeployments are fully backward compatible and see no behavior change.
Motivation
We want workflow state to live in a separate
durable_workflowdatabase, isolated from ourcentral/tenant OLTP databases, across both MariaDB and PostgreSQL. Today the package gives no
supported way to do that:
src/Models/andsrc/V2/Models/extends
Illuminate\Database\Eloquent\Modeldirectly and never overridesgetConnectionName(), so workflow storage always lands on the application's defaultconnection.
ConfiguredV2Models::resolve('*_model', …)mechanism only covers some models. Severaltables are also reached through hardcoded class references (e.g.
new WorkflowMessage(),WorkflowChildCall::,WorkerCompatibilityHeartbeat::) alongside theresolve()path.Overriding a model only on the
resolve()side would leave the hardcoded call sites on thedefault connection — the same table read on two connections (a split-brain). The only
correct place to route the connection is the model class itself.
(
telescope.storage.database.connection), Horizon, Passport, andspatie/laravel-activitylog(activitylog.database_connection) all expose this. A durableworkflow engine — the system of record for critical long-running processes — benefits even
more from isolating its store: independent backup/retention/scaling, multi-driver setups,
and tenant isolation.
What changed
storageblock insrc/config/workflows.php:DW_*→WORKFLOW_*→ default env contract.Workflow\Traits\ResolvesStorageConnection— a small trait that overridesgetConnectionName()to returnconfig('workflows.storage.connection') ?? $this->connection.Applied to all 33 models. Because the routing lives on the model class, both hardcoded and
resolve()-based usages resolve to the same connection — eliminating the split-brain.Workflow\Support\WorkflowMigration— an abstractMigrationsubclass that overridesgetConnection()the same way. All 39 migrations now extend it instead ofIlluminate\Database\Migrations\Migration.migration routing onto a second connection, and a fail-closed contract test that every
package migration extends
WorkflowMigration.Backward compatibility
The key defaults to
null. Withnull,getConnectionName()/getConnection()return themodel's own
$connection(alsonull), i.e. the application's default connection — exactlytoday's behavior. Existing apps are unaffected; no migration or config change is required.
How it works
For migrations the routing relies on Laravel's
Migrator:runMigration()resolves theconnection from
$migration->getConnection()(overriding the--databaseoption), andrunMethod()temporarily sets that connection as the default for the duration ofup()/down(). That is why the bareSchema::create(...)/Schema::dropIfExists(...)calls needno change — they land on the configured connection automatically. The migration repository
rows still record on the run connection, which is standard Laravel behavior (the same as
Telescope).
Testing
Tests\Unit\StorageConnectionTest— withworkflows.storage.connection = 'secondary',asserts
getConnectionName() === 'secondary'forStoredWorkflow(v1),WorkflowRun(v2 resolve-path) andWorkflowMessage(v2 hardcoded-usage); with the keyunset, asserts the null/default fallback.
Tests\Unit\Migrations\StorageConnectionMigrationTest— registers a secondsqliteconnection, points
workflows.storage.connectionat it, runs the migrations, then assertsrepresentative workflow tables (
workflows,workflow_runs,workflow_messages) exist onthe secondary connection and are absent on the default connection.
Tests\Unit\Migrations\MigrationsTest::testEveryPackageMigrationExtendsWorkflowMigrationBase— a fail-closed contract test (matching the existing migration-slate contracts) asserting
every
src/migrations/*.phpextendsWorkflowMigrationand none extends the framework'sbare
Migration. This guards against a futuremake:migrationregenerating a migration onthe default base class and silently bypassing the storage-connection routing.