{"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":3360,"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":3342,"sourceCodeEnd":3378,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L3342-L3378","documentation":"This error wraps failure to acquire an ACCESS EXCLUSIVE lock on the execution_transaction table (`LOCK TABLE ... IN ACCESS EXCLUSIVE MODE`) during migration. The library throws it because DDL changes require excluding concurrent readers/writers, and the lock request failed.","triggerScenarios":"Running ensure_execution_transaction_schema while another transaction holds locks on execution_transaction (long queries, other migrations, autovacuum), lock_timeout exceeded, or the table does not exist yet.","commonSituations":"Concurrent app instances running migrations at startup against the same database; a long-running report query holding a shared lock; deadlock_timeout/lock_timeout defaults blocking the lock wait.","solutions":["Ensure migrations run from a single instance at a time (advisory lock or leader election)","Find and terminate blocking sessions via pg_stat_activity / pg_locks","Set a lock_timeout and retry the migration","Confirm the execution_transaction table exists before locking"],"exampleFix":"// before\nsqlx::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// after\n// pre-step: clear blockers\n// SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND ...;\nsqlx::query(\"SET LOCAL lock_timeout = '5s'; 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}\"))?;","handlingStrategy":"retry","validationCode":"let blockers = sqlx::query_scalar::<_, i64>(\n    \"SELECT count(*) FROM pg_locks l JOIN pg_class c ON l.relation = c.oid WHERE c.relname = 'execution_transaction'\"\n).fetch_one(&pool).await?;\nif blockers > 0 { anyhow::bail!(\"execution_transaction is locked by another session\"); }","typeGuard":null,"tryCatchPattern":"for attempt in 0..5 {\n    match run_migration().await {\n        Ok(()) => break,\n        Err(e) if e.to_string().contains(\"Failed to lock\") && attempt < 4 => backoff(attempt).await,\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Gate migrations behind a Postgres advisory lock so only one instance migrates","Set a lock_timeout to fail fast and retry","Avoid long transactions/queries touching execution_transaction during deploys"],"tags":["database","postgres","locking","migration"],"backgroundTag":"permission-denied","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}