Page MenuHomePhabricator

Special:Abuselog throws when viewing details or examining (BadMethodCallException: Call get getId() on null)
Open, Stalled, HighPublic

Description

When I view details or examine of all items in Abuselog of Abusefilter 131 on zh.wikipedia. I get Internal error.
Ex.
Special:Abuselog/672224 : [WoJqhgpAIDEAAFY2JuoAAAAI] 2018-02-13 04:33:11: Fatal exception of type "BadMethodCallException"
Special:Abusefilter/examine/log/672224 : [WoJqrgpAIC8AAGfcNPkAAADI] 2018-02-13 04:33:51: Fatal exception of type "BadMethodCallException"

timestamp: 2018-07-17T15:23:25
exception_id: W04J7ApAME4AAKNdoBQAAAAC

BadMethodCallException : Call to a member function getId() on a non-object (null)
#0 /srv/mediawiki/php-1.32.0-wmf.12/includes/page/WikiPage.php(548): Revision->getId()
#1 /srv/mediawiki/php-1.32.0-wmf.12/includes/page/WikiPage.php(487): WikiPage->loadFromRow(stdClass, integer)
#2 /srv/mediawiki/php-1.32.0-wmf.12/includes/Storage/DerivedPageDataUpdater.php(503): WikiPage->loadPageData(integer)
#3 /srv/mediawiki/php-1.32.0-wmf.12/includes/Storage/DerivedPageDataUpdater.php(727): MediaWiki\Storage\DerivedPageDataUpdater->grabCurrentRevision()
#4 /srv/mediawiki/php-1.32.0-wmf.12/includes/page/WikiPage.php(1948): MediaWiki\Storage\DerivedPageDataUpdater->prepareContent(User, MediaWiki\Storage\RevisionSlotsUpdate, boolean)
#5 /srv/mediawiki/php-1.32.0-wmf.12/includes/page/Article.php(2471): WikiPage->prepareContentForEdit(WikitextContent, NULL, User, NULL, boolean)
#6 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/AFComputedVariable.php(195): Article->prepareContentForEdit(WikitextContent)
#7 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(49): AFComputedVariable->compute(AbuseFilterVariableHolder)
#8 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(188): AbuseFilterVariableHolder->getVar(string)
#9 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/Views/AbuseFilterViewExamine.php(156): AbuseFilterVariableHolder->dumpAllVars(boolean)
#10 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/Views/AbuseFilterViewExamine.php(27): AbuseFilterViewExamine->showExaminerForLogEntry(string)
#11 /srv/mediawiki/php-1.32.0-wmf.12/extensions/AbuseFilter/includes/special/SpecialAbuseFilter.php(122): AbuseFilterViewExamine->show()
#12 /srv/mediawiki/php-1.32.0-wmf.12/includes/specialpage/SpecialPage.php(566): SpecialAbuseFilter->execute(string)
#13 /srv/mediawiki/php-1.32.0-wmf.12/includes/specialpage/SpecialPageFactory.php(569): SpecialPage->run(string)
#14 /srv/mediawiki/php-1.32.0-wmf.12/includes/MediaWiki.php(288): SpecialPageFactory::executePath(Title, RequestContext)
#15 /srv/mediawiki/php-1.32.0-wmf.12/includes/MediaWiki.php(867): MediaWiki->performRequest()
#16 /srv/mediawiki/php-1.32.0-wmf.12/includes/MediaWiki.php(524): MediaWiki->main()
#17 /srv/mediawiki/php-1.32.0-wmf.12/index.php(42): MediaWiki->run()
#18 /srv/mediawiki/w/index.php(3): include(string)
#19 {main}

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Daimona renamed this task from BadMethodCallException (mRecord->getContent() when mRecord is null) when viewing details or examine of Abuselog of Abusefilter 131 on zh.wikipedia to BadMethodCallException (mRecord->getContent() when mRecord is null) when viewing details or examine of Abuselog.Mar 17 2018, 5:36 PM

