|  | 
| 24 | 24 | 
 | 
| 25 | 25 | namespace OC\Files\Cache; | 
| 26 | 26 | 
 | 
|  | 27 | +use Doctrine\DBAL\Exception\RetryableException; | 
| 27 | 28 | use OC\Files\Storage\Wrapper\Encryption; | 
| 28 | 29 | use OCP\DB\QueryBuilder\IQueryBuilder; | 
| 29 | 30 | use OCP\Files\Cache\IPropagator; | 
| 30 | 31 | use OCP\Files\Storage\IReliableEtagStorage; | 
| 31 | 32 | use OCP\IDBConnection; | 
|  | 33 | +use Psr\Log\LoggerInterface; | 
| 32 | 34 | 
 | 
| 33 | 35 | /** | 
| 34 | 36 |  * Propagate etags and mtimes within the storage | 
| 35 | 37 |  */ | 
| 36 | 38 | class Propagator implements IPropagator { | 
|  | 39 | +	public const MAX_RETRIES = 3; | 
| 37 | 40 | 	private $inBatch = false; | 
| 38 | 41 | 
 | 
| 39 | 42 | 	private $batch = []; | 
| @@ -100,35 +103,44 @@ public function propagateChange($internalPath, $time, $sizeDifference = 0) { | 
| 100 | 103 | 			$builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)); | 
| 101 | 104 | 		} | 
| 102 | 105 | 
 | 
| 103 |  | -		$builder->execute(); | 
| 104 |  | - | 
| 105 | 106 | 		if ($sizeDifference !== 0) { | 
| 106 |  | -			// we need to do size separably so we can ignore entries with uncalculated size | 
| 107 |  | -			$builder = $this->connection->getQueryBuilder(); | 
| 108 |  | -			$builder->update('filecache') | 
| 109 |  | -				->set('size', $builder->func()->greatest( | 
| 110 |  | -					$builder->func()->add('size', $builder->createNamedParameter($sizeDifference)), | 
| 111 |  | -					$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) | 
| 112 |  | -				)) | 
| 113 |  | -				->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) | 
| 114 |  | -				->andWhere($builder->expr()->in('path_hash', $hashParams)) | 
| 115 |  | -				->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")); | 
| 116 | 116 | 
 | 
| 117 | 117 | 			if ($this->storage->instanceOfStorage(Encryption::class)) { | 
| 118 | 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 | 
| 119 |  | -				$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)); | 
| 120 | 120 | 				$sizeColumn = $builder->getColumnName('size'); | 
| 121 | 121 | 				$unencryptedSizeColumn = $builder->getColumnName('unencrypted_size'); | 
| 122 |  | -				$builder->set('unencrypted_size', $builder->func()->greatest( | 
|  | 122 | +				$newUnencryptedSize = $builder->func()->greatest( | 
| 123 | 123 | 					$builder->func()->add( | 
| 124 |  | -						$builder->createFunction("CASE WHEN $eq THEN $unencryptedSizeColumn ELSE $sizeColumn END"), | 
|  | 124 | +						$builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $unencryptedSizeColumn ELSE $sizeColumn END"), | 
| 125 | 125 | 						$builder->createNamedParameter($sizeDifference) | 
| 126 | 126 | 					), | 
| 127 | 127 | 					$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) | 
| 128 |  | -				)); | 
|  | 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")); | 
| 129 | 132 | 			} | 
|  | 133 | +		} | 
| 130 | 134 | 
 | 
| 131 |  | -			$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 = \OCP\Server::get(LoggerInterface::class); | 
|  | 142 | +				$loggerInterface->warning('Retrying propagation query after retryable exception.', [ 'exception' => $e ]); | 
|  | 143 | +			} | 
| 132 | 144 | 		} | 
| 133 | 145 | 	} | 
| 134 | 146 | 
 | 
|  | 
0 commit comments