Page MenuHomePhabricator

MediaWiki\Revision\RevisionAccessException: Unable to load fresh row for rev_id: {rev_id}
Closed, ResolvedPublicPRODUCTION ERROR

Description

Error
  • service.version: 1.45.0-wmf.11
  • timestamp: 2025-07-24T14:42:53.061Z
  • labels.phpversion: 8.1.33
  • trace.id: eddf40b9-c42b-4e0a-9de0-d5756a242113
  • Find trace.id in Logstash
labels.normalized_message
[{reqId}] {exception_url}   MediaWiki\Revision\RevisionAccessException: Unable to load fresh row for rev_id: {rev_id}
FrameLocationCall
from/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreCacheRecord.php(114)
#0/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreCacheRecord.php(78)MediaWiki\Revision\RevisionStoreCacheRecord->loadFreshRow()
#1/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionRecord.php(489)MediaWiki\Revision\RevisionStoreCacheRecord->getVisibility()
#2/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreRecord.php(154)MediaWiki\Revision\RevisionRecord->isDeleted(int)
#3/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionRecord.php(531)MediaWiki\Revision\RevisionStoreRecord->isDeleted(int)
#4/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionRecord.php(465)MediaWiki\Revision\RevisionRecord->audienceCan(int, int, null)
#5/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreRecord.php(220)MediaWiki\Revision\RevisionRecord->getComment(int, null)
#6/srv/mediawiki/php-1.45.0-wmf.11/extensions/EventBus/includes/Serializers/MediaWiki/RevisionEntitySerializer.php(77)MediaWiki\Revision\RevisionStoreRecord->getComment()
#7/srv/mediawiki/php-1.45.0-wmf.11/extensions/EventBus/includes/Serializers/MediaWiki/PageChangeEventSerializer.php(189)MediaWiki\Extension\EventBus\Serializers\MediaWiki\RevisionEntitySerializer->toArray(MediaWiki\Revision\RevisionStoreCacheRecord)
#8/srv/mediawiki/php-1.45.0-wmf.11/extensions/EventBus/includes/Serializers/MediaWiki/PageChangeEventSerializer.php(370)MediaWiki\Extension\EventBus\Serializers\MediaWiki\PageChangeEventSerializer->toCommonAttrs(string, string, MediaWiki\Page\PageStoreRecord, MediaWiki\User\User, MediaWiki\Revision\RevisionStoreCacheRecord, null, string)
#9/srv/mediawiki/php-1.45.0-wmf.11/extensions/EventBus/includes/MediaWikiEventSubscribers/PageChangeEventIngress.php(327)MediaWiki\Extension\EventBus\Serializers\MediaWiki\PageChangeEventSerializer->toDeleteEvent(string, MediaWiki\Page\PageStoreRecord, MediaWiki\User\User, MediaWiki\Revision\RevisionStoreCacheRecord, string, string, int, null, bool)
#10/srv/mediawiki/php-1.45.0-wmf.11/includes/DomainEvent/EventDispatchEngine.php(204)MediaWiki\Extension\EventBus\MediaWikiEventSubscribers\PageChangeEventIngress->handlePageDeletedEvent(MediaWiki\Page\Event\PageDeletedEvent)
#11/srv/mediawiki/php-1.45.0-wmf.11/includes/DomainEvent/EventDispatchEngine.php(193)MediaWiki\DomainEvent\EventDispatchEngine->invoke(array, MediaWiki\Page\Event\PageDeletedEvent)
#12/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/MWCallableUpdate.php(52)MediaWiki\DomainEvent\EventDispatchEngine->MediaWiki\DomainEvent\{closure}(string)
#13/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdates.php(459)MediaWiki\Deferred\MWCallableUpdate->doUpdate()
#14/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdates.php(201)MediaWiki\Deferred\DeferredUpdates::attemptUpdate(MediaWiki\Deferred\MWCallableUpdate)
#15/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdates.php(288)MediaWiki\Deferred\DeferredUpdates::run(MediaWiki\Deferred\MWCallableUpdate)
#16/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdatesScope.php(243)MediaWiki\Deferred\DeferredUpdates::MediaWiki\Deferred\{closure}(MediaWiki\Deferred\MWCallableUpdate, int)
#17/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdatesScope.php(172)MediaWiki\Deferred\DeferredUpdatesScope->processStageQueue(int, int, Closure)
#18/srv/mediawiki/php-1.45.0-wmf.11/includes/deferred/DeferredUpdates.php(307)MediaWiki\Deferred\DeferredUpdatesScope->processUpdates(int, Closure)
#19/srv/mediawiki/php-1.45.0-wmf.11/includes/MediaWikiEntryPoint.php(670)MediaWiki\Deferred\DeferredUpdates::doUpdates()
#20/srv/mediawiki/php-1.45.0-wmf.11/includes/MediaWikiEntryPoint.php(492)MediaWiki\MediaWikiEntryPoint->restInPeace()
#21/srv/mediawiki/php-1.45.0-wmf.11/includes/MediaWikiEntryPoint.php(450)MediaWiki\MediaWikiEntryPoint->doPostOutputShutdown()
#22/srv/mediawiki/php-1.45.0-wmf.11/includes/MediaWikiEntryPoint.php(207)MediaWiki\MediaWikiEntryPoint->postOutputShutdown()
#23/srv/mediawiki/php-1.45.0-wmf.11/index.php(58)MediaWiki\MediaWikiEntryPoint->run()
#24/srv/mediawiki/w/index.php(3)require(string)
#25{main}
Notes
  • Started about two weeks ago (on 1.45.0-wmf.9 2025-07-10)
  • Happening at a low rate across wikis (enwiki, bnwikisource, wiktionary, etc)

