Repository navigation
alembic/env.py: an upgrade's exception ends it as itself, on one connection - #31
Merged
Merged
Conversation
…ection An interrupt, a SystemExit, or an error SQLAlchemy reads as a disconnect, inside a statement of an upgrade, makes SQLAlchemy invalidate the connection. env.py's handler then read connection.info, which on an invalidated connection raises PendingRollbackError in place of the exception. And after the invalidation, a revision's rollback() let the next statement reconnect on a second driver connection, outside the upgrade's transaction and the state the authorizer reads. - The handler records the failure on the connection's mapping, held before the upgrade runs. - Once the upgrade's connection is open, a connect listener closes and refuses any other; a revision that catches the refusal and goes on still fails the upgrade before the commit. - Tests: an interrupt and a SystemExit at a set point, a closed driver connection read as a disconnect, a reconnect after the invalidation (going on, and raising), a second connection a revision asks for (and catches), and an upgrade from the first schema on one connection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An upgrade's exception now ends it as itself, and an upgrade runs on one driver connection (redesign plan gap 111).
What was wrong
SystemExit, or an error SQLAlchemy reads as a disconnect, raised inside a statement of an upgrade, makes SQLAlchemy invalidate the connection.alembic/env.py's handler then wroteconnection.info[TRANSACTION_STATE] = "failed". On an invalidated connection, readingConnection.inforevalidates it and raisesPendingRollbackError: Can't reconnect until invalid transaction is rolled backin place of the original exception.rollback()let its next statement open a second driver connection, outside the upgrade's transaction and outside the state the authorizer reads, and the upgrade ran on to its end without an error. A revision could also ask the engine for a second connection outright.What changes
docs/database.mdstates the one-connection rule; the plan records it as gap 111.Tests (
tests/db/test_migrations.py,TestOneTransaction)test_an_interrupt_after_the_second_revision_applies_none[KeyboardInterrupt, SystemExit]after_cursor_executelistener raises at the real chain'sUPDATE alembic_versionfor the second revision after the first schematest_an_error_read_as_a_disconnect_applies_noneSELECT 1ProgrammingErrorwithconnection_invalidated; one invalidation, by itsorig; the file unchangedtest_no_statement_reconnects_after_the_connection_is_invalidated[the revision goes on, the revision raises]rollback()and aCREATE TABLEStatementErrorwhoseorigis the refusal; both driver connections closed; the file unchangedtest_a_revision_gets_no_second_connectionengine.connect()test_a_revision_that_catches_the_refusal_still_fails_the_upgradeCREATE TABLEMigrationRefusedwith its exact message; the file unchangedtest_an_upgrade_runs_on_one_driver_connectionAgainst
main'senv.py, the first seven cases fail. TheKeyboardInterrupt,SystemExit, and disconnect cases end withPendingRollbackError. The upgrades in the reconnect case (the revision going on), the second-connection case, and the caught-refusal case end without an error, and the reconnect case whose revision raises ends with thatRuntimeErrorafter reconnecting. The last test holds onmaintoo: it is the passing side of the one-connection rule.Ran, at
58ad137894343e6924309fa81b9bb2000f7256e7: the full suite (2066 tests, 0 failed, 0 errored, 0 skipped,scripts/check_junit.py --max-skipped 0);pre-commit run --all-files; the new tests againstmain'senv.py.Not run: a real SIGINT sent to
alembic upgradefrom a shell; a disconnect from a file deleted under the connection.