Repository navigation
Upgrade guide: 'nothing of it is applied' is false for an index declared with IsCreatedConcurrently() #171
Description
Found by a pre-release sweep of main @ eff80b4 (lens: docs-claims); reproduced in a scratch test. Before the tag, but not blocking it: the 5.5.0 release notes link this page at blob/v5.5.0, so the sentence is frozen there.
Problem
docs/upgrading.md, in the entry "Declarations on a TPC type cover every table of the hierarchy", says: "Before you apply it, remove the rows in the derived tables that violate a unique index or an exclusion constraint. The migration fails on them … The migration runs in a transaction, so nothing of it is applied." The 934ec55 wording ("in its transaction applies nothing") was the same.
That isn't true for an index declared with IsCreatedConcurrently(), which the 5.4.0 entry on the same page recommends for large tables. The TPC copies inherit the flag, and Npgsql renders each copy as a command with its transaction suppressed. EF's migrator commits the open transaction before it runs such a command. When a concurrent unique copy then fails on duplicates (23505):
- every operation before it is already committed;
- PostgreSQL leaves an INVALID index under the copy's name;
- the migration isn't recorded, so re-running it fails on what was already applied (42P07 or similar).
Scenario
Booking is TPC on bookings, with SpecialBooking on special_bookings, and declares b.HasComplexIndex(x => x.Code, i => i.IsUnique().IsCreatedConcurrently()). Its generated commands, from the differ through the wired Npgsql generator:
suppressed=False: CREATE TABLE bookings (…
suppressed=False: CREATE TABLE special_bookings (…
suppressed=True: CREATE UNIQUE INDEX CONCURRENTLY "IX_bookings_Code" ON bookings ("Code");
suppressed=True: CREATE UNIQUE INDEX CONCURRENTLY "IX_special_bookings_Code" ON special_bookings ("Code");
That TransactionSuppressed commands run outside the migration's transaction, and that a failed CREATE INDEX CONCURRENTLY leaves an invalid index, is documented EF Core and PostgreSQL behaviour. The concurrent path wasn't applied live in this sweep.
Change
Qualify the sentence: an index declared with IsCreatedConcurrently() is created outside the transaction. If it fails, earlier operations stay applied, and the invalid index has to be dropped (DROP INDEX CONCURRENTLY) before the migration runs again. Better still, tell the reader to remove the violating rows first in that case too.
Test
Docs only; no test.
Activity
Merge pull request #173 from CaffeinatedCoder/docs/170-171-upgrade-notes