|
24 | 24 |
|
25 | 25 | namespace OC\Files\Cache; |
26 | 26 |
|
27 | | -use OC\DB\QueryBuilder\QueryFunction; |
| 27 | +use Doctrine\DBAL\Exception\RetryableException; |
28 | 28 | use OC\Files\Storage\Wrapper\Encryption; |
29 | 29 | use OCP\DB\QueryBuilder\IQueryBuilder; |
30 | 30 | use OCP\Files\Cache\IPropagator; |
31 | 31 | use OCP\Files\Storage\IReliableEtagStorage; |
32 | 32 | use OCP\IDBConnection; |
| 33 | +use Psr\Log\LoggerInterface; |
33 | 34 |
|
34 | 35 | /** |
35 | 36 | * Propagate etags and mtimes within the storage |
36 | 37 | */ |
37 | 38 | class Propagator implements IPropagator { |
| 39 | + public const MAX_RETRIES = 3; |
38 | 40 | private $inBatch = false; |
39 | 41 |
|
40 | 42 | private $batch = []; |
@@ -101,35 +103,44 @@ public function propagateChange($internalPath, $time, $sizeDifference = 0) { |
101 | 103 | $builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)); |
102 | 104 | } |
103 | 105 |
|
104 | | - $builder->execute(); |
105 | | - |
106 | 106 | if ($sizeDifference !== 0) { |
107 | | - // we need to do size separably so we can ignore entries with uncalculated size |
108 | | - $builder = $this->connection->getQueryBuilder(); |
109 | | - $builder->update('filecache') |
110 | | - ->set('size', $builder->func()->greatest( |
111 | | - $builder->func()->add('size', $builder->createNamedParameter($sizeDifference)), |
112 | | - $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) |
113 | | - )) |
114 | | - ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) |
115 | | - ->andWhere($builder->expr()->in('path_hash', $hashParams)) |
116 | | - ->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT))); |
| 107 | + $hasCalculatedSize = $builder->expr()->gt('size', $builder->expr()->literal(-1, IQUeryBuilder::PARAM_INT)); |
| 108 | + $sizeColumn = $builder->getColumnName('size'); |
| 109 | + $newSize = $builder->func()->greatest( |
| 110 | + $builder->func()->add('size', $builder->createNamedParameter($sizeDifference)), |
| 111 | + $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) |
| 112 | + ); |
| 113 | + |
| 114 | + // Only update if row had a previously calculated size |
| 115 | + $builder->set('size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newSize ELSE $sizeColumn END")); |
117 | 116 |
|
118 | 117 | if ($this->storage->instanceOfStorage(Encryption::class)) { |
119 | 118 | // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size |
120 | | - $eq = $builder->expr()->eq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT)); |
| 119 | + $hasUnencryptedSize = $builder->expr()->neq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT)); |
121 | 120 | $sizeColumn = $builder->getColumnName('size'); |
122 | 121 | $unencryptedSizeColumn = $builder->getColumnName('unencrypted_size'); |
123 | | - $builder->set('unencrypted_size', $builder->func()->greatest( |
| 122 | + $newUnencryptedSize = $builder->func()->greatest( |
124 | 123 | $builder->func()->add( |
125 | | - new QueryFunction("CASE WHEN $eq THEN $unencryptedSizeColumn ELSE $sizeColumn END"), |
| 124 | + $builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $unencryptedSizeColumn ELSE $sizeColumn END"), |
126 | 125 | $builder->createNamedParameter($sizeDifference) |
127 | 126 | ), |
128 | 127 | $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) |
129 | | - )); |
| 128 | + ); |
| 129 | + |
| 130 | + // Only update if row had a previously calculated size |
| 131 | + $builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END")); |
130 | 132 | } |
| 133 | + } |
131 | 134 |
|
132 | | - $builder->execute(); |
| 135 | + for ($i = 0; $i < self::MAX_RETRIES; $i++) { |
| 136 | + try { |
| 137 | + $builder->executeStatement(); |
| 138 | + break; |
| 139 | + } catch (RetryableException $e) { |
| 140 | + /** @var LoggerInterface $loggerInterface */ |
| 141 | + $loggerInterface = \OC::$server->get(LoggerInterface::class); |
| 142 | + $loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]); |
| 143 | + } |
133 | 144 | } |
134 | 145 | } |
135 | 146 |
|
|
0 commit comments