Page MenuHomePhabricator

[INVESTIGATE] Find out if/how current rate limiting applies to REST API
Closed, ResolvedPublic

Description

Find out:

  • list of actions which apply to our REST API
  • which of these actions require us to enforce REST API rate limiting in our code

Time box: 16 hrs

Event Timeline

Ok, so the approach here was to a) look at all the standard rate limited actions and ponder which might apply to the REST API, and b) take the action API as a baseline and compare its calls to RateLimiter::limit() to those resulting from equivalent REST API requests.

TL;DR As far as I can tell the REST API already does everything the action API does by using the same MediawikiEditEntity::attemptSave() low-level service.

a) I looked at https://www.mediawiki.org/wiki/Manual:$wgRateLimits and https://gerrit.wikimedia.org/r/plugins/gitiles/operations/mediawiki-config/+/c44980b3fcfeb4d2139dac7311e33f599ea0788e/wmf-config/InitialiseSettings.php#7997 and only found "edit" and "create" of interest for our REST API so far. In part (b) of the investigation I also discovered "wikibase-idgenerator" here https://gerrit.wikimedia.org/g/mediawiki/extensions/Wikibase/+/cc66ac0089ce1eca84eaddf655791003d48259a2/extension-repo.json#964.

b) In this part I made requests to similar action API and REST API endpoints and simply put a breakpoint in RateLimiter::limit(). I didn't check all the endpoints because it is quite obvious that they behave similarly because of their implementation. Here are the results:

GET /rest.php/wikibase/v0/entities/items/{id}: no rate limiting
wbgetentities: no rate limiting


wbgetclaims: no rate limiting
GET /rest.php/wikibase/v0/entities/items/{id}/statements/{statement_id}: no rate limiting


wbcreateclaim:

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
EntitySavingHelper.php:387, Wikibase\Repo\Api\EntitySavingHelper->attemptSaveEntity()
CreateClaim.php:152, Wikibase\Repo\Api\CreateClaim->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

POST /rest.php/wikibase/v0/entities/items/{id}/statements

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
MediaWikiEditEntityFactoryItemUpdater.php:54, Wikibase\Repo\RestApi\DataAccess\MediaWikiEditEntityFactoryItemUpdater->update()
AddItemStatement.php:88, Wikibase\Repo\RestApi\UseCases\AddItemStatement\AddItemStatement->execute()
AddItemStatementRouteHandler.php:105, Wikibase\Repo\RestApi\RouteHandlers\AddItemStatementRouteHandler->runUseCase()
[...]
MiddlewareHandler.php:33, Wikibase\Repo\RestApi\RouteHandlers\Middleware\MiddlewareHandler->run()
AddItemStatementRouteHandler.php:90, Wikibase\Repo\RestApi\RouteHandlers\AddItemStatementRouteHandler->run()
SimpleHandler.php:38, Wikibase\Repo\RestApi\RouteHandlers\AddItemStatementRouteHandler->execute()
Router.php:487, MediaWiki\Rest\Router->executeHandler()
Router.php:406, MediaWiki\Rest\Router->execute()
EntryPoint.php:191, MediaWiki\Rest\EntryPoint->execute()
EntryPoint.php:131, MediaWiki\Rest\EntryPoint::main()
rest.php:31, {main}()

wbsetclaim:

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
EntitySavingHelper.php:387, Wikibase\Repo\Api\EntitySavingHelper->attemptSaveEntity()
SetClaim.php:210, Wikibase\Repo\Api\SetClaim->executeInternal()
SetClaim.php:163, Wikibase\Repo\Api\SetClaim->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

PUT /rest.php/wikibase/v0/entities/items/{id}/statements/{statement_id}:

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
MediaWikiEditEntityFactoryItemUpdater.php:54, Wikibase\Repo\RestApi\DataAccess\MediaWikiEditEntityFactoryItemUpdater->update()
ReplaceItemStatement.php:106, Wikibase\Repo\RestApi\UseCases\ReplaceItemStatement\ReplaceItemStatement->execute()
ReplaceItemStatementRouteHandler.php:97, Wikibase\Repo\RestApi\RouteHandlers\ReplaceItemStatementRouteHandler->runUseCase()
[...]
MiddlewareHandler.php:33, Wikibase\Repo\RestApi\RouteHandlers\Middleware\MiddlewareHandler->run()
ReplaceItemStatementRouteHandler.php:91, Wikibase\Repo\RestApi\RouteHandlers\ReplaceItemStatementRouteHandler->run()
SimpleHandler.php:38, Wikibase\Repo\RestApi\RouteHandlers\ReplaceItemStatementRouteHandler->execute()
Router.php:487, MediaWiki\Rest\Router->executeHandler()
Router.php:406, MediaWiki\Rest\Router->execute()
EntryPoint.php:191, MediaWiki\Rest\EntryPoint->execute()
EntryPoint.php:131, MediaWiki\Rest\EntryPoint::main()
rest.php:31, {main}()

Just out of curiosity, I also checked two action API endpoints which aren't implemented by the REST API yet, but are coming soon.

wbsetlabel:

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
EntitySavingHelper.php:387, Wikibase\Repo\Api\EntitySavingHelper->attemptSaveEntity()
ModifyEntity.php:340, Wikibase\Repo\Api\SetLabel->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

wbeditentity when creating a new item: 3 calls

$action = "wikibase-idgenerator"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
RateLimitingIdGenerator.php:33, Wikibase\Repo\Store\RateLimitingIdGenerator->getNewId()
WikiPageEntityStore.php:178, Wikibase\Repo\Store\Sql\WikiPageEntityStore->assignFreshId()
TypeDispatchingEntityStore.php:59, Wikibase\Lib\Store\TypeDispatchingEntityStore->assignFreshId()
EntitySavingHelper.php:298, Wikibase\Repo\Api\EntitySavingHelper->createEntity()
EntitySavingHelper.php:228, Wikibase\Repo\Api\EntitySavingHelper->loadEntity()
ModifyEntity.php:382, Wikibase\Repo\Api\EditEntity->loadEntityFromSavingHelper()
ModifyEntity.php:306, Wikibase\Repo\Api\EditEntity->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

$action = "edit"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:539, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
EntitySavingHelper.php:387, Wikibase\Repo\Api\EntitySavingHelper->attemptSaveEntity()
ModifyEntity.php:340, Wikibase\Repo\Api\EditEntity->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

$action = "create"
$incrBy = {int} 1

RateLimiter.php:141, MediaWiki\Permissions\RateLimiter->limit()
User.php:1472, User->pingLimiter()
MediawikiEditEntity.php:540, Wikibase\Repo\EditEntity\MediawikiEditEntity->checkRateLimits()
MediawikiEditEntity.php:694, Wikibase\Repo\EditEntity\MediawikiEditEntity->attemptSave()
StatsdSaveTimeRecordingEditEntity.php:77, Wikibase\Repo\EditEntity\StatsdSaveTimeRecordingEditEntity->attemptSave()
EntitySavingHelper.php:387, Wikibase\Repo\Api\EntitySavingHelper->attemptSaveEntity()
ModifyEntity.php:340, Wikibase\Repo\Api\EditEntity->execute()
ApiMain.php:1902, ApiMain->executeAction()
ApiMain.php:877, ApiMain->executeActionWithErrorHandling()
ApiMain.php:848, ApiMain->execute()
api.php:90, wfApiMain()
api.php:45, {main}()

nice, thank you! 👍

          #
  #      ##
  #     # #
#####     #
  #       #
  #       #
        #####