So, do we only need a cleanup script? Also, to me it's not totally clear what it should clean. Pinging @Addshore since he got straight to the cause of this bug.

daniel moved this task from Epic to Inbox on the Multi-Content-Revisions board.May 7 2018, 10:44 AM

Marking this as "external" on the MCR board. This is an AbuseFilter bug. PHP serialization is extremely brittle, and should never be used for long term storage. AbuseFilter needs to be changed to handle this problem gracefully. Any call to unserialize() should be treated as unsafe, the return value needs to be checked, and errors suppressed or caught. Note that the use of unserialize() is also discouraged for security reasons, see T161647: RFC: Deprecate using php serialization inside MediaWiki.

Creating a script for cleaning up old bad log entries would be nice, but is not necessary if failures to read the log entries are handled gracefully.

I understand. This means that we need some kind of major change anyway, but I'm not sure I can do it myself.

abian added a subscriber: abian.May 8 2018, 11:59 AM

This is still happening.

Update stacktrace:

2018-06-25 14:37:42 1.32.0-wmf.8 exception ERROR: [WzD@NgpAAEUAAF@SXcsAAABA] /wiki/Especial:RegistroAbusos/2842   BadMethodCallException from line 903 of /srv/mediawiki/php-1.32.0-wmf.8/includes/Revision.php: Call to a member function getContent() on a non-object (null) {"exception_id":"WzD@NgpAAEUAAF@SXcsAAABA","exception_url":"/wiki/Especial:RegistroAbusos/2842","caught_by":"mwe_handler"}
[Exception BadMethodCallException] (/srv/mediawiki/php-1.32.0-wmf.8/includes/Revision.php:903) Call to a member function getContent() on a non-object (null)
  #0 /srv/mediawiki/php-1.32.0-wmf.8/includes/page/WikiPage.php(721): Revision->getContent(integer, NULL)
  #1 /srv/mediawiki/php-1.32.0-wmf.8/includes/page/WikiPage.php(2154): WikiPage->getContent(integer)
  #2 /srv/mediawiki/php-1.32.0-wmf.8/includes/page/Article.php(2471): WikiPage->prepareContentForEdit(WikitextContent, NULL, User, string, boolean)
  #3 /srv/mediawiki/php-1.32.0-wmf.8/extensions/AbuseFilter/includes/AFComputedVariable.php(195): Article->prepareContentForEdit(WikitextContent)
  #4 /srv/mediawiki/php-1.32.0-wmf.8/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(49): AFComputedVariable->compute(AbuseFilterVariableHolder)
  #5 /srv/mediawiki/php-1.32.0-wmf.8/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(188): AbuseFilterVariableHolder->getVar(string)
  #6 /srv/mediawiki/php-1.32.0-wmf.8/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(483): AbuseFilterVariableHolder->dumpAllVars(boolean)
  #7 /srv/mediawiki/php-1.32.0-wmf.8/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(100): SpecialAbuseLog->showDetails(string)
  #8 /srv/mediawiki/php-1.32.0-wmf.8/includes/specialpage/SpecialPage.php(565): SpecialAbuseLog->execute(string)
  #9 /srv/mediawiki/php-1.32.0-wmf.8/includes/specialpage/SpecialPageFactory.php(569): SpecialPage->run(string)
  #10 /srv/mediawiki/php-1.32.0-wmf.8/includes/MediaWiki.php(288): SpecialPageFactory::executePath(Title, RequestContext)
  #11 /srv/mediawiki/php-1.32.0-wmf.8/includes/MediaWiki.php(864): MediaWiki->performRequest()
  #12 /srv/mediawiki/php-1.32.0-wmf.8/includes/MediaWiki.php(524): MediaWiki->main()
  #13 /srv/mediawiki/php-1.32.0-wmf.8/index.php(42): MediaWiki->run()
  #14 /srv/mediawiki/w/index.php(3): include(string)
  #15 {main}

https://es.wiktionary.org/wiki/Especial:RegistroAbusos/2842

@Addshore what was the proposed solution for this?

@Addshore what was the proposed solution for this?

