From 911ea94c4e4a902d456b23d2b254a40bed66f283 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Jun 2026 04:07:45 +1200 Subject: [PATCH 1/3] feat(database): use the cache lease in getDocument to fix read-after-write staleness getDocument() re-populates the cache after a miss. Under concurrency a reader that fetched a row just before a concurrent updateDocument purge could write that now-stale row back into the cache *after* the purge ran, so later reads served stale data until the next write. Capture the cache generation before the adapter read and persist the result via saveWithLease(): the write only lands if no purge advanced the generation in the meantime, otherwise it is rejected and the next read re-fetches. forUpdate reads still bypass the cache entirely. Requires utopia-php/cache's Leasable capability (getGeneration/saveWithLease, utopia-php/cache#75); non-leasable cache adapters fall back to an unconditional save, so behaviour is unchanged for them. NOTE: composer is temporarily pinned to the cache feature branch so CI can resolve the new methods; revert to the released ^3.1 once cache#75 ships. Co-Authored-By: Claude Opus 4.8 --- composer.json | 8 ++++++- composer.lock | 46 +++++++++++++++++++++++++++++---------- src/Database/Database.php | 8 ++++++- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 24226eb97..0f32398a6 100755 --- a/composer.json +++ b/composer.json @@ -32,6 +32,12 @@ "check": "./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 2G", "coverage": "./vendor/bin/coverage-check ./tmp/clover.xml 90" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/utopia-php/cache" + } + ], "require": { "php": ">=8.4", "ext-pdo": "*", @@ -40,7 +46,7 @@ "ext-redis": "*", "utopia-php/validators": "0.2.*", "utopia-php/console": "0.1.*", - "utopia-php/cache": "^3.0", + "utopia-php/cache": "dev-feat/leasable-cache as 3.1.0", "utopia-php/pools": "1.*", "utopia-php/mongo": "1.*" }, diff --git a/composer.lock b/composer.lock index 67a8878a0..450eb8e52 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f5fb1745555c5cd00d14474561bf0095", + "content-hash": "fc51d9ecd53cedaae713460b39785509", "packages": [ { "name": "brick/math", @@ -2034,16 +2034,16 @@ }, { "name": "utopia-php/cache", - "version": "3.0.0", + "version": "dev-feat/leasable-cache", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176" + "reference": "b30259d5d74b4dbc9721337b8d5db6b7c57d09cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176", - "reference": "ece1f4d11ec2804cd7e05b9717dc7a2bc66e4176", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/b30259d5d74b4dbc9721337b8d5db6b7c57d09cd", + "reference": "b30259d5d74b4dbc9721337b8d5db6b7c57d09cd", "shasum": "" }, "require": { @@ -2068,7 +2068,22 @@ "Utopia\\Cache\\": "src/Cache" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "Utopia\\Tests\\": "tests/Cache" + } + }, + "scripts": { + "check": [ + "./vendor/bin/phpstan analyse --level max src tests" + ], + "lint": [ + "./vendor/bin/pint --test" + ], + "format": [ + "./vendor/bin/pint" + ] + }, "license": [ "MIT" ], @@ -2081,10 +2096,10 @@ "utopia" ], "support": { - "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/3.0.0" + "source": "https://github.com/utopia-php/cache/tree/feat/leasable-cache", + "issues": "https://github.com/utopia-php/cache/issues" }, - "time": "2026-05-14T14:13:17+00:00" + "time": "2026-06-22T16:01:56+00:00" }, { "name": "utopia-php/circuit-breaker", @@ -4651,9 +4666,18 @@ "time": "2026-02-10T04:21:53+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "utopia-php/cache", + "version": "dev-feat/leasable-cache", + "alias": "3.1.0", + "alias_normalized": "3.1.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "utopia-php/cache": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Database/Database.php b/src/Database/Database.php index 5a6cd97d7..366b5aeb5 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -4879,6 +4879,12 @@ public function getDocument(string $collection, string $id, array $queries = [], return $document; } + // Capture the cache generation BEFORE reading the row. If a concurrent + // updateDocument purges (and so advances the generation) while we read, + // saveWithLease() below rejects this now-stale value instead of + // re-poisoning the cache. See Cache\Feature\Leasable. + $generation = $forUpdate ? '0' : $this->cache->getGeneration($documentKey); + $document = $this->adapter->getDocument( $collection, $id, @@ -4931,7 +4937,7 @@ public function getDocument(string $collection, string $id, array $queries = [], // caching the pre-commit row would poison the cache for other readers. if (!$forUpdate && empty($relationships)) { try { - $this->cache->save($documentKey, $document->getArrayCopy(), $hashKey); + $this->cache->saveWithLease($documentKey, $document->getArrayCopy(), $hashKey, $generation); $this->cache->save($collectionKey, 'empty', $documentKey); } catch (Exception $e) { Console::warning('Failed to save document to cache: ' . $e->getMessage()); From 05ddac7aadb19263866155e4548c300de66dc836 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Jun 2026 16:32:28 +1200 Subject: [PATCH 2/3] fix(database): tolerate cache failure in getGeneration; gate registry save on lease success Addresses review + the failing cache-fallback/transaction adapter tests on #904: - getGeneration() is now wrapped in try/catch in getDocument() and degrades to '0' (no lease) on a cache error, mirroring the load() handling above it. The cache-skip-on-failure adapter tests bring Redis down and expect getDocument() to fall back to the DB; the unguarded getGeneration() was letting a RedisException escape and break them. - Only register the document in the collection invalidation index when saveWithLease() actually cached it (truthy return). A lease rejection (concurrent purge) caches nothing, so skipping the registry write avoids a phantom entry. Co-Authored-By: Claude Opus 4.8 --- src/Database/Database.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 366b5aeb5..6811100c6 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -4882,8 +4882,17 @@ public function getDocument(string $collection, string $id, array $queries = [], // Capture the cache generation BEFORE reading the row. If a concurrent // updateDocument purges (and so advances the generation) while we read, // saveWithLease() below rejects this now-stale value instead of - // re-poisoning the cache. See Cache\Feature\Leasable. - $generation = $forUpdate ? '0' : $this->cache->getGeneration($documentKey); + // re-poisoning the cache. See Cache\Feature\Leasable. A cache failure + // must not break the read, so degrade to '0' (no lease), mirroring the + // load() handling above. + $generation = '0'; + if (!$forUpdate) { + try { + $generation = $this->cache->getGeneration($documentKey); + } catch (Exception $e) { + Console::warning('Warning: Failed to get cache generation: ' . $e->getMessage()); + } + } $document = $this->adapter->getDocument( $collection, @@ -4937,8 +4946,13 @@ public function getDocument(string $collection, string $id, array $queries = [], // caching the pre-commit row would poison the cache for other readers. if (!$forUpdate && empty($relationships)) { try { - $this->cache->saveWithLease($documentKey, $document->getArrayCopy(), $hashKey, $generation); - $this->cache->save($collectionKey, 'empty', $documentKey); + // Only register the document in the collection's invalidation + // index when the value was actually cached. A lease rejection + // (concurrent purge) returns false and caches nothing, so adding + // the key here would leave a phantom entry. + if ($this->cache->saveWithLease($documentKey, $document->getArrayCopy(), $hashKey, $generation) !== false) { + $this->cache->save($collectionKey, 'empty', $documentKey); + } } catch (Exception $e) { Console::warning('Failed to save document to cache: ' . $e->getMessage()); } From 0f697a7d64642c37d0e5c264b950c20f34b055dd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 23 Jun 2026 23:17:55 +1200 Subject: [PATCH 3/3] chore(deps): use released cache 3.1.0 and tighten lease comments The lease primitive shipped in utopia-php/cache 3.1.0, so drop the temporary VCS branch pin (and its repositories entry) and require the released 3.1.* line. Compact the getDocument lease comments to the load-bearing why. Co-Authored-By: Claude Opus 4.8 --- composer.json | 8 +- composer.lock | 171 ++++++++++++++++---------------------- src/Database/Database.php | 13 +-- 3 files changed, 76 insertions(+), 116 deletions(-) diff --git a/composer.json b/composer.json index 0f32398a6..cc86f1964 100755 --- a/composer.json +++ b/composer.json @@ -32,12 +32,6 @@ "check": "./vendor/bin/phpstan analyse --level 7 src tests --memory-limit 2G", "coverage": "./vendor/bin/coverage-check ./tmp/clover.xml 90" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/utopia-php/cache" - } - ], "require": { "php": ">=8.4", "ext-pdo": "*", @@ -46,7 +40,7 @@ "ext-redis": "*", "utopia-php/validators": "0.2.*", "utopia-php/console": "0.1.*", - "utopia-php/cache": "dev-feat/leasable-cache as 3.1.0", + "utopia-php/cache": "3.1.*", "utopia-php/pools": "1.*", "utopia-php/mongo": "1.*" }, diff --git a/composer.lock b/composer.lock index 450eb8e52..313e1ff98 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,26 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fc51d9ecd53cedaae713460b39785509", + "content-hash": "f9c4668228d81541dc573fdf229dcbab", "packages": [ { "name": "brick/math", - "version": "0.14.8", + "version": "0.18.0", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" + "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", - "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", + "url": "https://api.github.com/repos/brick/math/zipball/82944324d1c1bdb2c2618e89978d4e2ad78d69ad", + "reference": "82944324d1c1bdb2c2618e89978d4e2ad78d69ad", "shasum": "" }, "require": { "php": "^8.2" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.2", "phpstan/phpstan": "2.1.22", "phpunit/phpunit": "^11.5" }, @@ -56,7 +55,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.8" + "source": "https://github.com/brick/math/tree/0.18.0" }, "funding": [ { @@ -64,7 +63,7 @@ "type": "github" } ], - "time": "2026-02-10T14:33:43+00:00" + "time": "2026-06-14T18:21:03+00:00" }, { "name": "composer/semver", @@ -1238,20 +1237,20 @@ }, { "name": "ramsey/uuid", - "version": "4.9.2", + "version": "4.9.3", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8429c78ca35a09f27565311b98101e2826affde0" + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", - "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/1df15849d00943a67d677dc9cfd80795f038c9f8", + "reference": "1df15849d00943a67d677dc9cfd80795f038c9f8", "shasum": "" }, "require": { - "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "brick/math": ">=0.8.16 <=0.18", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -1310,9 +1309,9 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.9.2" + "source": "https://github.com/ramsey/uuid/tree/4.9.3" }, - "time": "2025-12-14T04:43:48+00:00" + "time": "2026-06-18T03:57:49+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1387,16 +1386,16 @@ }, { "name": "symfony/http-client", - "version": "v7.4.9", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6" + "reference": "e8a112b8415707265a7e614278136a9d92989a6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6", - "reference": "7e941c6abf4e3bf7dca160bf0e11ef36a9f832f6", + "url": "https://api.github.com/repos/symfony/http-client/zipball/e8a112b8415707265a7e614278136a9d92989a6a", + "reference": "e8a112b8415707265a7e614278136a9d92989a6a", "shasum": "" }, "require": { @@ -1464,7 +1463,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.4.9" + "source": "https://github.com/symfony/http-client/tree/v7.4.13" }, "funding": [ { @@ -1484,7 +1483,7 @@ "type": "tidelift" } ], - "time": "2026-04-29T13:25:15+00:00" + "time": "2026-05-24T09:57:54+00:00" }, { "name": "symfony/http-client-contracts", @@ -1570,16 +1569,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.37.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -1631,7 +1630,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -1651,20 +1650,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { "name": "symfony/polyfill-php82", - "version": "v1.37.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php82.git", - "reference": "34808efe3e68f69685796f7c253a2f1d8ea9df59" + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/34808efe3e68f69685796f7c253a2f1d8ea9df59", - "reference": "34808efe3e68f69685796f7c253a2f1d8ea9df59", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", "shasum": "" }, "require": { @@ -1711,7 +1710,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php82/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php82/tree/v1.38.1" }, "funding": [ { @@ -1731,20 +1730,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.37.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/796a26abb75ce49f3a84433cd81bf1009d73d5f8", + "reference": "796a26abb75ce49f3a84433cd81bf1009d73d5f8", "shasum": "" }, "require": { @@ -1791,7 +1790,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.38.2" }, "funding": [ { @@ -1811,7 +1810,7 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2026-05-27T06:51:48+00:00" }, { "name": "symfony/polyfill-php85", @@ -2034,16 +2033,16 @@ }, { "name": "utopia-php/cache", - "version": "dev-feat/leasable-cache", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "b30259d5d74b4dbc9721337b8d5db6b7c57d09cd" + "reference": "d5048409cf3a8db668b00cd2e6c46a627b16d76a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/b30259d5d74b4dbc9721337b8d5db6b7c57d09cd", - "reference": "b30259d5d74b4dbc9721337b8d5db6b7c57d09cd", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/d5048409cf3a8db668b00cd2e6c46a627b16d76a", + "reference": "d5048409cf3a8db668b00cd2e6c46a627b16d76a", "shasum": "" }, "require": { @@ -2068,22 +2067,7 @@ "Utopia\\Cache\\": "src/Cache" } }, - "autoload-dev": { - "psr-4": { - "Utopia\\Tests\\": "tests/Cache" - } - }, - "scripts": { - "check": [ - "./vendor/bin/phpstan analyse --level max src tests" - ], - "lint": [ - "./vendor/bin/pint --test" - ], - "format": [ - "./vendor/bin/pint" - ] - }, + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2096,23 +2080,23 @@ "utopia" ], "support": { - "source": "https://github.com/utopia-php/cache/tree/feat/leasable-cache", - "issues": "https://github.com/utopia-php/cache/issues" + "issues": "https://github.com/utopia-php/cache/issues", + "source": "https://github.com/utopia-php/cache/tree/3.1.0" }, - "time": "2026-06-22T16:01:56+00:00" + "time": "2026-06-23T10:59:08+00:00" }, { "name": "utopia-php/circuit-breaker", - "version": "0.3.0", + "version": "0.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/circuit-breaker.git", - "reference": "064243c1667778c00abf027ff53a735a228776de" + "reference": "db5d77f6c99ebce2ee81bd8ed4ae8f41bd2b0828" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/circuit-breaker/zipball/064243c1667778c00abf027ff53a735a228776de", - "reference": "064243c1667778c00abf027ff53a735a228776de", + "url": "https://api.github.com/repos/utopia-php/circuit-breaker/zipball/db5d77f6c99ebce2ee81bd8ed4ae8f41bd2b0828", + "reference": "db5d77f6c99ebce2ee81bd8ed4ae8f41bd2b0828", "shasum": "" }, "require": { @@ -2122,7 +2106,7 @@ "laravel/pint": "^1.29", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^10.0", - "utopia-php/telemetry": "0.2.*" + "utopia-php/telemetry": "^0.4" }, "suggest": { "ext-opentelemetry": "Required by utopia-php/telemetry when using OpenTelemetry metrics.", @@ -2159,9 +2143,9 @@ ], "support": { "issues": "https://github.com/utopia-php/circuit-breaker/issues", - "source": "https://github.com/utopia-php/circuit-breaker/tree/0.3.0" + "source": "https://github.com/utopia-php/circuit-breaker/tree/0.3.1" }, - "time": "2026-05-12T04:27:08+00:00" + "time": "2026-05-29T12:12:23+00:00" }, { "name": "utopia-php/console", @@ -2274,28 +2258,28 @@ }, { "name": "utopia-php/pools", - "version": "1.0.3", + "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/utopia-php/pools.git", - "reference": "74de7c5457a2c447f27e7ec4d72e8412a7d68c10" + "reference": "b685ca01883ed820b9898b85163a8f3d970a2da7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/pools/zipball/74de7c5457a2c447f27e7ec4d72e8412a7d68c10", - "reference": "74de7c5457a2c447f27e7ec4d72e8412a7d68c10", + "url": "https://api.github.com/repos/utopia-php/pools/zipball/b685ca01883ed820b9898b85163a8f3d970a2da7", + "reference": "b685ca01883ed820b9898b85163a8f3d970a2da7", "shasum": "" }, "require": { "php": ">=8.4", - "utopia-php/telemetry": "*" + "utopia-php/telemetry": "^0.4" }, "require-dev": { - "laravel/pint": "1.*", - "phpstan/phpstan": "1.*", - "phpunit/phpunit": "11.*", "swoole/ide-helper": "6.*" }, + "suggest": { + "ext-swoole": "Required to use the Swoole pool adapter" + }, "type": "library", "autoload": { "psr-4": { @@ -2321,22 +2305,22 @@ ], "support": { "issues": "https://github.com/utopia-php/pools/issues", - "source": "https://github.com/utopia-php/pools/tree/1.0.3" + "source": "https://github.com/utopia-php/pools/tree/1.0.8" }, - "time": "2026-02-26T08:42:40+00:00" + "time": "2026-06-20T09:45:06+00:00" }, { "name": "utopia-php/telemetry", - "version": "0.3.0", + "version": "0.4.3", "source": { "type": "git", "url": "https://github.com/utopia-php/telemetry.git", - "reference": "62bbadad03e593b071b8ca63fac2c117c1900991" + "reference": "50491fb1686a9796e9cd5005ede6141e2ceeb5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/62bbadad03e593b071b8ca63fac2c117c1900991", - "reference": "62bbadad03e593b071b8ca63fac2c117c1900991", + "url": "https://api.github.com/repos/utopia-php/telemetry/zipball/50491fb1686a9796e9cd5005ede6141e2ceeb5ac", + "reference": "50491fb1686a9796e9cd5005ede6141e2ceeb5ac", "shasum": "" }, "require": { @@ -2349,10 +2333,7 @@ "symfony/http-client": "7.*" }, "require-dev": { - "laravel/pint": "1.*", "phpbench/phpbench": "1.*", - "phpstan/phpstan": "2.*", - "phpunit/phpunit": "11.*", "swoole/ide-helper": "6.*" }, "suggest": { @@ -2369,6 +2350,7 @@ "license": [ "MIT" ], + "description": "A lite & fast telemetry library, with adapters for OpenTelemetry", "keywords": [ "framework", "php", @@ -2376,9 +2358,9 @@ ], "support": { "issues": "https://github.com/utopia-php/telemetry/issues", - "source": "https://github.com/utopia-php/telemetry/tree/0.3.0" + "source": "https://github.com/utopia-php/telemetry/tree/0.4.3" }, - "time": "2026-04-01T13:52:56+00:00" + "time": "2026-06-20T09:45:06+00:00" }, { "name": "utopia-php/validators", @@ -4666,18 +4648,9 @@ "time": "2026-02-10T04:21:53+00:00" } ], - "aliases": [ - { - "package": "utopia-php/cache", - "version": "dev-feat/leasable-cache", - "alias": "3.1.0", - "alias_normalized": "3.1.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "utopia-php/cache": 20 - }, + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Database/Database.php b/src/Database/Database.php index 6811100c6..4caac03ff 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -4879,12 +4879,8 @@ public function getDocument(string $collection, string $id, array $queries = [], return $document; } - // Capture the cache generation BEFORE reading the row. If a concurrent - // updateDocument purges (and so advances the generation) while we read, - // saveWithLease() below rejects this now-stale value instead of - // re-poisoning the cache. See Cache\Feature\Leasable. A cache failure - // must not break the read, so degrade to '0' (no lease), mirroring the - // load() handling above. + // Capture the generation before reading: if a concurrent purge advances + // it, saveWithLease() below rejects this now-stale value. '0' means no lease. $generation = '0'; if (!$forUpdate) { try { @@ -4946,10 +4942,7 @@ public function getDocument(string $collection, string $id, array $queries = [], // caching the pre-commit row would poison the cache for other readers. if (!$forUpdate && empty($relationships)) { try { - // Only register the document in the collection's invalidation - // index when the value was actually cached. A lease rejection - // (concurrent purge) returns false and caches nothing, so adding - // the key here would leave a phantom entry. + // Index for invalidation only when the value was actually cached. if ($this->cache->saveWithLease($documentKey, $document->getArrayCopy(), $hashKey, $generation) !== false) { $this->cache->save($collectionKey, 'empty', $documentKey); }