{"record":{"id":"b32e12464882cd1d","repo":"nautechsystems/nautilus_trader","slug":"failed-to-add-pool-event-block-hash-storage-e","errorCode":null,"errorMessage":"Failed to add pool event block hash storage: {e}","messagePattern":"Failed to add pool event block hash storage: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":641,"sourceCode":"        .bind(&hashes[..])\n        .bind(&timestamps[..])\n        .execute(&self.pool)\n        .await\n        .map(|_| ())\n        .map_err(|e| anyhow::anyhow!(\"Failed to batch insert into pool_event_block table: {e}\"))\n    }\n\n    /// Adds block-hash storage to databases created before hash-bound profiler checkpoints.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the schema update fails.\n    pub async fn ensure_pool_event_block_hash_schema(&self) -> anyhow::Result<()> {\n        sqlx::query(\"ALTER TABLE pool_event_block ADD COLUMN IF NOT EXISTS hash TEXT\")\n            .execute(&self.pool)\n            .await\n            .map(|_| ())\n            .map_err(|e| anyhow::anyhow!(\"Failed to add pool event block hash storage: {e}\"))\n    }\n\n    /// Inserts blocks using PostgreSQL COPY BINARY for maximum performance.\n    ///\n    /// This method is significantly faster than INSERT for bulk operations as it bypasses\n    /// SQL parsing and uses PostgreSQL's native binary protocol.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the COPY operation fails.\n    pub async fn add_blocks_copy(&self, chain_id: u32, blocks: &[Block]) -> anyhow::Result<()> {\n        let copy_handler = PostgresCopyHandler::new(&self.pool);\n        copy_handler.copy_blocks(chain_id, blocks).await\n    }\n\n    /// Inserts tokens using PostgreSQL COPY BINARY for maximum performance.\n    ///\n    /// # Errors","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L623-L659","documentation":"The schema-migration helper `ensure_pool_event_block_hash_schema` runs `ALTER TABLE pool_event_block ADD COLUMN IF NOT EXISTS hash TEXT`; failure is wrapped with this message. It indicates the DDL statement itself could not run against Postgres.","triggerScenarios":"Calling ensure_pool_event_block_hash_schema when the database user lacks ALTER privilege on the table, the table `pool_event_block` does not exist yet (ALTER fails because the base table is missing), the DB is read-only (replica), or a lock on the table blocks the ALTER until statement timeout.","commonSituations":"Fresh database provisioned without running the base migrations first; connecting with an app role that has DML but not DDL rights; running against a hot-standby replica; long-running queries holding ACCESS EXCLUSIVE-compatible locks causing ALTER to hang/timeout.","solutions":["Ensure the base migrations created `pool_event_block` before calling this helper, or create the table if absent","Run the migration as a role with ALTER privilege (owner/superuser) or grant the required rights","Run against the primary, not a read replica","Check pg_locks for blocking sessions if the ALTER hangs, and run migrations during low traffic"],"exampleFix":"// before: ALTER fails when table missing\nsqlx::query(\"ALTER TABLE pool_event_block ADD COLUMN IF NOT EXISTS hash TEXT\")\n// after: guard the table's existence first\nsqlx::query(\"CREATE TABLE IF NOT EXISTS pool_event_block (...)\").execute(&self.pool).await?;\nsqlx::query(\"ALTER TABLE pool_event_block ADD COLUMN IF NOT EXISTS hash TEXT\")","handlingStrategy":"try-catch","validationCode":"// Verify table exists and you can ALTER before running the migration helper\nlet exists: Option<i32> = sqlx::query_scalar(\n    \"SELECT 1 FROM information_schema.tables WHERE table_name = 'pool_event_block'\")\n    .fetch_optional(&pool).await?;\nanyhow::ensure!(exists.is_some(), \"pool_event_block table missing; run base migrations first\");","typeGuard":"async fn table_exists(pool: &sqlx::PgPool, name: &str) -> anyhow::Result<bool> {\n    Ok(sqlx::query_scalar::<_, i32>(\"SELECT 1 FROM information_schema.tables WHERE table_name = $1\")\n        .bind(name).fetch_optional(pool).await?.is_some())\n}","tryCatchPattern":"if let Err(e) = db.ensure_pool_event_block_hash_schema().await {\n    anyhow::bail!(\"schema upgrade failed (check DDL privileges / primary vs replica): {e:#}\");\n}","preventionTips":["Run schema migrations at startup against the primary, before any writes","Use a migration role with ALTER privileges, or grant them to the app role","Never run DDL against read replicas","Check pg_locks if ALTER appears to hang — long transactions can block it"],"tags":["database","postgres","migration","ddl"],"backgroundTag":"schema-migration-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}