{"record":{"id":"369efd12dde7556c","repo":"nautechsystems/nautilus_trader","slug":"failed-to-lock-legacy-execution-transactions-e","errorCode":null,"errorMessage":"Failed to lock legacy execution transactions: {e}","messagePattern":"Failed to lock legacy execution transactions: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":3054,"sourceCode":"    /// The migration locks the legacy transaction table, refuses unresolved version 1 rows,\n    /// installs the versioned intent and hash-history tables, and fences older writers before\n    /// releasing the lock. This prevents a mixed-version process from bypassing the new signer\n    /// ownership constraints.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the database operation fails.\n    pub async fn ensure_execution_transaction_schema(&self) -> anyhow::Result<()> {\n        let mut transaction = self\n            .pool\n            .begin()\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to start execution schema migration: {e}\"))?;\n\n        sqlx::query(\"LOCK TABLE execution_transaction IN ACCESS EXCLUSIVE MODE\")\n            .execute(&mut *transaction)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to lock legacy execution transactions: {e}\"))?;\n\n        for statement in [\n            \"\n            ALTER TABLE execution_transaction\n            ADD COLUMN IF NOT EXISTS client_order_id TEXT\n            \",\n            \"\n            ALTER TABLE execution_transaction\n            ADD COLUMN IF NOT EXISTS wallet_address TEXT\n            \",\n            \"\n            ALTER TABLE execution_transaction\n            ALTER COLUMN wallet_address DROP NOT NULL\n            \",\n            \"\n            CREATE TABLE IF NOT EXISTS execution_schema_version (\n                component TEXT PRIMARY KEY,\n                version SMALLINT NOT NULL CHECK (version > 0)","sourceCodeStart":3036,"sourceCodeEnd":3072,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/cache/database.rs#L3036-L3072","documentation":"Thrown when the LOCK TABLE execution_transaction IN ACCESS EXCLUSIVE MODE statement inside ensure_execution_transaction_schema fails. This exclusive lock fences older writers during the v2 migration; the wrapped error typically reports 'lock timeout', 'deadlock detected', 'relation \"execution_transaction\" does not exist', or a privilege failure. No DDL has run when this fires.","triggerScenarios":"Running the v2 migration while an old-version node still holds locks on execution_transaction; session lock_timeout/statement_timeout aborts the ACCESS EXCLUSIVE wait; a fresh database where the legacy v1 execution_transaction table was never created; role lacks privileges on the table.","commonSituations":"Rolling upgrade where old and new binaries overlap against one database; DBA-set lock_timeout; pointing the node at an empty database instead of the evolved one.","solutions":["Read the appended {e}: 'relation does not exist' means the v1 table is missing - point at the correct database or create the legacy table first","Stop all old-version processes before migrating so the exclusive lock is granted immediately","Raise or clear lock_timeout/statement_timeout for the migration session","If blocked, find blockers in pg_stat_activity and terminate them, then re-run (the migration is idempotent)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"-- Confirm the legacy table exists and see blockers before migrating\nSELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'execution_transaction');\nSELECT pid, state, query FROM pg_stat_activity WHERE wait_event_type = 'Lock';","typeGuard":null,"tryCatchPattern":"match db.ensure_execution_transaction_schema().await {\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"lock timeout\") || msg.contains(\"deadlock\") {\n            // retry after the blocking session finishes; migration is idempotent\n        } else if msg.contains(\"does not exist\") {\n            // wrong database or missing v1 table - fix the target, do not retry\n        } else {\n            return Err(e);\n        }\n    }\n    Ok(()) => {}\n}","preventionTips":["Quiesce old-version nodes before running the v2 migration","Set a generous lock_timeout for the migration session only, then restore it","Keep the migration on a direct connection rather than a transaction-pooling proxy","Run migrations from a role that owns the execution_transaction table"],"tags":["rust","sqlx","postgresql","table-lock","migration","rolling-upgrade"],"backgroundTag":"postgres-lock-timeout","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}