Skip to content

Support SQLAlchemy 2.1 - #4364

Merged
un-def merged 1 commit into
masterfrom
pr_sqlalchemy_2_1
Oct 12, 2026
Merged

un-def merged 1 commit into
masterfrom
pr_sqlalchemy_2_1

Conversation

@un-def

@un-def un-def commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator

SQLAlchemy was capped below 2.1 because sqlalchemy-utils didn't support it (kvesteri/sqlalchemy-utils#800). sqlalchemy-utils 0.43.0 adds SQLAlchemy 2.1 support, so require it and drop the cap. Python 3.10, which SQLAlchemy 2.1 doesn't support, still gets 2.0.

Also:

  • delete_metrics() no longer runs its two DELETE statements concurrently with asyncio.gather() in separate sessions. In tests, StaticPool makes both sessions share one SQLite connection. In 2.1, the aiosqlite adapter runs execute + fetchall under a per-connection mutex that commit() doesn't take, so one session's COMMIT could land between the other session's DELETE ... RETURNING and its fetch, and fail with "cannot commit transaction - SQL statements in progress". Under 2.0 the interleaving happened to be safe. Production uses a connection pool, so only tests were affected. Both deletes now run in one session.
  • noload() is deprecated in 2.1. Replace it with raiseload(), which also skips the mapped lazy="selectin" load, but raises on access instead of returning an empty collection. RunModel.jobs isn't read after list_projects_run_models(), so the swap is enough there. FleetModel.instances is read downstream for candidate fleets that have no matching instances, so set it to an empty list explicitly with set_committed_value().

SQLAlchemy was capped below 2.1 because sqlalchemy-utils didn't
support it (kvesteri/sqlalchemy-utils#800). sqlalchemy-utils 0.43.0
adds SQLAlchemy 2.1 support, so require it and drop the cap. Python
3.10, which SQLAlchemy 2.1 doesn't support, still gets 2.0.

Also:

* delete_metrics() no longer runs its two DELETE statements
  concurrently with asyncio.gather() in separate sessions. In tests,
  StaticPool makes both sessions share one SQLite connection. In 2.1,
  the aiosqlite adapter runs execute + fetchall under a per-connection
  mutex that commit() doesn't take, so one session's COMMIT could land
  between the other session's DELETE ... RETURNING and its fetch, and
  fail with "cannot commit transaction - SQL statements in progress".
  Under 2.0 the interleaving happened to be safe. Production uses a
  connection pool, so only tests were affected. Both deletes now run
  in one session.
* noload() is deprecated in 2.1. Replace it with raiseload(), which
  also skips the mapped lazy="selectin" load, but raises on access
  instead of returning an empty collection. RunModel.jobs isn't read
  after list_projects_run_models(), so the swap is enough there.
  FleetModel.instances is read downstream for candidate fleets that
  have no matching instances, so set it to an empty list explicitly
  with set_committed_value().

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@un-def
un-def merged commit e088489 into master Oct 12, 2026
26 checks passed
@un-def
un-def deleted the pr_sqlalchemy_2_1 branch October 12, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant