From 8a205d1b6381ae0c2fdabe68ae35ee5d8a93eebc Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 19 Jul 2021 15:39:31 -0400 Subject: [PATCH 01/10] Retry initial db connection once before throwing exception --- app/http.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/http.php b/app/http.php index b148e52d1e..cac1819b2c 100644 --- a/app/http.php +++ b/app/http.php @@ -72,7 +72,13 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { // wait for database to be ready sleep(5); - $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ + try { + $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ + } catch(\Exception $e) { + Console::warning('Database not ready. Retrying connection...'); + sleep(5); + $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ + } if(!$dbForConsole->exists()) { Console::success('[Setup] - Server database init started...'); From 9870c6fdff76983b9eb957b007545b5a8714ce77 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 20 Jul 2021 13:01:47 -0400 Subject: [PATCH 02/10] Attempt to reconnect five times before throwing exception --- app/http.php | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/app/http.php b/app/http.php index cac1819b2c..9b42ccff9f 100644 --- a/app/http.php +++ b/app/http.php @@ -70,15 +70,20 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { }); // wait for database to be ready - sleep(5); - - try { - $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ - } catch(\Exception $e) { - Console::warning('Database not ready. Retrying connection...'); - sleep(5); - $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ - } + $attempts = 0; + do { + try { + $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ + break; // leave the do-while if successful + } catch(\Exception $e) { + $attempts++; + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= 5) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep(5); + } + } while (!$dbForConsole || $attempts < 5); if(!$dbForConsole->exists()) { Console::success('[Setup] - Server database init started...'); From e16bfe7fd3fd25c17fb96ef7b541dfb9d0cb0e52 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 20 Jul 2021 14:59:58 -0400 Subject: [PATCH 03/10] Move reconnection loop to first call to db --- app/http.php | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/app/http.php b/app/http.php index 9b42ccff9f..b85d6a051b 100644 --- a/app/http.php +++ b/app/http.php @@ -54,8 +54,23 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { $app = new App('UTC'); go(function() use ($register, $app) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); + // wait for database to be ready + $attempts = 0; + do { + try { + $attempts++; + $db = $register->get('dbPool')->get(); + $redis = $register->get('redisPool')->get(); + break; // leave the do-while if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= 10) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep(1); + continue; + } + } while ($attempts < 10); App::setResource('db', function () use (&$db) { return $db; @@ -69,21 +84,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { return $app; }); - // wait for database to be ready - $attempts = 0; - do { - try { - $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ - break; // leave the do-while if successful - } catch(\Exception $e) { - $attempts++; - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= 5) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep(5); - } - } while (!$dbForConsole || $attempts < 5); + $dbForConsole = $app->getResource('dbForConsole'); /** @var Utopia\Database\Database $dbForConsole */ if(!$dbForConsole->exists()) { Console::success('[Setup] - Server database init started...'); From 12e2ebc2b5a899b404c11ae638cfea3351d9888e Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 20 Jul 2021 15:11:54 -0400 Subject: [PATCH 04/10] Define loop control vars outside loop --- app/http.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/http.php b/app/http.php index b85d6a051b..74bbd1b0c2 100644 --- a/app/http.php +++ b/app/http.php @@ -56,6 +56,9 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { go(function() use ($register, $app) { // wait for database to be ready $attempts = 0; + $max = 10; + $sleep = 1; + do { try { $attempts++; @@ -64,13 +67,13 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { break; // leave the do-while if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= 10) { + if ($attempts >= $max) { throw new \Exception('Failed to connect to database: '. $e->getMessage()); } - sleep(1); + sleep($sleep); continue; } - } while ($attempts < 10); + } while ($attempts < $max); App::setResource('db', function () use (&$db) { return $db; From 1b051d32feefdee034fa5e6a3bbb8bc062f665de Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 20 Jul 2021 15:13:00 -0400 Subject: [PATCH 05/10] Attempt to reconnect deletes worker to db --- app/workers/deletes.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 1242d7a8ab..751c7db054 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -389,9 +389,27 @@ class DeletesV1 extends Worker { global $register; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); - $dbForConsole->setNamespace('project_console_internal'); // Main DB + // wait for database to be ready + $attempts = 0; + $max = 5; + $sleep = 5; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); + $dbForConsole->setNamespace('project_console_internal'); // Main DB + break; // leave the do-while if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + continue; + } + } while ($attempts < $max); return $dbForConsole; } From 4242ba75f5a212bc80d4b85e6b1410801e155b61 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Wed, 21 Jul 2021 15:14:18 -0400 Subject: [PATCH 06/10] Remove unneeded continue statements --- app/http.php | 1 - app/workers/deletes.php | 1 - 2 files changed, 2 deletions(-) diff --git a/app/http.php b/app/http.php index 74bbd1b0c2..55adbb2359 100644 --- a/app/http.php +++ b/app/http.php @@ -71,7 +71,6 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) { throw new \Exception('Failed to connect to database: '. $e->getMessage()); } sleep($sleep); - continue; } } while ($attempts < $max); diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 751c7db054..9c69b57927 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -407,7 +407,6 @@ class DeletesV1 extends Worker throw new \Exception('Failed to connect to database: '. $e->getMessage()); } sleep($sleep); - continue; } } while ($attempts < $max); From 03288ffbfac4a09d8365af724955002049bde46d Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Sun, 25 Jul 2021 18:20:31 -0400 Subject: [PATCH 07/10] Refactor database methods to parent worker script --- app/workers.php | 104 ++++++++++++++++++++++++++++++++++++++- app/workers/audits.php | 8 +-- app/workers/database.php | 61 ++--------------------- app/workers/deletes.php | 87 +++++--------------------------- app/workers/tasks.php | 9 +--- 5 files changed, 120 insertions(+), 149 deletions(-) diff --git a/app/workers.php b/app/workers.php index ff5ba1dbd3..b33ec9db61 100644 --- a/app/workers.php +++ b/app/workers.php @@ -1,7 +1,13 @@ set('db', function () { return $pdo; }); + $register->set('cache', function () { // Register cache connection $redis = new Redis(); $redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', '')); $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); return $redis; -}); \ No newline at end of file +}); + +/** + * Get internal project database + * @param string $projectId + * @return Database + */ +function getInternalDB(string $projectId): Database +{ + global $register; + + $attempts = 0; + $max = 10; + $sleep = 2; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); + $dbForInternal->setNamespace("project_{$projectId}_internal"); // Main DB + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForInternal; +} + +/** + * Get external project database + * @param string $projectId + * @return Database + */ +function getExternalDB(string $projectId): Database +{ + global $register; + + $attempts = 0; + $max = 10; + $sleep = 2; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); + $dbForExternal->setNamespace("project_{$projectId}_external"); // Main DB + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForExternal; +} + +/** + * Get console database + * @return Database + */ +function getConsoleDB(): Database +{ + global $register; + + $attempts = 0; + $max = 5; + $sleep = 5; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); + $dbForConsole->setNamespace('project_console_internal'); // Main DB + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForConsole; +} diff --git a/app/workers/audits.php b/app/workers/audits.php index 495c93bc48..2b603092fe 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -23,8 +23,6 @@ class AuditsV1 extends Worker public function run(): void { - global $register; - $projectId = $this->args['projectId']; $userId = $this->args['userId']; $event = $this->args['event']; @@ -32,12 +30,8 @@ class AuditsV1 extends Worker $userAgent = $this->args['userAgent']; $ip = $this->args['ip']; $data = $this->args['data']; - $db = $register->get('db', true); - $cache = new Cache(new Redis($register->get('cache'))); - $dbForInternal = new Database(new MariaDB($db), $cache); - $dbForInternal->setNamespace('project_'.$projectId.'_internal'); - + $dbForInternal = getInternalDB($projectId); $audit = new Audit($dbForInternal); $audit->log($userId, $event, $resource, $userAgent, $ip, '', $data); diff --git a/app/workers/database.php b/app/workers/database.php index b024871045..bdf33a4b37 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -1,12 +1,8 @@ getExternalDB($projectId); + $dbForExternal = getExternalDB($projectId); $collectionId = $attribute->getCollection(); $id = $attribute->getAttribute('$id', ''); @@ -89,7 +85,7 @@ class DatabaseV1 extends Worker */ protected function deleteAttribute($attribute, $projectId): void { - $dbForExternal = $this->getExternalDB($projectId); + $dbForExternal = getExternalDB($projectId); $collectionId = $attribute->getCollection(); $id = $attribute->getAttribute('$id'); @@ -103,7 +99,7 @@ class DatabaseV1 extends Worker */ protected function createIndex($index, $projectId): void { - $dbForExternal = $this->getExternalDB($projectId); + $dbForExternal = getExternalDB($projectId); $collectionId = $index->getCollection(); $id = $index->getAttribute('$id', ''); @@ -124,60 +120,11 @@ class DatabaseV1 extends Worker */ protected function deleteIndex($index, $projectId): void { - $dbForExternal = $this->getExternalDB($projectId); + $dbForExternal = getExternalDB($projectId); $collectionId = $index->getCollection(); $id = $index->getAttribute('$id'); $success = $dbForExternal->deleteIndex($collectionId, $id); } - - /** - * @param string $projectId - * - * @return Database - */ - protected function getInternalDB($projectId): Database - { - global $register; - - $dbForInternal = null; - - go(function() use ($register, $projectId, &$dbForInternal) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $cache = new Cache(new RedisCache($redis)); - $dbForInternal = new Database(new MariaDB($db), $cache); - $dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB - - }); - - return $dbForInternal; - } - - /** - * @param string $projectId - * - * @return Database - */ - protected function getExternalDB($projectId): Database - { - global $register; - - /** @var Database $dbForExternal */ - $dbForExternal = null; - - go(function() use ($register, $projectId, &$dbForExternal) { - $db = $register->get('dbPool')->get(); - $redis = $register->get('redisPool')->get(); - - $cache = new Cache(new RedisCache($redis)); - $dbForExternal = new Database(new MariaDB($db), $cache); - $dbForExternal->setNamespace('project_'.$projectId.'_external'); // Main DB - - }); - - return $dbForExternal; - } } diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 9c69b57927..8ecce1d483 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -3,17 +3,13 @@ use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Query; -use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\Database\Validator\Authorization; use Appwrite\Resque\Worker; use Utopia\Storage\Device\Local; use Utopia\Abuse\Abuse; use Utopia\Abuse\Adapters\TimeLimit; use Utopia\CLI\Console; -use Utopia\Config\Config; use Utopia\Audit\Audit; -use Utopia\Cache\Cache; -use Utopia\Database\Adapter\MariaDB; require_once __DIR__.'/../workers.php'; @@ -97,7 +93,7 @@ class DeletesV1 extends Worker // Delete Memberships $this->deleteByGroup('memberships', [ new Query('teamId', Query::TYPE_EQUAL, [$teamId]) - ], $this->getInternalDB($projectId)); + ], getInternalDB($projectId)); } /** @@ -107,8 +103,8 @@ class DeletesV1 extends Worker { $projectId = $document->getId(); // Delete all DBs - $this->getExternalDB($projectId)->delete(); - $this->getInternalDB($projectId)->delete(); + getExternalDB($projectId)->delete(); + getInternalDB($projectId)->delete(); // Delete all storage directories $uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId()); @@ -130,13 +126,13 @@ class DeletesV1 extends Worker // Delete Memberships and decrement team membership counts $this->deleteByGroup('memberships', [ new Query('userId', Query::TYPE_EQUAL, [$userId]) - ], $this->getInternalDB($projectId), function(Document $document) use ($projectId, $userId) { + ], getInternalDB($projectId), function(Document $document) use ($projectId, $userId) { if ($document->getAttribute('confirm')) { // Count only confirmed members $teamId = $document->getAttribute('teamId'); - $team = $this->getInternalDB($projectId)->getDocument('teams', $teamId); + $team = getInternalDB($projectId)->getDocument('teams', $teamId); if(!$team->isEmpty()) { - $team = $this->getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [ + $team = getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [ 'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0 ]))); } @@ -150,7 +146,7 @@ class DeletesV1 extends Worker protected function deleteExecutionLogs($timestamp) { $this->deleteForProjectIds(function($projectId) use ($timestamp) { - if (!($dbForInternal = $this->getInternalDB($projectId))) { + if (!($dbForInternal = getInternalDB($projectId))) { throw new Exception('Failed to get projectDB for project '.$projectId); } @@ -171,7 +167,7 @@ class DeletesV1 extends Worker } $this->deleteForProjectIds(function($projectId) use ($timestamp){ - $timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId)); + $timeLimit = new TimeLimit("", 0, 1, getInternalDB($projectId)); $abuse = new Abuse($timeLimit); $status = $abuse->cleanup($timestamp); @@ -190,7 +186,7 @@ class DeletesV1 extends Worker throw new Exception('Failed to delete audit logs. No timestamp provided'); } $this->deleteForProjectIds(function($projectId) use ($timestamp){ - $audit = new Audit($this->getInternalDB($projectId)); + $audit = new Audit(getInternalDB($projectId)); $status = $audit->cleanup($timestamp); if (!$status) { throw new Exception('Failed to delete Audit logs for project'.$projectId); @@ -204,7 +200,7 @@ class DeletesV1 extends Worker */ protected function deleteFunction(Document $document, $projectId) { - $dbForInternal = $this->getInternalDB($projectId); + $dbForInternal = getInternalDB($projectId); $device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId); // Delete Tags @@ -273,7 +269,7 @@ class DeletesV1 extends Worker $chunk++; Authorization::disable(); - $projects = $this->getConsoleDB()->find('projects', [], $limit); + $projects = getConsoleDB()->find('projects', [], $limit); Authorization::reset(); $projectIds = array_map (function ($project) { @@ -351,65 +347,4 @@ class DeletesV1 extends Worker Console::info("No certificate files found for {$domain}"); } } - - /** - * @param string $projectId - * @return Database - */ - protected function getInternalDB($projectId): Database - { - global $register; - - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB - - return $dbForInternal; - } - - /** - * @param string $projectId - * @return Database - */ - protected function getExternalDB($projectId): Database - { - global $register; - - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForExternal->setNamespace('project_'.$projectId.'_external'); // Main DB - - return $dbForExternal; - } - - /** - * @return Database - */ - protected function getConsoleDB(): Database - { - global $register; - - // wait for database to be ready - $attempts = 0; - $max = 5; - $sleep = 5; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); - $dbForConsole->setNamespace('project_console_internal'); // Main DB - break; // leave the do-while if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForConsole; - } } diff --git a/app/workers/tasks.php b/app/workers/tasks.php index cb24c65a9f..cbc8d9573e 100644 --- a/app/workers/tasks.php +++ b/app/workers/tasks.php @@ -28,11 +28,6 @@ class TasksV1 extends Worker public function run(): void { - global $register; - - $db = $register->get('db'); - $cache = $register->get('cache'); - $projectId = $this->args['projectId'] ?? null; $taskId = $this->args['$id'] ?? null; $updated = $this->args['updated'] ?? null; @@ -44,9 +39,7 @@ class TasksV1 extends Worker $logLimit = 5; $alert = ''; - $cache = new Cache(new Redis($cache)); - $dbForConsole = new Database(new MariaDB($db), $cache); - $dbForConsole->setNamespace('project_console_internal'); + $dbForConsole = getConsoleDB(); /* * 1. Get Original Task From 51100fda0aa4559396f90ed0a87df97890422b90 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Sun, 25 Jul 2021 18:24:29 -0400 Subject: [PATCH 08/10] Ensure namespace exists before proceeding --- app/workers.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/workers.php b/app/workers.php index b33ec9db61..7ae36c1922 100644 --- a/app/workers.php +++ b/app/workers.php @@ -57,6 +57,9 @@ function getInternalDB(string $projectId): Database $cache = new Cache(new RedisCache($register->get('cache'))); $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); $dbForInternal->setNamespace("project_{$projectId}_internal"); // Main DB + if (!$dbForInternal->exists()) { + throw new Exception("Table does not exist: {$dbForInternal->getNamespace()}"); + } break; // leave loop if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); @@ -89,6 +92,9 @@ function getExternalDB(string $projectId): Database $cache = new Cache(new RedisCache($register->get('cache'))); $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); $dbForExternal->setNamespace("project_{$projectId}_external"); // Main DB + if (!$dbForExternal->exists()) { + throw new Exception("Table does not exist: {$dbForExternal->getNamespace()}"); + } break; // leave loop if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); @@ -120,6 +126,9 @@ function getConsoleDB(): Database $cache = new Cache(new RedisCache($register->get('cache'))); $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); $dbForConsole->setNamespace('project_console_internal'); // Main DB + if (!$dbForConsole->exists()) { + throw new Exception("Table does not exist: {$dbForConsole->getNamespace()}"); + } break; // leave loop if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); From 51449841bf8a0170208291ee2222a719d4e9644a Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Sun, 25 Jul 2021 19:14:19 -0400 Subject: [PATCH 09/10] Refactor db methods to worker class instead of worker script --- app/workers.php | 109 -------------------------------- app/workers/audits.php | 6 +- app/workers/database.php | 10 +-- app/workers/deletes.php | 22 +++---- app/workers/functions.php | 10 +-- app/workers/tasks.php | 2 +- src/Appwrite/Resque/Worker.php | 110 +++++++++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 140 deletions(-) diff --git a/app/workers.php b/app/workers.php index 7ae36c1922..7dd48eaff8 100644 --- a/app/workers.php +++ b/app/workers.php @@ -1,13 +1,7 @@ set('cache', function () { // Register cache connection return $redis; }); -/** - * Get internal project database - * @param string $projectId - * @return Database - */ -function getInternalDB(string $projectId): Database -{ - global $register; - - $attempts = 0; - $max = 10; - $sleep = 2; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForInternal->setNamespace("project_{$projectId}_internal"); // Main DB - if (!$dbForInternal->exists()) { - throw new Exception("Table does not exist: {$dbForInternal->getNamespace()}"); - } - break; // leave loop if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForInternal; -} - -/** - * Get external project database - * @param string $projectId - * @return Database - */ -function getExternalDB(string $projectId): Database -{ - global $register; - - $attempts = 0; - $max = 10; - $sleep = 2; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForExternal->setNamespace("project_{$projectId}_external"); // Main DB - if (!$dbForExternal->exists()) { - throw new Exception("Table does not exist: {$dbForExternal->getNamespace()}"); - } - break; // leave loop if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForExternal; -} - -/** - * Get console database - * @return Database - */ -function getConsoleDB(): Database -{ - global $register; - - $attempts = 0; - $max = 5; - $sleep = 5; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); - $dbForConsole->setNamespace('project_console_internal'); // Main DB - if (!$dbForConsole->exists()) { - throw new Exception("Table does not exist: {$dbForConsole->getNamespace()}"); - } - break; // leave loop if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForConsole; -} diff --git a/app/workers/audits.php b/app/workers/audits.php index 2b603092fe..663de3ce4f 100644 --- a/app/workers/audits.php +++ b/app/workers/audits.php @@ -2,11 +2,7 @@ use Appwrite\Resque\Worker; use Utopia\Audit\Audit; -use Utopia\Cache\Adapter\Redis; -use Utopia\Cache\Cache; use Utopia\CLI\Console; -use Utopia\Database\Adapter\MariaDB; -use Utopia\Database\Database; require_once __DIR__.'/../workers.php'; @@ -31,7 +27,7 @@ class AuditsV1 extends Worker $ip = $this->args['ip']; $data = $this->args['data']; - $dbForInternal = getInternalDB($projectId); + $dbForInternal = $this->getInternalDB($projectId); $audit = new Audit($dbForInternal); $audit->log($userId, $event, $resource, $userAgent, $ip, '', $data); diff --git a/app/workers/database.php b/app/workers/database.php index bdf33a4b37..544ffe1060 100644 --- a/app/workers/database.php +++ b/app/workers/database.php @@ -4,7 +4,7 @@ use Appwrite\Resque\Worker; use Utopia\CLI\Console; use Utopia\Database\Document; -require_once __DIR__.'/../init.php'; +require_once __DIR__.'/../workers.php'; Console::title('Database V1 Worker'); Console::success(APP_NAME.' database worker v1 has started'."\n"); @@ -61,7 +61,7 @@ class DatabaseV1 extends Worker */ protected function createAttribute($attribute, $projectId): void { - $dbForExternal = getExternalDB($projectId); + $dbForExternal = $this->getExternalDB($projectId); $collectionId = $attribute->getCollection(); $id = $attribute->getAttribute('$id', ''); @@ -85,7 +85,7 @@ class DatabaseV1 extends Worker */ protected function deleteAttribute($attribute, $projectId): void { - $dbForExternal = getExternalDB($projectId); + $dbForExternal = $this->getExternalDB($projectId); $collectionId = $attribute->getCollection(); $id = $attribute->getAttribute('$id'); @@ -99,7 +99,7 @@ class DatabaseV1 extends Worker */ protected function createIndex($index, $projectId): void { - $dbForExternal = getExternalDB($projectId); + $dbForExternal = $this->getExternalDB($projectId); $collectionId = $index->getCollection(); $id = $index->getAttribute('$id', ''); @@ -120,7 +120,7 @@ class DatabaseV1 extends Worker */ protected function deleteIndex($index, $projectId): void { - $dbForExternal = getExternalDB($projectId); + $dbForExternal = $this->getExternalDB($projectId); $collectionId = $index->getCollection(); $id = $index->getAttribute('$id'); diff --git a/app/workers/deletes.php b/app/workers/deletes.php index 8ecce1d483..e98723a5ea 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -93,7 +93,7 @@ class DeletesV1 extends Worker // Delete Memberships $this->deleteByGroup('memberships', [ new Query('teamId', Query::TYPE_EQUAL, [$teamId]) - ], getInternalDB($projectId)); + ], $this->getInternalDB($projectId)); } /** @@ -103,8 +103,8 @@ class DeletesV1 extends Worker { $projectId = $document->getId(); // Delete all DBs - getExternalDB($projectId)->delete(); - getInternalDB($projectId)->delete(); + $this->getExternalDB($projectId)->delete(); + $this->getInternalDB($projectId)->delete(); // Delete all storage directories $uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId()); @@ -126,13 +126,13 @@ class DeletesV1 extends Worker // Delete Memberships and decrement team membership counts $this->deleteByGroup('memberships', [ new Query('userId', Query::TYPE_EQUAL, [$userId]) - ], getInternalDB($projectId), function(Document $document) use ($projectId, $userId) { + ], $this->getInternalDB($projectId), function(Document $document) use ($projectId, $userId) { if ($document->getAttribute('confirm')) { // Count only confirmed members $teamId = $document->getAttribute('teamId'); - $team = getInternalDB($projectId)->getDocument('teams', $teamId); + $team = $this->getInternalDB($projectId)->getDocument('teams', $teamId); if(!$team->isEmpty()) { - $team = getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [ + $team = $this->getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [ 'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0 ]))); } @@ -146,7 +146,7 @@ class DeletesV1 extends Worker protected function deleteExecutionLogs($timestamp) { $this->deleteForProjectIds(function($projectId) use ($timestamp) { - if (!($dbForInternal = getInternalDB($projectId))) { + if (!($dbForInternal = $this->getInternalDB($projectId))) { throw new Exception('Failed to get projectDB for project '.$projectId); } @@ -167,7 +167,7 @@ class DeletesV1 extends Worker } $this->deleteForProjectIds(function($projectId) use ($timestamp){ - $timeLimit = new TimeLimit("", 0, 1, getInternalDB($projectId)); + $timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId)); $abuse = new Abuse($timeLimit); $status = $abuse->cleanup($timestamp); @@ -186,7 +186,7 @@ class DeletesV1 extends Worker throw new Exception('Failed to delete audit logs. No timestamp provided'); } $this->deleteForProjectIds(function($projectId) use ($timestamp){ - $audit = new Audit(getInternalDB($projectId)); + $audit = new Audit($this->getInternalDB($projectId)); $status = $audit->cleanup($timestamp); if (!$status) { throw new Exception('Failed to delete Audit logs for project'.$projectId); @@ -200,7 +200,7 @@ class DeletesV1 extends Worker */ protected function deleteFunction(Document $document, $projectId) { - $dbForInternal = getInternalDB($projectId); + $dbForInternal = $this->getInternalDB($projectId); $device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId); // Delete Tags @@ -269,7 +269,7 @@ class DeletesV1 extends Worker $chunk++; Authorization::disable(); - $projects = getConsoleDB()->find('projects', [], $limit); + $projects = $this->getConsoleDB()->find('projects', [], $limit); Authorization::reset(); $projectIds = array_map (function ($project) { diff --git a/app/workers/functions.php b/app/workers/functions.php index 8db7d64753..b6263cc5f8 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -6,11 +6,8 @@ use Appwrite\Utopia\Response\Model\Execution; use Cron\CronExpression; use Swoole\Runtime; use Utopia\App; -use Utopia\Cache\Adapter\Redis; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Config\Config; -use Utopia\Database\Adapter\MariaDB; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -139,9 +136,6 @@ class FunctionsV1 extends Worker { global $register; - $db = $register->get('db'); - $cache = $register->get('cache'); - $projectId = $this->args['projectId'] ?? ''; $functionId = $this->args['functionId'] ?? ''; $webhooks = $this->args['webhooks'] ?? []; @@ -154,9 +148,7 @@ class FunctionsV1 extends Worker $userId = $this->args['userId'] ?? ''; $jwt = $this->args['jwt'] ?? ''; - $cache = new Cache(new Redis($cache)); - $database = new Database(new MariaDB($db), $cache); - $database->setNamespace('project_'.$projectId.'_internal'); + $database = $this->getInternalDB($projectId); switch ($trigger) { case 'event': diff --git a/app/workers/tasks.php b/app/workers/tasks.php index cbc8d9573e..b3469393ef 100644 --- a/app/workers/tasks.php +++ b/app/workers/tasks.php @@ -39,7 +39,7 @@ class TasksV1 extends Worker $logLimit = 5; $alert = ''; - $dbForConsole = getConsoleDB(); + $dbForConsole = $this->getConsoleDB(); /* * 1. Get Original Task diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php index db8dc91ce7..93c666723e 100644 --- a/src/Appwrite/Resque/Worker.php +++ b/src/Appwrite/Resque/Worker.php @@ -2,6 +2,13 @@ namespace Appwrite\Resque; +use Exception; +use Utopia\Cache\Cache; +use Utopia\Cache\Adapter\Redis as RedisCache; +use Utopia\CLI\Console; +use Utopia\Database\Database; +use Utopia\Database\Adapter\MariaDB; + abstract class Worker { public $args = []; @@ -26,4 +33,107 @@ abstract class Worker { $this->shutdown(); } + /** + * Get internal project database + * @param string $projectId + * @return Database + */ + protected function getInternalDB(string $projectId): Database + { + global $register; + + $attempts = 0; + $max = 10; + $sleep = 2; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); + $dbForInternal->setNamespace("project_{$projectId}_internal"); // Main DB + if (!$dbForInternal->exists()) { + throw new Exception("Table does not exist: {$dbForInternal->getNamespace()}"); + } + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForInternal; + } + + /** + * Get external project database + * @param string $projectId + * @return Database + */ + protected function getExternalDB(string $projectId): Database + { + global $register; + + $attempts = 0; + $max = 10; + $sleep = 2; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); + $dbForExternal->setNamespace("project_{$projectId}_external"); // Main DB + if (!$dbForExternal->exists()) { + throw new Exception("Table does not exist: {$dbForExternal->getNamespace()}"); + } + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForExternal; + } + + /** + * Get console database + * @return Database + */ + protected function getConsoleDB(): Database + { + global $register; + + $attempts = 0; + $max = 5; + $sleep = 5; + + do { + try { + $attempts++; + $cache = new Cache(new RedisCache($register->get('cache'))); + $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); + $dbForConsole->setNamespace('project_console_internal'); // Main DB + if (!$dbForConsole->exists()) { + throw new Exception("Table does not exist: {$dbForConsole->getNamespace()}"); + } + break; // leave loop if successful + } catch(\Exception $e) { + Console::warning("Database not ready. Retrying connection ({$attempts})..."); + if ($attempts >= $max) { + throw new \Exception('Failed to connect to database: '. $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + return $dbForConsole; + } } \ No newline at end of file From b340d236dbf3afe701dacd4b56653bb3f751a151 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Mon, 26 Jul 2021 10:59:38 -0400 Subject: [PATCH 10/10] Refactor retry logic to private method --- src/Appwrite/Resque/Worker.php | 114 +++++++++++++++------------------ 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php index 93c666723e..84241173f5 100644 --- a/src/Appwrite/Resque/Worker.php +++ b/src/Appwrite/Resque/Worker.php @@ -2,7 +2,6 @@ namespace Appwrite\Resque; -use Exception; use Utopia\Cache\Cache; use Utopia\Cache\Adapter\Redis as RedisCache; use Utopia\CLI\Console; @@ -19,6 +18,13 @@ abstract class Worker abstract public function shutdown(): void; + const MAX_ATTEMPTS = 10; + const SLEEP_TIME = 2; + + const DATABASE_INTERNAL = 'internal'; + const DATABASE_EXTERNAL = 'external'; + const DATABASE_CONSOLE = 'console'; + public function setUp(): void { $this->init(); @@ -40,32 +46,7 @@ abstract class Worker */ protected function getInternalDB(string $projectId): Database { - global $register; - - $attempts = 0; - $max = 10; - $sleep = 2; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForInternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForInternal->setNamespace("project_{$projectId}_internal"); // Main DB - if (!$dbForInternal->exists()) { - throw new Exception("Table does not exist: {$dbForInternal->getNamespace()}"); - } - break; // leave loop if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForInternal; + return $this->getDB(self::DATABASE_INTERNAL, $projectId); } /** @@ -75,32 +56,7 @@ abstract class Worker */ protected function getExternalDB(string $projectId): Database { - global $register; - - $attempts = 0; - $max = 10; - $sleep = 2; - - do { - try { - $attempts++; - $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForExternal = new Database(new MariaDB($register->get('db')), $cache); - $dbForExternal->setNamespace("project_{$projectId}_external"); // Main DB - if (!$dbForExternal->exists()) { - throw new Exception("Table does not exist: {$dbForExternal->getNamespace()}"); - } - break; // leave loop if successful - } catch(\Exception $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: '. $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - return $dbForExternal; + return $this->getDB(self::DATABASE_EXTERNAL, $projectId); } /** @@ -108,32 +64,66 @@ abstract class Worker * @return Database */ protected function getConsoleDB(): Database + { + return $this->getDB(self::DATABASE_CONSOLE); + } + + /** + * Get console database + * @param string $type One of (internal, external, console) + * @param string $projectId of internal or external DB + * @return Database + */ + private function getDB($type, $projectId = ''): Database { global $register; + $namespace = ''; + $sleep = self::SLEEP_TIME; // overwritten when necessary + + switch ($type) { + case self::DATABASE_INTERNAL: + if (!$projectId) { + throw new \Exception('ProjectID not provided - cannot get database'); + } + $namespace = "project_{$projectId}_internal"; + break; + case self::DATABASE_EXTERNAL: + if (!$projectId) { + throw new \Exception('ProjectID not provided - cannot get database'); + } + $namespace = "project_{$projectId}_external"; + break; + case self::DATABASE_CONSOLE: + $namespace = "project_console_internal"; + $sleep = 5; // ConsoleDB needs extra sleep time to ensure tables are created + break; + default: + throw new \Exception('Unknown database type: ' . $type); + break; + } + $attempts = 0; - $max = 5; - $sleep = 5; do { try { $attempts++; $cache = new Cache(new RedisCache($register->get('cache'))); - $dbForConsole = new Database(new MariaDB($register->get('db')), $cache); - $dbForConsole->setNamespace('project_console_internal'); // Main DB - if (!$dbForConsole->exists()) { - throw new Exception("Table does not exist: {$dbForConsole->getNamespace()}"); + $database = new Database(new MariaDB($register->get('db')), $cache); + $database->setNamespace($namespace); // Main DB + if (!$database->exists()) { + throw new \Exception("Table does not exist: {$database->getNamespace()}"); } break; // leave loop if successful } catch(\Exception $e) { Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { + if ($attempts >= self::MAX_ATTEMPTS) { throw new \Exception('Failed to connect to database: '. $e->getMessage()); } sleep($sleep); } - } while ($attempts < $max); + } while ($attempts < self::MAX_ATTEMPTS); - return $dbForConsole; + return $database; } } \ No newline at end of file