Safe non‑transactional DDL execution in Yii 2.x migrations for PostgreSQL
0 reputation · 07 Aug 2024, 23:37 UTC
0 reputation · 07 Aug 2024, 23:37 UTC
Goal: Ensure a Yii 2.x migration can apply a CREATE INDEX CONCURRENTLY statement on a live PostgreSQL database without causing the migration to fail or leaving the database in an inconsistent state after a rollback.
Constraint: Yii migrations wrap each step in a transaction by default, but PostgreSQL rejects CREATE INDEX CONCURRENTLY when executed inside a transaction, and disabling transactions removes rollback safety for the entire migration, risking partial schema changes.
Questions: Can the migration be split into separate transactional and non‑transactional sections while preserving atomicity for the transactional part? Does Yii offer a mechanism to run non‑transactional DDL safely and still allow rollback of subsequent steps? Is there a recommended approach to coordinate cache invalidation for sessions and assets during such zero‑downtime deployments?
31775 reputation · 08 Aug 2024, 04:28 UTC
Use the up() and down() methods instead of safeUp()/safeDown() so Yii does not wrap the whole migration in a transaction. Then manually control transaction boundaries for the parts that need atomicity and run the concurrent index statement outside any transaction.
public function up() and public function down() (no safeUp/safeDown).$transaction = $this->db->beginTransaction();
try {
// e.g., add a column, create a regular index, etc.
$this->addColumn('{{%table}}', 'new_col', $this->string()->null());
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
}
$this->db->createCommand('CREATE INDEX CONCURRENTLY idx_table_new_col ON {{%table}} (new_col)')->execute();
down(), reverse the steps in opposite order, using transactions for the transactional parts and a simple DROP INDEX IF EXISTS for the concurrent index (which can run inside a transaction).$this->db->createCommand('DROP INDEX IF EXISTS idx_table_new_col')->execute();
// then rollback transactional changes in reverse order
Yii::$app->cache->flush();
Yii::$app->getDb()->getSchema()->refresh();
safeUp/safeDown in a transaction; PostgreSQL rejects CREATE INDEX CONCURRENTLY inside any transaction block.up()/down() disables automatic transaction wrapping, allowing manual beginTransaction/commit calls.up()/down() in a transaction. If it does, the approach must be adjusted.Confirm that your Yii version does not automatically wrap up()/down() in a transaction (check yii\db\Migration::wrapInTransaction or run a test migration with a simple BEGIN; statement and see if it errors). This detail changes the recommendation because if wrapping still occurs, you would need to extend the migration class to disable it.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.