Skip to content

fix: share multisite transaction journals - #400

Merged
chubes4 merged 8 commits into
mainfrom
fix/native-post-mutation-transactions
Sep 9, 2026
Merged

chubes4 merged 8 commits into
mainfrom
fix/native-post-mutation-transactions

Conversation

@chubes4

@chubes4 chubes4 commented Sep 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Share native transaction ownership across multisite scopes and serialize canonical writes across processes before pre-image reads, ID allocation and narrower statement locks. Hold the root write lock through transaction completion; autocommit releases it after the statement.

Recover abandoned journals before admitting writes. Recovery, decode, cleanup and unsafe-path failures block further admission until safely retried. Only factory-configured canonical roots are admitted, including split state/content roots; cached owners receive validated later roots. Stable lock files reject symlinks, hardlinks and nonregular files.

Verification

Exact candidate 96887dd97e87eff5b22a1a4d5cafcf8d65bf269b.

Parent independently ran the following on Linux at that exact Git head:

php tests/smoke-native-split-root-transaction-journal.php
php tests/smoke-native-journal-ownership.php
php tests/smoke-native-transaction-write-isolation.php
php tests/smoke-native-transaction-write-isolation-files.php
php tests/smoke-native-multisite-prefix-routing.php
php tests/smoke-native-post-transaction-boundary.php

All passed, including independent-process post/option/JSON writes, crash recovery after a prior startup lock miss, failed-recovery retries, unsafe locks, split-root rename/delete rollback, abandoned split-root restoration, and multisite cross-scope post rollback. The pinned Codebox multisite install/switch and cold-reload acceptance also passed in worker verification.

Limits

This establishes write serialization, not MVCC or InnoDB read isolation. Contention has a five-second wait limit and may return an explicit failure. Runtime instances for the same root within one PHP process still share one logical transaction owner; independent same-process connection isolation is not provided. #377 remains open for broader SQL and consumer parity.

No release or deployment is included.

AI Assistance

GPT-5.6 Terra (openai/gpt-5.6-terra) via OpenCode implemented the repair and regression coverage. GPT-6 Astra (openai/gpt-6-astra) via OpenCode reviewed lock ordering, recovery admission and split-root lifecycle, requested corrections, independently reran the exact Linux safety tests, and finalized evidence under Chris Huber's direction.

@chubes4
chubes4 marked this pull request as ready for review September 9, 2026 18:46
@chubes4
chubes4 merged commit 0bec3f7 into main Sep 9, 2026
chubes4 added a commit that referenced this pull request Sep 24, 2026
…434)

wp_users is the one core table whose compiled column set differs
between the single-site and multisite catalog variants (spam,
deleted). WordPress promotes a single-site install to a network by
running exactly this ALTER TABLE against it (dbDelta, inside
populate_network()) -- one ADD COLUMN statement per missing column.

A generated core table is described by the compiled schema catalog,
not a persisted CREATE TABLE statement: creating one registers the
catalog definition directly and never writes a `_schema/<suffix>.sql`
file (see the core-table branch of
WP_Markdown_Native_Schema_Mutation_Runtime::execute()). The ordinary
ALTER path requires that persisted file to rewrite and recompile, so
this ALTER TABLE failed closed as `unknown_table`, and
reconcile_rows()'s existing backfill-on-ADD-COLUMN behavior --
already correct for a plugin's own persisted table -- never ran for a
core table at all.

Add execute_core_table_alter(): resolve the added column from the
catalog's own two known variants, backfill it on the persisted
snapshot via the existing reconcile_rows(), and rebuild the table's
registration from the catalog so a core table's bespoke lookup
semantics (e.g. wp_users' ASCII case-insensitive user_login matching)
stay exactly what a fresh boot would build. MODIFY is a no-op (every
shared column is identical between variants); DROP is intentionally
unsupported and fails closed, since re-deriving from the catalog
cannot shrink the compiled shape back down for a runtime whose
is_multisite() state is fixed for its lifetime, and WordPress core has
no DDL path that downgrades a core table this way.

Investigation note, wp-codebox#2500: this ALTER TABLE gap is real and
independently verified by the new smoke test, but it does not explain
the issue's originally reported 15 mdi-native-only Route_AffinityTest
failures on Extra-Chill/extrachill-api. Those do not reproduce against
this repository's main (verified via wp-codebox's real PHPUnit harness
with an explicit markdown-database-integration source pointed at an
unmodified main checkout, both isolated -- 35/35 -- and full-suite --
247/247). main already carries materialize_multisite_user_defaults()
(WP_Markdown_Native_JSON_Snapshot_Provider::rows(), added in #393,
merged 2026-09-10 19:00), an unconditional read-side default for
wp_users rows missing the multisite-only columns, independent of
whether any ALTER TABLE ever ran. wp-codebox's bundled mdi-native zip
is pinned to 0bec3f7 ("share
multisite transaction journals", #400, merged 2026-09-09 14:46) -- a
day older than, and an ancestor of, #393. The bundled zip predates the
fix; main already has it. Confirmed via isolation test that this ALTER
TABLE fix, applied alone with materialize_multisite_user_defaults()
neutralized, does not reproduce the fix either -- main's real
bootstrap does not appear to reach this ALTER TABLE path for wp_users
at all, so wp_insert_user() working correctly rests entirely on the
read-side default. This fix stands on its own merit regardless: an
ALTER TABLE WordPress core genuinely issues against a generated core
table should not fail closed and silently skip reconciling existing
rows. See the PR description for the full investigation writeup.

Ref: Automattic/wp-codebox#2500
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