Resolve Subject ids through a MediaWiki table - #1310
Open
JeroenDeDauw wants to merge 1 commit into
Open
Conversation
Fixes #1040 Subject ids are not derived from page titles, so finding the page holding a Subject takes a query, and the only index answering it was the Neo4j projection. That put identity resolution in storage that is allowed to lag — graph failures during a write are swallowed by design — so a Subject written during an outage stayed unfindable, and therefore uneditable, until an administrator rebuilt. It also made Neo4j a requirement for Subject CRUD, `{{#view}}`, `{{#neowiki_value}}` and the `mw.neowiki.*` getters. The mapping is now authoritative in `neowiki_subject_page`. A write that changes what a page holds replaces that page's rows; the edit, delete, undelete and import paths all reach it, and a save that changes no Subjects leaves the index untouched. An edit indexes from `RevisionFromEditComplete`, inside PageUpdater's atomic section, so the index commits with the revision or not at all, outside the projection's failure isolation. Rows are deleted by primary key rather than by page id, so concurrent saves do not contend on gap locks in the page-id index — the same reason core deletes `page_restrictions` that way (T214035). Ids come from the slot's raw JSON rather than the deserializer, so a Subject too broken to deserialize stays findable. Reads join `page`, so moves need no index maintenance and the rows a deleted page leaves behind resolve to nothing. Duplicate ids from cross-wiki transfer resolve to the lowest page id. A wiki with no graph backend configured is now a supported mode. Query surfaces stay registered per backend; relation-target suggestions still come from Neo4j and degrade to none without it. Because identity now resolves authoritatively, an unresolvable Subject is refused rather than measured against the wiki-global `edit` right. Creating a Subject authorizes against the page the request names, so it is unaffected; deleting one the index cannot resolve answers 404, like the other endpoints keyed by Subject id. `RebuildSubjectPageIndex.php` backfills the table and repairs it, registered to run from `update.php`. The decision is recorded as ADR 32, added here. ## Worth a close look - The rebuild sweeps with one unbatched `DELETE` and loads revisions a page at a time. Free on the initial backfill, not on a later repair run. - `DELETE /subject/{id}` still has no per-page read gate, unlike its siblings. Pre-existing, and this PR changes the same method, so it is filed as #1312 ## Considered, omitted - **`ArticleMergeComplete` coverage** for a history merge that leaves the source page as a redirect — the one write neither the index nor the graph projection is told about. The rebuild script repairs both. - **Subject Sources** (#993) beyond keying on the bare local nanoid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JeroenDeDauw
force-pushed
the
subject-page-index
branch
from
August 20, 2026 16:37
a48e491 to
8324eb8
Compare
JeroenDeDauw
marked this pull request as ready for review
August 20, 2026 22:12
Member
Author
|
LGTM Relation value autocomplete will not work (graceful degradation) on wikis without Neo4j. This is tracked by #1313. Considered solving it in this PR by adding a label and schema field to the table (cc suggested approach) and rejected due to outstanding design work around multilinguality. |
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.
Fixes #1040
Subject ids are not derived from page titles, so finding the page holding a Subject takes a query, and the only index
answering it was the Neo4j projection. That put identity resolution in storage that is allowed to lag — graph failures
during a write are swallowed by design — so a Subject written during an outage stayed unfindable, and therefore
uneditable, until an administrator rebuilt. It also made Neo4j a requirement for Subject CRUD,
{{#view}},{{#neowiki_value}}and themw.neowiki.*getters.The mapping is now authoritative in
neowiki_subject_page. A write that changes what a page holds replaces thatpage's rows; the edit, delete, undelete and import paths all reach it, and a save that changes no Subjects leaves the
index untouched. An edit indexes from
RevisionFromEditComplete, inside PageUpdater's atomic section, so the indexcommits with the revision or not at all, outside the projection's failure isolation. Rows are deleted by primary key
rather than by page id, so concurrent saves do not contend on gap locks in the page-id index — the same reason core
deletes
page_restrictionsthat way (T214035).Ids come from the slot's raw JSON rather than the deserializer, so a Subject too broken to deserialize stays findable.
Reads join
page, so moves need no index maintenance and the rows a deleted page leaves behind resolve to nothing.Duplicate ids from cross-wiki transfer resolve to the lowest page id.
A wiki with no graph backend configured is now a supported mode. Query surfaces stay registered per backend;
relation-target suggestions still come from Neo4j and degrade to none without it.
Because identity now resolves authoritatively, an unresolvable Subject is refused rather than measured against the
wiki-global
editright. Creating a Subject authorizes against the page the request names, so it is unaffected;deleting one the index cannot resolve answers 404, like the other endpoints keyed by Subject id.
RebuildSubjectPageIndex.phpbackfills the table and repairs it, registered to run fromupdate.php. The decision isrecorded as ADR 32, added here.
Worth a close look
DELETEand loads revisions a page at a time. Free on the initial backfill,not on a later repair run.
DELETE /subject/{id}still has no per-page read gate, unlike its siblings. Pre-existing, and this PR changes thesame method, so it is filed as DELETE /subject/{id} has no per-page read gate, unlike the other Subject write endpoints #1312
Considered, omitted
ArticleMergeCompletecoverage for a history merge that leaves the source page as a redirect — the one writeneither the index nor the graph projection is told about. The rebuild script repairs both.
Production notes
Implementation and the review pass ran as
Opus 5subagents; design, triage of the review findings and this description areFable 5. The independent review measured the index's locking behaviour on a live MariaDB, which is what produced the primary-key delete.