Event Timeline

Umherirrender subscribed.

This is a delete action, which does not find the deleted revision any more.

This happens via DomainEvents (after T392205). The latest revision of the deleted event is a cached record and try to refresh itself to avoid steal data, but domain events send after the deletion happens and nothing can be refreshed in that case, the row is deleted.

It seems that MediaWiki-Core-Revision-backend is not responsible here, adding other tags.

Hm, I'm a bit lost at this point in the stack trace:

from	/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreCacheRecord.php(114)
	
#0	/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionStoreCacheRecord.php(78)	MediaWiki\Revision\RevisionStoreCacheRecord->loadFreshRow()

#1	/srv/mediawiki/php-1.45.0-wmf.11/includes/Revision/RevisionRecord.php(489)	MediaWiki\Revision\RevisionStoreCacheRecord->getVisibility()

Why is getVisibility here causing a load from the database?

The latest revision of the deleted event is a cached record and try to refresh itself to avoid steal data,

Is this the answer? Post transaction (e.g. DeferredUpdates etc.) callbacks do not have access to the original RevisionRecord that was deleted?

Hm, I think I see. After page delete and RevisionDelete (visibility change), attempts to access any potentially sensitive bits of the RevisionRecord will always cause a re-lookup from the database to avoid exposing e.g. comment or content fields. Is this right?

Okay, in this case then, I think we can just avoid attempting to set things like revision comment and visibility settings if we can't access them.

I'm not exactly sure how to detect this without catching the RevisionAccessException though.

Given a RevisionRecord (or specifically in this case, a RevisionStoreCacheRecord), how can I detect that I will not be able to access the comment or visibility settings (via RevisionRecord->isDeleted(...) ) without causing a RevisionAccessException?

cc @daniel

Ottomata changed the task status from Open to In Progress.Sep 23 2025, 7:08 PM
Ottomata claimed this task.

@Ottomata I can't think of a clean solution for this situation off the top of my head. Two thoughts:

  • Maybe the exception shouldn't be an exception. RevisionRecords are "conceptually immutable" and should relect the state of the database at the moment the object was created (though lazy loading applies). So if RevisionStoreCacheRecord fails to find the row, perhaps it should simply use the "old" values. That will result in the expected behavior more often than throwing an exception would. I think.
  • If you catch the exception and then call getVisibility() again, you should get the old cached data. RevisionStoreCacheRecord only tries the callback once and gives up after that. That wasn't intended for the error case - the idea was to avoid redundant queries on the "happy path". But it should work... at least for now... So "just try once again if it fails the first time" could provide a workable solution. Though it should have a big fat comment pointing to this ticket :)

If you catch the exception and then call getVisibility() again, you should get the old cached data.

Oh, wow. Okay, will do.

