We should use advisory locks to prevent page actions such as deletions, moves, and imports (and maybe also edit) from interfering with each other. This will allow us to present a meaningful message to the user if an action cannot be performed because another action is ongoing. Currently, this situation often leads to backend errors doe to database lock timeouts or to client side browser timeouts. It canalso lead to confusion for the user, when a page that they just moved just vanishes immediately because someone else deleted it at the same time.
Background:
This came out of an analysis of the cause for T382699: Page Deletion Fatal exception of type "Wikimedia\Rdbms\DBQueryError" due to lock timeout:
Currently, the critical section of a page operation will holds a lock on the relevant database tables/rows. Another action trying to update these rows will be blocked, and that block may eventually time out, on the server or on the client side. In the case of T382699, the browser timed out on the original deletion request, so the user tried again, but failed to get a database lock, which led to a fatal error for the second request. The original request eventually completed successfully.
Using an advisory lock that can be tested in a non-blocking way would allow us to immedately inform the user of the situation.
Implementation notes:
- Locking should be performed by "command" classes like MovePage, DeletePage, etc.
- Locks should be obtained from a PageLockManager service.
- Care must be taken that the lock is freed eventually if the process performing the action terminates unexpectedly.
- We could use one of the LockManager implementation we also use in FileBackend.
- Alternatively, we could use PoolCounter, which suppotrs automatic lock release. We could also create a LockManager implementation based on PoolCounter.