fix(propagator): Lock rows also in propagateChange

Signed-off-by: Carl Schwan <carlschwan@kde.org>

[skip ci]
This commit is contained in:
Carl Schwan 2026-02-06 16:13:44 +01:00 committed by backportbot[bot]
parent 97a3ea7bac
commit fd9fec9b2a

View file

@ -9,6 +9,7 @@ namespace OC\Files\Cache;
use OC\DB\Exceptions\DbalException;
use OC\Files\Storage\Wrapper\Encryption;
use OCP\DB\QueryBuilder\ILiteral;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Storage\IReliableEtagStorage;
@ -81,9 +82,7 @@ class Propagator implements IPropagator {
$etag = uniqid(); // since we give all folders the same etag we don't ask the storage for the etag
$builder = $this->connection->getQueryBuilder();
$hashParams = array_map(function ($hash) use ($builder) {
return $builder->expr()->literal($hash);
}, $parentHashes);
$hashParams = array_map(static fn (string $hash): ILiteral => $builder->expr()->literal($hash), $parentHashes);
$builder->update('filecache')
->set('mtime', $builder->func()->greatest('mtime', $builder->createNamedParameter($time, IQueryBuilder::PARAM_INT)))
@ -124,9 +123,27 @@ class Propagator implements IPropagator {
for ($i = 0; $i < self::MAX_RETRIES; $i++) {
try {
$builder->executeStatement();
if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_SQLITE) {
$this->connection->beginTransaction();
// Lock all the rows first with a SELECT FOR UPDATE ordered by path_hash
$forUpdate = $this->connection->getQueryBuilder();
$forUpdate->select('fileid')
->from('filecache')
->where($forUpdate->expr()->eq('storage', $forUpdate->createNamedParameter($storageId, IQueryBuilder::PARAM_INT)))
->andWhere($forUpdate->expr()->in('path_hash', $hashParams))
->orderBy('path_hash')
->forUpdate()
->executeQuery();
$builder->executeStatement();
$this->connection->commit();
} else {
$builder->executeStatement();
}
break;
} catch (DbalException $e) {
if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_SQLITE) {
$this->connection->rollBack();
}
if (!$e->isRetryable()) {
throw $e;
}