From 8b3f4da52f7ee8075aa7a6e14dbc083c0c310cbf Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 6 Nov 2025 20:08:44 +0100 Subject: [PATCH 1/2] fix: make failed availability check apply in the same request Signed-off-by: Robin Appelman --- .../Files/Storage/Wrapper/Availability.php | 13 +++++++--- .../Storage/Wrapper/AvailabilityTest.php | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/private/Files/Storage/Wrapper/Availability.php b/lib/private/Files/Storage/Wrapper/Availability.php index 32c51a1b25e..dc2f9a2e6ac 100644 --- a/lib/private/Files/Storage/Wrapper/Availability.php +++ b/lib/private/Files/Storage/Wrapper/Availability.php @@ -22,6 +22,7 @@ class Availability extends Wrapper { /** @var IConfig */ protected $config; + protected ?bool $available = null; public function __construct(array $parameters) { $this->config = $parameters['config'] ?? \OCP\Server::get(IConfig::class); @@ -54,11 +55,14 @@ class Availability extends Wrapper { } private function isAvailable(): bool { - $availability = $this->getAvailability(); - if (self::shouldRecheck($availability)) { - return $this->updateAvailability(); + if (is_null($this->available)) { + $availability = $this->getAvailability(); + if (self::shouldRecheck($availability)) { + return $this->updateAvailability(); + } + $this->available = $availability['available']; } - return $availability['available']; + return $this->available; } /** @@ -257,6 +261,7 @@ class Availability extends Wrapper { self::RECHECK_TTL_SEC ); } + $this->available = false; $this->getStorageCache()->setAvailability(false, $delay); if ($e !== null) { throw $e; diff --git a/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php b/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php index d890081cbb6..bc388a0a32b 100644 --- a/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php +++ b/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php @@ -155,4 +155,30 @@ class AvailabilityTest extends \Test\TestCase { $this->wrapper->mkdir('foobar'); } + + public function testUnavailableMultiple(): void { + $this->storage->expects($this->once()) + ->method('getAvailability') + ->willReturn(['available' => true, 'last_checked' => 0]); + $this->storage->expects($this->never()) + ->method('test'); + $this->storage + ->expects($this->once()) // load-bearing `once` + ->method('mkdir') + ->willThrowException(new StorageNotAvailableException()); + + try { + $this->wrapper->mkdir('foobar'); + $this->fail(); + } catch (StorageNotAvailableException) { + } + + $this->storage->expects($this->never())->method('file_exists'); + + try { + $this->wrapper->mkdir('foobar'); + $this->fail(); + } catch (StorageNotAvailableException) { + } + } } From dbf084054e7165a3b9ad7841f256ad611473365d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 6 Nov 2025 20:09:17 +0100 Subject: [PATCH 2/2] chore: better type hints for `getAvailability` Signed-off-by: Robin Appelman --- lib/private/Files/Cache/Storage.php | 2 +- lib/private/Files/Storage/Common.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php index 1a3bda58e6a..a2fdfc76cc7 100644 --- a/lib/private/Files/Cache/Storage.php +++ b/lib/private/Files/Cache/Storage.php @@ -126,7 +126,7 @@ class Storage { } /** - * @return array [ available, last_checked ] + * @return array{available: bool, last_checked: int} */ public function getAvailability() { if ($row = self::getStorageById($this->storageId)) { diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 2dc359169d7..9532582a42a 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -711,7 +711,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage, } /** - * @return array [ available, last_checked ] + * @return array{available: bool, last_checked: int} */ public function getAvailability(): array { return $this->getStorageCache()->getAvailability();