It looks like the issue with serializing Article objects is already fixed on newer entries.

We need a cleanup script.

@Addshore that's how to apply the solution, not the solution itself :-)

You can see a sample serialized blob with the following in eval.php for enwiki from 20120914230240:

$dbr = wfGetDB( DB_REPLICA );
$text_row = $dbr->selectRow( 'text', [ 'old_text', 'old_flags' ], [ 'old_id' => 515800020 ] );
$text = ExternalStore::fetchFromURL( $text_row->old_text );
$text = gzinflate( $text );
var_dump( $text );

May someone please paste the content of that row (I can't see it) so that I'll be able to do some testing? It should also be possible to retrieve it with fetchText.php using the old_id above.

May someone please paste the content of that row (I can't see it) so that I'll be able to do some testing? It should also be possible to retrieve it with fetchText.php using the old_id above.

So the text stored for the problem rows is the PHP serialization of a PHP Revision object, using whatever version of the Revision class that happened to exist when the row / text was inserted.
The reason we see the errors are because the Revision class has changed, so the serialized object can no longer be deserialized / converted back into a PHP object.
Newer rows use JSON instead of php serialization.
A possible fix therefor is to convert the php serialization for the erroring rows into the equivalent JSON serialization and re store them.

Good! And is there a way to determine whether a row is causing this error?

Krinkle updated the task description. (Show Details)Jul 18 2018, 11:52 PM
Krinkle renamed this task from BadMethodCallException (mRecord->getContent() when mRecord is null) when viewing details or examine of Abuselog to Special:Abuselog throws BadMethodCallException when viewing details or examining (Call getContent() on null mRecord).
Huji lowered the priority of this task from High to Normal.Jul 19 2018, 1:25 AM
Huji added a subscriber: Huji.

@Daimona it doesn't matter; we should update *all* rows that use serialized data.

The real issue is something else though: how do we know which version of Revision was in use when each serialized abuse log entry was created?

Reducing it to normal priority, because we don't have a clear understanding of the trade-off of cost and benefit here. We don't know how much work it is to JSONize the serialized data (cost) and we don't know how much value there is in fixing this issue (benefit).

Krinkle renamed this task from Special:Abuselog throws BadMethodCallException when viewing details or examining (Call getContent() on null mRecord) to Special:Abuselog throws when viewing details or examining (BadMethodCallException: Call get getId() on null).Sep 12 2018, 4:35 PM

Whatever we decide to do, we should handle this together with T204236, which needs a maintenance script for the afl_var_dump field as well. As I wrote in T204236#4581410, we should also change the way we store the dump to be simpler, and JSON may help with it.

Daimona moved this task from Backlog to Future on the User-Daimona board.

@daniel I suspect this might (also) identify a problem or regression with the RevisionRecord/DerivedDataUpdater refactors.

Note how the trace is reaching from WikiPage->prepareContentForEdit(WikitextContent, NULL, User, NULL, boolean) all the way to Revision->getId, whilst having somehow created a Revision object that has a non-object held by mRecord.

That shouldn't be possible. In the case of AbuseFilter it probably means that once this issue is fixed, it'll still be broken, just with detection for it at an earlier point. But we should be detecting that to also avoid other such issues in the future.

The report from T207545 identified an instance from en.wikipedia.org (first time I think, previously was zh.wikipedia.org):

exception_id: W8s79wpAADsAAFlsN8wAAACW
exception_url: /wiki/Special:AbuseLog/7343365 [restricted]

> BadMethodCallException: Call to a member function getId() on a non-object (null)

#0 /srv/mediawiki/php-1.32.0-wmf.26/includes/Storage/DerivedPageDataUpdater.php(538): Revision->getId()
#1 /srv/mediawiki/php-1.32.0-wmf.26/includes/Storage/DerivedPageDataUpdater.php(750): MediaWiki\Storage\DerivedPageDataUpdater->grabCurrentRevision()
#2 /srv/mediawiki/php-1.32.0-wmf.26/includes/page/WikiPage.php(1981): MediaWiki\Storage\DerivedPageDataUpdater->prepareContent(User, MediaWiki\Storage\RevisionSlotsUpdate, boolean)
#3 /srv/mediawiki/php-1.32.0-wmf.26/includes/page/Article.php(2685): WikiPage->prepareContentForEdit(WikitextContent, NULL, User, NULL, boolean)
#4 /srv/mediawiki/php-1.32.0-wmf.26/extensions/AbuseFilter/includes/AFComputedVariable.php(214): Article->prepareContentForEdit(WikitextContent)
#5 /srv/mediawiki/php-1.32.0-wmf.26/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(60): AFComputedVariable->compute(AbuseFilterVariableHolder)
#6 /srv/mediawiki/php-1.32.0-wmf.26/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(193): AbuseFilterVariableHolder->getVar(string)
#7 /srv/mediawiki/php-1.32.0-wmf.26/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(564): AbuseFilterVariableHolder->dumpAllVars(boolean)
#8 /srv/mediawiki/php-1.32.0-wmf.26/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(108): SpecialAbuseLog->showDetails(string)
#9 /srv/mediawiki/php-1.32.0-wmf.26/includes/specialpage/SpecialPage.php(569): SpecialAbuseLog->execute(string)
#10 /srv/mediawiki/php-1.32.0-wmf.26/includes/specialpage/SpecialPageFactory.php(568): SpecialPage->run(string)
#11 /srv/mediawiki/php-1.32.0-wmf.26/includes/MediaWiki.php(288): MediaWiki\Special\SpecialPageFactory->executePath(Title, RequestContext)
#12 /srv/mediawiki/php-1.32.0-wmf.26/includes/MediaWiki.php(860): MediaWiki->performRequest()
#13 /srv/mediawiki/php-1.32.0-wmf.26/includes/MediaWiki.php(517): MediaWiki->main()
#14 /srv/mediawiki/php-1.32.0-wmf.26/index.php(42): MediaWiki->run()
#15 /srv/mediawiki/w/index.php(3): include(string)
#16 {main}
bd808 added a subscriber: bd808.

From T212352: Fatal error from https://en.wikipedia.org/wiki/Special:AbuseLog/8229629:

[XBrChQpAME8AAKEkZUIAAABU] 2018-12-19 22:13:25: Fatal exception of type "BadMethodCallException"

Error log output:

[XBrChQpAME8AAKEkZUIAAABU] /wiki/Special:AbuseLog/8229629   BadMethodCallException from line 652 of /srv/mediawiki/php-1.33.0-wmf.8/includes/Revision.php: Call to a member function getId() on a non-object (null)
#0 /srv/mediawiki/php-1.33.0-wmf.8/includes/page/WikiPage.php(565): Revision->getId()
#1 /srv/mediawiki/php-1.33.0-wmf.8/includes/page/WikiPage.php(504): WikiPage->loadFromRow(stdClass, integer)
#2 /srv/mediawiki/php-1.33.0-wmf.8/includes/Storage/DerivedPageDataUpdater.php(515): WikiPage->loadPageData(integer)
#3 /srv/mediawiki/php-1.33.0-wmf.8/includes/Storage/DerivedPageDataUpdater.php(748): MediaWiki\Storage\DerivedPageDataUpdater->grabCurrentRevision()
#4 /srv/mediawiki/php-1.33.0-wmf.8/includes/page/WikiPage.php(1998): MediaWiki\Storage\DerivedPageDataUpdater->prepareContent(User, MediaWiki\Storage\RevisionSlotsUpdate, boolean)
#5 /srv/mediawiki/php-1.33.0-wmf.8/includes/page/Article.php(2691): WikiPage->prepareContentForEdit(WikitextContent, NULL, User, NULL, boolean)
#6 /srv/mediawiki/php-1.33.0-wmf.8/extensions/AbuseFilter/includes/AFComputedVariable.php(216): Article->prepareContentForEdit(WikitextContent)
#7 /srv/mediawiki/php-1.33.0-wmf.8/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(60): AFComputedVariable->compute(AbuseFilterVariableHolder)
#8 /srv/mediawiki/php-1.33.0-wmf.8/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(193): AbuseFilterVariableHolder->getVar(string)
#9 /srv/mediawiki/php-1.33.0-wmf.8/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(564): AbuseFilterVariableHolder->dumpAllVars(boolean)
#10 /srv/mediawiki/php-1.33.0-wmf.8/extensions/AbuseFilter/includes/special/SpecialAbuseLog.php(108): SpecialAbuseLog->showDetails(string)
#11 /srv/mediawiki/php-1.33.0-wmf.8/includes/specialpage/SpecialPage.php(569): SpecialAbuseLog->execute(string)
#12 /srv/mediawiki/php-1.33.0-wmf.8/includes/specialpage/SpecialPageFactory.php(558): SpecialPage->run(string)
#13 /srv/mediawiki/php-1.33.0-wmf.8/includes/MediaWiki.php(288): MediaWiki\Special\SpecialPageFactory->executePath(Title, RequestContext)
#14 /srv/mediawiki/php-1.33.0-wmf.8/includes/MediaWiki.php(862): MediaWiki->performRequest()
#15 /srv/mediawiki/php-1.33.0-wmf.8/includes/MediaWiki.php(517): MediaWiki->main()
#16 /srv/mediawiki/php-1.33.0-wmf.8/index.php(42): MediaWiki->run()
#17 /srv/mediawiki/w/index.php(3): include(string)
#18 {main}

It should not be possible for $this->mRecord to be null in line 652 of /srv/mediawiki/php-1.33.0-wmf.8/includes/Revision.php. I made a patch that should help us to find out how it does become null.

Change 481014 had a related patch set uploaded (by Daniel Kinzler; owner: Daniel Kinzler):
[mediawiki/core@master] Assert that $mRecord is never null in Revision.

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

Change 481014 merged by jenkins-bot:
[mediawiki/core@master] Revision: Assert that $mRecord is never null in Revision

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

Change 482499 had a related patch set uploaded (by Daimona Eaytoy; owner: Daimona Eaytoy):
[mediawiki/extensions/AbuseFilter@master] Add a maintenance script to clean afl_var_dump

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

Daimona claimed this task.Jan 6 2019, 1:56 PM
Daimona moved this task from Future to Under review on the User-Daimona board.
demon removed a subscriber: demon.Feb 19 2019, 10:36 AM
Daimona merged a task: Restricted Task.Mar 17 2019, 3:09 PM
Daimona raised the priority of this task from Normal to High.
Daimona added subscribers: elukey, 4shadoww, Andrew and 2 others.

This is hitting us over and over.

Again a ton of exceptions registered from 2019-03-22 ~21 UTC:

#0 /srv/mediawiki/php-1.33.0-wmf.22/includes/page/WikiPage.php(575): Revision->getId()
#1 /srv/mediawiki/php-1.33.0-wmf.22/includes/page/WikiPage.php(514): WikiPage->loadFromRow(stdClass, integer)
#2 /srv/mediawiki/php-1.33.0-wmf.22/includes/Storage/DerivedPageDataUpdater.php(518): WikiPage->loadPageData(integer)
#3 /srv/mediawiki/php-1.33.0-wmf.22/includes/Storage/DerivedPageDataUpdater.php(751): MediaWiki\Storage\DerivedPageDataUpdater->grabCurrentRevision()
#4 /srv/mediawiki/php-1.33.0-wmf.22/includes/page/WikiPage.php(2009): MediaWiki\Storage\DerivedPageDataUpdater->prepareContent(User, MediaWiki\Storage\RevisionSlotsUpdate, boolean)
#5 /srv/mediawiki/php-1.33.0-wmf.22/extensions/AbuseFilter/includes/AFComputedVariable.php(216): WikiPage->prepareContentForEdit(WikitextContent)
#6 /srv/mediawiki/php-1.33.0-wmf.22/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(58): AFComputedVariable->compute(AbuseFilterVariableHolder)
#7 /srv/mediawiki/php-1.33.0-wmf.22/extensions/AbuseFilter/includes/AbuseFilterVariableHolder.php(106): AbuseFilterVariableHolder->getVar(string)
#8 /srv/mediawiki/php-1.33.0-wmf.22/extensions/AbuseFilter/includes/api/ApiQueryAbuseLog.php(218): AbuseFilterVariableHolder->exportAllVars()
#9 /srv/mediawiki/php-1.33.0-wmf.22/includes/api/ApiQuery.php(249): ApiQueryAbuseLog->execute()
#10 /srv/mediawiki/php-1.33.0-wmf.22/includes/api/ApiMain.php(1595): ApiQuery->execute()
#11 /srv/mediawiki/php-1.33.0-wmf.22/includes/api/ApiMain.php(531): ApiMain->executeAction()
#12 /srv/mediawiki/php-1.33.0-wmf.22/includes/api/ApiMain.php(502): ApiMain->executeActionWithErrorHandling()
#13 /srv/mediawiki/php-1.33.0-wmf.22/api.php(87): ApiMain->execute()
#14 /srv/mediawiki/w/api.php(3): include(string)
#15 {main}

Well, the maintscript is on gerrit. However, given that it'll touch many rows, I want to include there any maintenance that we need to perform on the abuse_filter_log table. Currently, it's blocked on deciding what to do with T213478 and T34478. If someone could please take a look, it'd be great.

@Daimona didn't mean to rush anybody, just added a comment as FYI for the task if needed! Thanks for the work :)

@elukey I didn't read it like that :-) Although, actually, this task deserves some attention, as it's really happening very often. Mine above was just a quick overview of the situation.

Change 482499 had a related patch set uploaded (by Daimona Eaytoy; owner: Daimona Eaytoy):
[mediawiki/extensions/AbuseFilter@master] [WIP] Add a maintenance script to clean afl_var_dump

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

I don't think this should be a train blocker: it was discovered in 2018 and only affects some old entries. Nevertheless, it's really high priority. Last weeks, the spike on logstash was likely caused by a bot or something like that mass-requesting corrupted abuselog entries. I cannot check right now if that's still true, though.

zeljkofilipin raised the priority of this task from High to Unbreak Now!.Wed, Apr 10, 3:20 PM
zeljkofilipin added a subscriber: zeljkofilipin.

Train blockers are UBN. If you don't think this is blocking the train, remove the parent task and change priority.

Restricted Application added a subscriber: TerraCodes. · View Herald TranscriptWed, Apr 10, 3:20 PM
Daimona lowered the priority of this task from Unbreak Now! to High.
Daimona changed the task status from Open to Stalled.

@zeljkofilipin Thanks for the heads-up, per my comment above this is not a TB IMHO. Actually, this is blocked on T34478 and T213478.

If we can't solve the root problem can we at least clean up the log spam? Log spam IS a train blocker.

@mmodell I guess we do, although I don't think it's worth doing anything beyond displaying an error instead of throwing. Also, this logspam is happening since at least one year, so it's a bit late to recognize it as train blocker.

At any rate, looking at Logstash I see that it's still due to that "pseudo-bot" I noticed last week. Maybe understanding where all of those requests come from and notifying the requester would already help?

Displaying an error instead of throwing sounds like exactly the right solution to me. The log spam is intermittent but that causes confusion and uncertainty when it happens around the time of a deployment.

Change 502946 had a related patch set uploaded (by Daimona Eaytoy; owner: Daimona Eaytoy):
[mediawiki/extensions/AbuseFilter@master] Temporarily catch BadMethodCallException when computing _links vars

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

Instead of displaying an error, I just made it so that _links variables aren't computed for faulty entries.
Nevertheless, I hope to get the root cause solved soon. Not only for this task, but also for related ones (listed in commit message).

Change 502946 merged by jenkins-bot:
[mediawiki/extensions/AbuseFilter@master] Temporarily catch BadMethodCallException when computing _links vars

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