diff --git a/app/init/constants.php b/app/init/constants.php index 90f06952fb..71d2572eed 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -383,3 +383,7 @@ const SCHEDULE_RESOURCE_TYPE_MESSAGE = 'message'; /** Preview cookie */ const COOKIE_NAME_PREVIEW = 'a_jwt_console'; + +// Cache Reconnect +const CACHE_RECONNECT_MAX_ATTEMPTS = 2; +const CACHE_RECONNECT_RETRY_DELAY = 1000; diff --git a/app/init/registers.php b/app/init/registers.php index 8c596aae8e..83a4758426 100644 --- a/app/init/registers.php +++ b/app/init/registers.php @@ -313,10 +313,14 @@ $register->set('pools', function () { default => null }; case 'cache': - return match ($dsn->getScheme()) { + $adapter = match ($dsn->getScheme()) { 'redis' => new RedisCache($resource()), default => null }; + + $adapter->setMaxRetries(CACHE_RECONNECT_MAX_ATTEMPTS); + $adapter->setRetryDelay(CACHE_RECONNECT_RETRY_DELAY); + return $adapter; default: throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation."); } diff --git a/composer.json b/composer.json index 6744544e78..73d164fdc9 100644 --- a/composer.json +++ b/composer.json @@ -45,16 +45,16 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.19.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "1.*", + "utopia-php/abuse": "1.2.*", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "2.*", + "utopia-php/audit": "2.2.*", "utopia-php/auth": "0.5.*", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "1.0.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "1.*", - "utopia-php/database": "dev-joins8 as 4.6.2", + "utopia-php/database": "dev-joins8 as 5.0.0", "utopia-php/detector": "0.2.*", - "utopia-php/domains": "0.11.*", + "utopia-php/domains": "1.*", "utopia-php/emails": "0.6.*", "utopia-php/dns": "1.5.*", "utopia-php/dsn": "0.2.1", @@ -64,7 +64,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "dev-joins as 1.4.4", + "utopia-php/migration": "dev-joins as 1.5.0", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", @@ -75,7 +75,7 @@ "utopia-php/swoole": "1.*", "utopia-php/system": "0.9.*", "utopia-php/telemetry": "0.1.*", - "utopia-php/vcs": "0.13.*", + "utopia-php/vcs": "1.*", "utopia-php/websocket": "0.3.*", "matomo/device-detector": "6.4.*", "dragonmantank/cron-expression": "3.4.*", diff --git a/composer.lock b/composer.lock index aff29676e0..6de0cfd920 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": "2aca1c8eeaa9fa338e389e3527cb6bd6", + "content-hash": "e2308896fe86b2bd967390d250b95bae", "packages": [ { "name": "adhocore/jwt", @@ -3517,16 +3517,16 @@ }, { "name": "utopia-php/abuse", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "15cd5dbefa4453e8a2d90649a7078e242966ac3f" + "reference": "20bee84fd14dbe81d50ecabf1ffd81cceca06152" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/15cd5dbefa4453e8a2d90649a7078e242966ac3f", - "reference": "15cd5dbefa4453e8a2d90649a7078e242966ac3f", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/20bee84fd14dbe81d50ecabf1ffd81cceca06152", + "reference": "20bee84fd14dbe81d50ecabf1ffd81cceca06152", "shasum": "" }, "require": { @@ -3535,7 +3535,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "4.*" + "utopia-php/database": "5.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3563,9 +3563,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/1.2.1" + "source": "https://github.com/utopia-php/abuse/tree/1.2.2" }, - "time": "2026-01-15T02:09:49+00:00" + "time": "2026-02-02T10:43:10+00:00" }, { "name": "utopia-php/analytics", @@ -3615,21 +3615,21 @@ }, { "name": "utopia-php/audit", - "version": "2.1.1", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "00b1d5c0fe7f912315598ee83278fe74d234b196" + "reference": "e3e2d6ad5c7f6377d9237df296a12eb7943892fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/00b1d5c0fe7f912315598ee83278fe74d234b196", - "reference": "00b1d5c0fe7f912315598ee83278fe74d234b196", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/e3e2d6ad5c7f6377d9237df296a12eb7943892fd", + "reference": "e3e2d6ad5c7f6377d9237df296a12eb7943892fd", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "4.*", + "utopia-php/database": "5.*", "utopia-php/fetch": "0.5.*", "utopia-php/validators": "0.2.*" }, @@ -3658,9 +3658,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/2.1.1" + "source": "https://github.com/utopia-php/audit/tree/2.2.1" }, - "time": "2026-02-01T08:12:53+00:00" + "time": "2026-02-02T10:39:25+00:00" }, { "name": "utopia-php/auth", @@ -3719,16 +3719,16 @@ }, { "name": "utopia-php/cache", - "version": "0.13.3", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cache.git", - "reference": "355707ab2c0090435059216165db86976b68a126" + "reference": "7068870c086a6aea16173563a26b93ef3e408439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cache/zipball/355707ab2c0090435059216165db86976b68a126", - "reference": "355707ab2c0090435059216165db86976b68a126", + "url": "https://api.github.com/repos/utopia-php/cache/zipball/7068870c086a6aea16173563a26b93ef3e408439", + "reference": "7068870c086a6aea16173563a26b93ef3e408439", "shasum": "" }, "require": { @@ -3765,9 +3765,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cache/issues", - "source": "https://github.com/utopia-php/cache/tree/0.13.3" + "source": "https://github.com/utopia-php/cache/tree/1.0.0" }, - "time": "2026-01-16T07:54:34+00:00" + "time": "2026-01-28T10:55:44+00:00" }, { "name": "utopia-php/cli", @@ -3961,27 +3961,27 @@ }, { "name": "utopia-php/database", - "version": "4.6.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "4dfffd4d528f89b3b3fc09180d4c965ef9bdae30" + "reference": "221794bd7de027f9177cd325209e8162ca2520cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/4dfffd4d528f89b3b3fc09180d4c965ef9bdae30", - "reference": "4dfffd4d528f89b3b3fc09180d4c965ef9bdae30", + "url": "https://api.github.com/repos/utopia-php/database/zipball/221794bd7de027f9177cd325209e8162ca2520cb", + "reference": "221794bd7de027f9177cd325209e8162ca2520cb", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-mongodb": "*", "ext-pdo": "*", - "php": ">=8.1", - "utopia-php/cache": "0.13.*", + "php": ">=8.4", + "utopia-php/cache": "1.0.*", "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.11.*", - "utopia-php/pools": "1.*" + "utopia-php/pools": "1.0.*" }, "require-dev": { "fakerphp/faker": "1.23.*", @@ -4013,9 +4013,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/4.6.4" + "source": "https://github.com/utopia-php/database/tree/5.0.0" }, - "time": "2026-01-30T08:19:14+00:00" + "time": "2026-01-30T06:17:53+00:00" }, { "name": "utopia-php/detector", @@ -4064,21 +4064,21 @@ }, { "name": "utopia-php/dns", - "version": "1.5.3", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "a1f490ba425b1a5128e7aaa24eff560900812d21" + "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/a1f490ba425b1a5128e7aaa24eff560900812d21", - "reference": "a1f490ba425b1a5128e7aaa24eff560900812d21", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/ee831a6f2ceb28babb042ea65539c26ea4530bf6", + "reference": "ee831a6f2ceb28babb042ea65539c26ea4530bf6", "shasum": "" }, "require": { "php": ">=8.3", - "utopia-php/domains": "0.11.*", + "utopia-php/domains": "1.0.*", "utopia-php/span": "1.0.*", "utopia-php/telemetry": "*", "utopia-php/validators": "0.*" @@ -4115,27 +4115,27 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.5.3" + "source": "https://github.com/utopia-php/dns/tree/1.5.4" }, - "time": "2026-01-13T11:39:38+00:00" + "time": "2026-02-02T10:40:38+00:00" }, { "name": "utopia-php/domains", - "version": "0.11.1", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/domains.git", - "reference": "63fc5b9b58a32a5efd426510bbab4199db24593b" + "reference": "ecac82392e83d4a8ede76c3c94258d868d82f709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/domains/zipball/63fc5b9b58a32a5efd426510bbab4199db24593b", - "reference": "63fc5b9b58a32a5efd426510bbab4199db24593b", + "url": "https://api.github.com/repos/utopia-php/domains/zipball/ecac82392e83d4a8ede76c3c94258d868d82f709", + "reference": "ecac82392e83d4a8ede76c3c94258d868d82f709", "shasum": "" }, "require": { "php": ">=8.2", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "1.0.*", "utopia-php/validators": "0.*" }, "require-dev": { @@ -4177,9 +4177,9 @@ ], "support": { "issues": "https://github.com/utopia-php/domains/issues", - "source": "https://github.com/utopia-php/domains/tree/0.11.1" + "source": "https://github.com/utopia-php/domains/tree/1.0.0" }, - "time": "2026-01-23T09:28:08+00:00" + "time": "2026-01-30T06:15:50+00:00" }, { "name": "utopia-php/dsn", @@ -4230,22 +4230,22 @@ }, { "name": "utopia-php/emails", - "version": "0.6.5", + "version": "0.6.6", "source": { "type": "git", "url": "https://github.com/utopia-php/emails.git", - "reference": "178e57a0f9a24139500c94ce73d166800197b6cf" + "reference": "354f7fe591e1fba7736afada558cb3b02ec03fea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/emails/zipball/178e57a0f9a24139500c94ce73d166800197b6cf", - "reference": "178e57a0f9a24139500c94ce73d166800197b6cf", + "url": "https://api.github.com/repos/utopia-php/emails/zipball/354f7fe591e1fba7736afada558cb3b02ec03fea", + "reference": "354f7fe591e1fba7736afada558cb3b02ec03fea", "shasum": "" }, "require": { "php": ">=8.0", "utopia-php/cli": "^0.15", - "utopia-php/domains": "^0.11", + "utopia-php/domains": "^1.0", "utopia-php/fetch": "^0.5", "utopia-php/validators": "0.*" }, @@ -4284,9 +4284,9 @@ ], "support": { "issues": "https://github.com/utopia-php/emails/issues", - "source": "https://github.com/utopia-php/emails/tree/0.6.5" + "source": "https://github.com/utopia-php/emails/tree/0.6.6" }, - "time": "2026-01-13T09:55:59+00:00" + "time": "2026-02-02T10:41:22+00:00" }, { "name": "utopia-php/fetch", @@ -4578,16 +4578,16 @@ }, { "name": "utopia-php/migration", - "version": "1.4.6", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "f358db6fb6a01d855bbed39e283387069e4f277d" + "reference": "0d6e77748ca7d9302a88953751bd89d577610afd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/f358db6fb6a01d855bbed39e283387069e4f277d", - "reference": "f358db6fb6a01d855bbed39e283387069e4f277d", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/0d6e77748ca7d9302a88953751bd89d577610afd", + "reference": "0d6e77748ca7d9302a88953751bd89d577610afd", "shasum": "" }, "require": { @@ -4597,7 +4597,7 @@ "halaxa/json-machine": "^1.2", "php": ">=8.1", "utopia-php/console": "0.0.*", - "utopia-php/database": "4.*", + "utopia-php/database": "5.*", "utopia-php/dsn": "0.2.*", "utopia-php/storage": "0.18.*" }, @@ -4628,9 +4628,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.4.6" + "source": "https://github.com/utopia-php/migration/tree/1.5.0" }, - "time": "2026-01-20T11:07:17+00:00" + "time": "2026-02-02T10:42:04+00:00" }, { "name": "utopia-php/mongo", @@ -4902,16 +4902,16 @@ }, { "name": "utopia-php/queue", - "version": "0.15.1", + "version": "0.15.2", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "e551606385990ec7901d222017c4cfc2749a518c" + "reference": "5051c08d9f50ff4aa78c0eb2f7ca74b328509c81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/e551606385990ec7901d222017c4cfc2749a518c", - "reference": "e551606385990ec7901d222017c4cfc2749a518c", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/5051c08d9f50ff4aa78c0eb2f7ca74b328509c81", + "reference": "5051c08d9f50ff4aa78c0eb2f7ca74b328509c81", "shasum": "" }, "require": { @@ -4962,9 +4962,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.15.1" + "source": "https://github.com/utopia-php/queue/tree/0.15.2" }, - "time": "2026-01-16T07:54:54+00:00" + "time": "2026-02-02T11:56:51+00:00" }, { "name": "utopia-php/registry", @@ -5323,22 +5323,22 @@ }, { "name": "utopia-php/vcs", - "version": "0.13.0", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/vcs.git", - "reference": "c59e21db5ca42014fe2071fec3c2f814efcc86dd" + "reference": "3a427529dae09c0794541adac19062603d441e28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/vcs/zipball/c59e21db5ca42014fe2071fec3c2f814efcc86dd", - "reference": "c59e21db5ca42014fe2071fec3c2f814efcc86dd", + "url": "https://api.github.com/repos/utopia-php/vcs/zipball/3a427529dae09c0794541adac19062603d441e28", + "reference": "3a427529dae09c0794541adac19062603d441e28", "shasum": "" }, "require": { "adhocore/jwt": "^1.1", "php": ">=8.0", - "utopia-php/cache": "0.13.*", + "utopia-php/cache": "1.0.*", "utopia-php/framework": "0.*.*", "utopia-php/system": "0.9.*" }, @@ -5366,9 +5366,9 @@ ], "support": { "issues": "https://github.com/utopia-php/vcs/issues", - "source": "https://github.com/utopia-php/vcs/tree/0.13.0" + "source": "https://github.com/utopia-php/vcs/tree/1.0.0" }, - "time": "2025-11-28T08:42:31+00:00" + "time": "2026-01-30T06:18:24+00:00" }, { "name": "utopia-php/websocket", diff --git a/tests/e2e/Services/Health/CacheTest.php b/tests/e2e/Services/Health/CacheTest.php index 0b825b2dba..735446b836 100644 --- a/tests/e2e/Services/Health/CacheTest.php +++ b/tests/e2e/Services/Health/CacheTest.php @@ -2,6 +2,10 @@ namespace Tests\E2E\Services\Health; +use Redis; +use Utopia\Cache\Adapter\Redis as RedisAdapter; +use Utopia\Cache\Cache; + class CacheTest extends HealthBase { public function testCacheSuccess(): void @@ -13,4 +17,69 @@ class CacheTest extends HealthBase $this->assertLessThan(100, $response['body']['statuses'][0]['ping']); $this->assertEquals('pass', $response['body']['statuses'][0]['status']); } + + public function testCacheReconnect(): void + { + $redis = new Redis(); + $redis->connect('redis', 6379); + $cache = new Cache((new RedisAdapter($redis))->setMaxRetries(CACHE_RECONNECT_MAX_ATTEMPTS)->setRetryDelay(CACHE_RECONNECT_RETRY_DELAY)); + + $cache->save('test:reconnect', 'reconnect', 'test:reconnect'); + + $stopCmd = 'docker ps -a --filter "name=appwrite-redis" --format "{{.Names}}" | xargs -r docker stop'; + exec($stopCmd . ' 2>&1', $output, $exitCode); + $this->assertEquals(0, $exitCode, "Docker stop failed: $stopCmd\nOutput: " . implode("\n", $output)); + sleep(1); + + try { + try { + $cache->load('test:reconnect', 5); + $this->fail('Redis connection should have failed'); + } catch (\RedisException $e) { + } + } finally { + $output = []; + $startCmd = 'docker ps -a --filter "name=appwrite-redis" --format "{{.Names}}" | xargs -r docker start'; + exec($startCmd . ' 2>&1', $output, $exitCode); + $this->assertEquals(0, $exitCode, "Docker start failed: $startCmd\nOutput: " . implode("\n", $output)); + sleep(3); + } + + $this->assertEquals('reconnect', $cache->save('test:reconnect', 'reconnect', 'test:reconnect')); + $this->assertEquals('reconnect', $cache->load('test:reconnect', 5)); + } + + /** + * @depends testCacheReconnect + */ + public function testCacheReconnectPersistent(): void + { + $redis = new Redis(); + $redis->pconnect('redis', 6379); + $cache = new Cache((new RedisAdapter($redis))->setMaxRetries(CACHE_RECONNECT_MAX_ATTEMPTS)->setRetryDelay(CACHE_RECONNECT_RETRY_DELAY)); + + $cache->save('test:reconnect_persistent', 'reconnect_persistent', 'test:reconnect_persistent'); + + $stopCmd = 'docker ps -a --filter "name=appwrite-redis" --format "{{.Names}}" | xargs -r docker stop'; + exec($stopCmd . ' 2>&1', $output, $exitCode); + $this->assertEquals(0, $exitCode, "Docker stop failed: $stopCmd\nOutput: " . implode("\n", $output)); + sleep(1); + + try { + try { + $cache->load('test:reconnect_persistent', 5); + $this->fail('Redis connection should have failed'); + } catch (\RedisException $e) { + } + } finally { + $output = []; + $startCmd = 'docker ps -a --filter "name=appwrite-redis" --format "{{.Names}}" | xargs -r docker start'; + exec($startCmd . ' 2>&1', $output, $exitCode); + $this->assertEquals(0, $exitCode, "Docker start failed: $startCmd\nOutput: " . implode("\n", $output)); + sleep(3); + } + + $this->assertEquals('reconnect_persistent', $cache->save('test:reconnect_persistent', 'reconnect_persistent', 'test:reconnect_persistent')); + $this->assertEquals('reconnect_persistent', $cache->load('test:reconnect_persistent', 5)); + } }