Skip to content

Free-threaded Python (3.14t) support#151

Merged
lesnik512 merged 6 commits into
mainfrom
free-threaded-support
Jul 18, 2026
Merged

Free-threaded Python (3.14t) support#151
lesnik512 merged 6 commits into
mainfrom
free-threaded-support

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Guarantees free-threaded (no-GIL) CPython 3.14t compatibility — a
compatibility guarantee, not a behavior change. No runtime/source change to the
package.

What lands

  • A standalone freethreaded CI job: installs 3.14t, runs the full suite
    (--no-cov), and asserts sys._is_gil_enabled() is False for the outbox's
    runtime dependency graph so a silent GIL re-enable turns the build red.
  • A Programming Language :: Python :: Free Threading :: 2 - Beta classifier.
  • A "Free-threaded Python" docs section.

Notable finding baked into the design: SQLAlchemy's Cython extensions ship
cp314t wheels but don't declare free-thread safety, so importing SQLAlchemy
re-enables the GIL. The job sets DISABLE_SQLALCHEMY_CEXT_RUNTIME=1 (SQLAlchemy's
own switch); the docs tell users to do the same for a genuinely GIL-free process.
The suite itself runs GIL-on (test-only aiokafka re-enables it at collection) —
the docs state the two CI facts separately.

Design & rationale: planning/changes/2026-07-18.01-free-threaded-support.md
Decisions: planning/decisions/2026-07-18-free-threading-is-compat-only.md,
planning/decisions/2026-07-18-sqlalchemy-cyext-disables-gil.md

Follow-up (repo admin): add freethreaded to branch-protection required checks,
as with the other _checks jobs.

🤖 Generated with Claude Code

lesnik512 and others added 6 commits July 18, 2026 08:47
Full-lane design change + a decision recording free-threading as a
compatibility guarantee (runs on 3.14t, GIL off) rather than a
parallelism redesign, and why 3.14t is the only target (deps ship
cp314t wheels, no cp313t).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Local proof on 3.14t showed SQLAlchemy's Cython extensions re-enable the
GIL (they ship cp314t wheels but don't declare Py_MOD_GIL_NOT_USED).
Scope the guarantee to DISABLE_SQLALCHEMY_CEXT_RUNTIME=1 (SQLAlchemy's
sanctioned switch); record a decision with a revisit trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a standalone `freethreaded` job that installs 3.14t, runs the full
suite (--no-cov), and asserts sys._is_gil_enabled() is False so a
dependency that silently re-enables the GIL fails the build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Advertises the CI-proven 3.14t free-threaded compatibility on PyPI.
Conservative first claim; bump to 3 - Stable after real-world soak.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
States what is guaranteed (runs on 3.14t, GIL off) and what is not
(no extra in-process parallelism; the subscriber stays single-loop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The suite's pytest step runs GIL-on (test tooling imports aiokafka at
collection, which re-enables the GIL); only the standalone assertion step
proves the outbox's runtime dependency graph keeps the GIL disabled. Word
the docs/spec to reflect both facts instead of implying the suite runs
GIL-off.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Benchmark gate

✅ gate passed

scenario msg/s delete/msg WALrec/msg WALB/msg fpi upd del dead_tup
consumer/w1/b10 766 1.000 6.80 912 242 5000 5000 10000
consumer/w1/b100 844 1.000 6.85 918 243 5000 5000 10000
consumer/w2/b10 958 1.000 6.80 964 242 5000 5000 10000
consumer/w2/b100 1044 1.000 6.74 959 243 5000 5000 10000
consumer/w4/b10 833 1.000 6.68 922 242 5000 5000 10000
consumer/w4/b100 1341 1.000 6.87 1037 244 5000 5000 10000
consumer/w1/b100/tfbs100 5459 0.010 6.07 1116 243 5000 5000 10000
producer/w1/b100 2495 0.000 3.04 584 0 0 0 0

Gated (fails the build): delete_calls + tuple counters (upd/del/ins) + the producer's insert_calls/select_calls, exact; wal_records within a 10% band. msg/s, WAL bytes and total calls are informational (timing/FPI noise).

@lesnik512
lesnik512 merged commit 4a1ed31 into main Jul 18, 2026
8 checks passed
@lesnik512
lesnik512 deleted the free-threaded-support branch July 18, 2026 08:14
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