|  | 
| 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; | 
|  | 
| 34 | 35 |  * Propagate etags and mtimes within the storage | 
| 35 | 36 |  */ | 
| 36 | 37 | class Propagator implements IPropagator { | 
|  | 38 | +	public const MAX_RETRIES = 3; | 
| 37 | 39 | 	private $inBatch = false; | 
| 38 | 40 | 
 | 
| 39 | 41 | 	private $batch = []; | 
| @@ -100,35 +102,42 @@ public function propagateChange($internalPath, $time, $sizeDifference = 0) { | 
| 100 | 102 | 			$builder->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)); | 
| 101 | 103 | 		} | 
| 102 | 104 | 
 | 
| 103 |  | -		$builder->execute(); | 
| 104 |  | - | 
| 105 | 105 | 		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))); | 
|  | 106 | +			$hasCalculatedSize = $builder->expr()->gt('size', $builder->expr()->literal(-1, IQUeryBuilder::PARAM_INT)); | 
|  | 107 | +			$sizeColumn = $builder->getColumnName('size'); | 
|  | 108 | +			$newSize = $builder->func()->greatest( | 
|  | 109 | +				$builder->func()->add('size', $builder->createNamedParameter($sizeDifference)), | 
|  | 110 | +				$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) | 
|  | 111 | +			); | 
|  | 112 | + | 
|  | 113 | +			// Only update if row had a previously calculated size | 
|  | 114 | +			$builder->set('size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newSize ELSE $sizeColumn END")); | 
| 116 | 115 | 
 | 
| 117 | 116 | 			if ($this->storage->instanceOfStorage(Encryption::class)) { | 
| 118 | 117 | 				// 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)); | 
|  | 118 | +				$hasUnencryptedSize = $builder->expr()->neq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT)); | 
| 120 | 119 | 				$sizeColumn = $builder->getColumnName('size'); | 
| 121 | 120 | 				$unencryptedSizeColumn = $builder->getColumnName('unencrypted_size'); | 
| 122 |  | -				$builder->set('unencrypted_size', $builder->func()->greatest( | 
|  | 121 | +				$newUnencryptedSize = $builder->func()->greatest( | 
| 123 | 122 | 					$builder->func()->add( | 
| 124 |  | -						$builder->createFunction("CASE WHEN $eq THEN $unencryptedSizeColumn ELSE $sizeColumn END"), | 
|  | 123 | +						$builder->createFunction("CASE WHEN $hasUnencryptedSize THEN $sizeColumn ELSE $unencryptedSizeColumn END"), | 
| 125 | 124 | 						$builder->createNamedParameter($sizeDifference) | 
| 126 | 125 | 					), | 
| 127 | 126 | 					$builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT) | 
| 128 |  | -				)); | 
|  | 127 | +				); | 
|  | 128 | + | 
|  | 129 | +				// Only update if row had a previously calculated size | 
|  | 130 | +				$builder->set('unencrypted_size', $builder->createFunction("CASE WHEN $hasCalculatedSize THEN $newUnencryptedSize ELSE $unencryptedSizeColumn END")); | 
| 129 | 131 | 			} | 
|  | 132 | +		} | 
| 130 | 133 | 
 | 
| 131 |  | -			$builder->execute(); | 
|  | 134 | +		for ($i = 0; $i < self::MAX_RETRIES; $i++) { | 
|  | 135 | +			try { | 
|  | 136 | +				$builder->executeStatement(); | 
|  | 137 | +			} catch (RetryableException $e) { | 
|  | 138 | +				continue; | 
|  | 139 | +			} | 
|  | 140 | +			break; | 
| 132 | 141 | 		} | 
| 133 | 142 | 	} | 
| 134 | 143 | 
 | 
|  | 
0 commit comments