Change #1191152 had a related patch set uploaded (by Ottomata; author: Ottomata):

[mediawiki/extensions/EventBus@master] RevisionEntitySerializer - retry call to getVisibility to avoid RevisionAccessException

https://gerrit.wikimedia.org/r/1191152

Change #1201113 had a related patch set uploaded (by Ottomata; author: Ottomata):

[mediawiki/core@master] WIP - RevisionStoreCacheRecord - add test about accessing data after failing to load from db

https://gerrit.wikimedia.org/r/1201113

@daniel, moving the convo from the patch to this ticket.

I tried adding a test to MW core for this: WIP - RevisionStoreCacheRecord - add test about accessing data after failing to load from db (1201113)

It fails!

In the patch review, I wrote:

The callback provided to RevisionStoreCachedRecord is expected to lookup and return fresh $rev_deleted and UserIdentity. However, in this case, the callback fails looking it up and returns [null, null] for these, which is what triggers the RevisionAccessException in loadFreshRow.

So, if we catch the RevisionAccessException, we will be able to access most parts of the RevisionRecord, but not the $rev_deleted (visibility) bits or the revision's editor UserIdentity.

This means it is not possible to access the Revision's $rev_deleted or User from a RevisionStoreCachedRecord if they cannot be loaded from the db. The RevisionStoreCachedRecord delays even setting those properties until they are accessed, so they aren't actually (ever) 'cached' in memory.

Perhaps...RevisionRecords provided to DomainEvent handlers should not use RevisionStoreCachedRecord?

Or, I understand why you might want User to always be fresh / not accessible if the user is not visible in $rev_deleted. But, why should the $rev_deleted be not accessible? The actual visibility settings of a Revision are not privacy sensitive?

Could RevisionStoreCachedRecord cache $rev_deleted along with other revision properties, but not User?

Change #1238426 had a related patch set uploaded (by Ottomata; author: Ottomata):

[mediawiki/extensions/EventBus@master] RevisionEntitySerializer - avoid RevisionAccessException on page delete

https://gerrit.wikimedia.org/r/1238426

Change #1238426 abandoned by Ottomata:

[mediawiki/extensions/EventBus@master] RevisionEntitySerializer - avoid RevisionAccessException on page delete

Reason:

I2861ff3d6e9cac72e222c8fae638bda71a6c2a95 was the original change. this was mistakenly submitted as new Change-Id.

https://gerrit.wikimedia.org/r/1238426

So, if we catch the RevisionAccessException, we will be able to access most parts of the RevisionRecord, but not the $rev_deleted (visibility) bits or the revision's editor UserIdentity.

This is wrong. I guess busy from Nov 3 2025 me couldn't read code. Daniel's suggestion works just fine.

"just try once again if it fails the first time" could provide a workable solution.

Change #1245037 had a related patch set uploaded (by Aaron Schulz; author: Aaron Schulz):

[mediawiki/core@master] page: make WikiPage::loadFromRow() clear mLastRevision from object cache

https://gerrit.wikimedia.org/r/1245037

@aaron sweet! Sooooo...what will that do in relation to this bug? :D

Change #1201113 merged by jenkins-bot:

[mediawiki/core@master] RevisionStoreCacheRecordTest - test intentional access of stale data after failing to load fresh

https://gerrit.wikimedia.org/r/1201113

Change #1245037 merged by jenkins-bot:

[mediawiki/core@master] page: make WikiPage::loadFromRow() clear revision fields in more cases

https://gerrit.wikimedia.org/r/1245037

@aaron sweet! Sooooo...what will that do in relation to this bug? :D

If we avoid the RevisionStoreCacheRecord class, which has the lazy-loading, then the bug should not happen in DomainEvent handlers anymore since they can't trigger the lazy-load with the regular RevisionStoreRecord class.

Ah yes, that would be best! TY

The error has disappeared!

I'm going to call this done and abandon my EventBus workaround patch! Thank you @aaron!

Change #1191152 abandoned by Ottomata:

[mediawiki/extensions/EventBus@master] RevisionEntitySerializer - avoid RevisionAccessException on page delete

Reason:

Was fixed upstream in MW core.

https://phabricator.wikimedia.org/T400380#11908367

https://gerrit.wikimedia.org/r/1191152