From e9dac6710f05dd951b4a05e4c84e91b80cc6177b Mon Sep 17 00:00:00 2001 From: shimon Date: Sun, 4 Jan 2026 09:53:29 +0200 Subject: [PATCH] Refactor: Remove unused webhook and function event filters, implement caching for function events retrieval --- app/config/collections/platform.php | 22 ---- app/init/database/filters.php | 38 ------- .../Platform/Modules/Compute/Base.php | 25 +++++ .../Collections/Documents/Action.php | 100 +++++++++++++++++- .../Collections/Documents/Bulk/Delete.php | 3 +- .../Collections/Documents/Bulk/Update.php | 3 +- .../Collections/Documents/Bulk/Upsert.php | 3 +- .../Collections/Documents/Create.php | 3 +- .../Functions/Http/Functions/Delete.php | 6 ++ .../Functions/Http/Functions/Update.php | 3 + 10 files changed, 138 insertions(+), 68 deletions(-) diff --git a/app/config/collections/platform.php b/app/config/collections/platform.php index 9f46d5e8c7..d44d9b725c 100644 --- a/app/config/collections/platform.php +++ b/app/config/collections/platform.php @@ -276,28 +276,6 @@ return [ 'array' => false, 'filters' => ['subQueryWebhooks'], ], - [ - '$id' => ID::custom('webhookEvents'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => true, - 'filters' => ['subQueryWebhookEvents'], - ], - [ - '$id' => ID::custom('functionEvents'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => 16384, - 'signed' => true, - 'required' => false, - 'default' => [], - 'array' => true, - 'filters' => ['subQueryFunctionEvents'], - ], [ '$id' => ID::custom('keys'), 'type' => Database::VAR_STRING, diff --git a/app/init/database/filters.php b/app/init/database/filters.php index ef40e55379..c4cfd1ac81 100644 --- a/app/init/database/filters.php +++ b/app/init/database/filters.php @@ -170,44 +170,6 @@ Database::addFilter( } ); -Database::addFilter( - 'subQueryWebhookEvents', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - $webhooks = $database - ->find('webhooks', [ - Query::equal('projectInternalId', [$document->getSequence()]), - Query::limit(APP_LIMIT_SUBQUERY), - ]); - - $events = []; - foreach ($webhooks as $webhook) { - $webhookEvents = $webhook->getAttribute('events', []); - if (!empty($webhookEvents)) { - $events = array_merge($events, $webhookEvents); - } - } - - return array_unique($events); - } -); - -Database::addFilter( - 'subQueryFunctionEvents', - function (mixed $value) { - return; - }, - function (mixed $value, Document $document, Database $database) { - // Functions are stored in the project database, not platform database - // This filter will return empty array when called from platform DB - // Function events will need to be computed separately when dbForProject is available - // For now, return empty to avoid errors - return []; - } -); - Database::addFilter( 'subQuerySessions', function (mixed $value) { diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index 47afc90986..0ef22f9383 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -336,4 +336,29 @@ class Base extends Action return $deployment; } + + /** + * Purge function events cache for a project + * @param Document $project + * @param Database $dbForProject + * @return void + */ + protected function purgeFunctionEventsCache(Document $project, Database $dbForProject): void + { + if ($project->isEmpty() || $project->getId() === 'console') { + return; + } + + $hostname = $dbForProject->getAdapter()->getHostname(); + $cacheKey = \sprintf( + '%s-cache-%s:%s:%s:project:%s:functionEvents', + $dbForProject->getCacheName(), + $hostname ?? '', + $dbForProject->getNamespace(), + $dbForProject->getTenant(), + $project->getId() + ); + + $dbForProject->getCache()->purge($cacheKey); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index a0cb5c20f5..f8196b582b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -7,6 +7,7 @@ use Appwrite\Extend\Exception; use Appwrite\Platform\Modules\Databases\Http\Databases\Action as DatabasesAction; use Utopia\Database\Database; use Utopia\Database\Document; +use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; abstract class Action extends DatabasesAction @@ -348,6 +349,7 @@ abstract class Action extends DatabasesAction * @param Event $queueForRealtime * @param Event $queueForFunctions * @param Event $queueForWebhooks + * @param Database $dbForProject * @return void */ protected function triggerBulk( @@ -358,7 +360,8 @@ abstract class Action extends DatabasesAction Event $queueForEvents, Event $queueForRealtime, Event $queueForFunctions, - Event $queueForWebhooks + Event $queueForWebhooks, + Database $dbForProject ): void { $queueForEvents ->setEvent($event) @@ -368,6 +371,11 @@ abstract class Action extends DatabasesAction ->setParam('tableId', $collection->getId()) ->setContext($this->getCollectionsEventsContext(), $collection); + // Get project and function events (cached) + $project = $queueForEvents->getProject(); + $functionEvents = $this->getFunctionEvents($project, $dbForProject); + $webhookEvents = $this->getWebhookEvents($project); + foreach ($documents as $document) { $queueForEvents ->setParam('documentId', $document->getId()) @@ -378,20 +386,20 @@ abstract class Action extends DatabasesAction ->from($queueForEvents) ->trigger(); - $project = $queueForEvents->getProject(); + // Generate events for this document operation $generatedEvents = Event::generateEvents( $queueForEvents->getEvent(), $queueForEvents->getParams() ); - $functionEvents = $project?->getAttribute('functionEvents', []); + // Only trigger functions if there are matching function events if (!empty($functionEvents) && !empty(array_intersect($functionEvents, $generatedEvents))) { $queueForFunctions ->from($queueForEvents) ->trigger(); } - $webhookEvents = $project?->getAttribute('webhookEvents', []); + // Only trigger webhooks if there are matching webhook events if (!empty($webhookEvents) && !empty(array_intersect($webhookEvents, $generatedEvents))) { $queueForWebhooks ->from($queueForEvents) @@ -404,4 +412,88 @@ abstract class Action extends DatabasesAction $queueForFunctions->reset(); $queueForWebhooks->reset(); } + + /** + * Get function events for a project, using Redis cache + * @param Document|null $project + * @param Database $dbForProject + * @return array + */ + protected function getFunctionEvents(?Document $project, Database $dbForProject): array + { + if ($project === null || $project->isEmpty() || $project->getId() === 'console') { + return []; + } + + $hostname = $dbForProject->getAdapter()->getHostname(); + $cacheKey = \sprintf( + '%s-cache-%s:%s:%s:project:%s:functionEvents', + $dbForProject->getCacheName(), + $hostname ?? '', + $dbForProject->getNamespace(), + $dbForProject->getTenant(), + $project->getId() + ); + + $ttl = 3600; // 1 hour cache TTL + $cachedFunctionEvents = $dbForProject->getCache()->load($cacheKey, $ttl); + + if ($cachedFunctionEvents !== false) { + return \json_decode($cachedFunctionEvents, true) ?? []; + } + + try { + $functions = $dbForProject->skipValidation(fn () => $dbForProject->find('functions', [ + Query::limit(APP_LIMIT_SUBQUERY), + ])); + + $events = []; + foreach ($functions as $function) { + $functionEvents = $function->getAttribute('events', []); + if (!empty($functionEvents)) { + $events = array_merge($events, $functionEvents); + } + } + + $uniqueEvents = array_unique($events); + + // Save to cache + $dbForProject->getCache()->save($cacheKey, \json_encode($uniqueEvents), $ttl); + + return $uniqueEvents; + } catch (\Throwable $e) { + return []; + } + } + + /** + * Get webhook events for a project from the project's webhooks attribute + * @param Document|null $project + * @return array + */ + protected function getWebhookEvents(?Document $project): array + { + if ($project === null || $project->isEmpty() || $project->getId() === 'console') { + return []; + } + + $webhooks = $project->getAttribute('webhooks', []); + if (empty($webhooks)) { + return []; + } + + $events = []; + foreach ($webhooks as $webhook) { + if ($webhook->getAttribute('enabled', false) !== true) { + continue; + } + + $webhookEvents = $webhook->getAttribute('events', []); + if (!empty($webhookEvents)) { + $events = array_merge($events, $webhookEvents); + } + } + + return array_unique($events); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 070ee09450..e3ba6a37e7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -203,7 +203,8 @@ class Delete extends Action $queueForEvents, $queueForRealtime, $queueForFunctions, - $queueForWebhooks + $queueForWebhooks, + $dbForProject ); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 192b10c956..892ab7f0da 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -234,7 +234,8 @@ class Update extends Action $queueForEvents, $queueForRealtime, $queueForFunctions, - $queueForWebhooks + $queueForWebhooks, + $dbForProject ); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 45db6cc96b..c15a8b94ae 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -209,7 +209,8 @@ class Upsert extends Action $queueForEvents, $queueForRealtime, $queueForFunctions, - $queueForWebhooks + $queueForWebhooks, + $dbForProject ); } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 6ec06f5c8a..60a1f66f36 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -491,7 +491,8 @@ class Create extends Action $queueForEvents, $queueForRealtime, $queueForFunctions, - $queueForWebhooks + $queueForWebhooks, + $dbForProject ); return; } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php index dfa6636554..ee4db800a2 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Delete.php @@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; use Utopia\Database\Database; use Utopia\Database\DateTime; +use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; @@ -58,6 +59,7 @@ class Delete extends Base ->param('functionId', '', new UID(), 'Function ID.') ->inject('response') ->inject('dbForProject') + ->inject('project') ->inject('queueForDeletes') ->inject('queueForEvents') ->inject('dbForPlatform') @@ -68,6 +70,7 @@ class Delete extends Base string $functionId, Response $response, Database $dbForProject, + Document $project, DeleteEvent $queueForDeletes, Event $queueForEvents, Database $dbForPlatform @@ -95,6 +98,9 @@ class Delete extends Base $queueForEvents->setParam('functionId', $function->getId()); + // Purge function events cache when function is deleted + $this->purgeFunctionEventsCache($project, $dbForProject); + $response->noContent(); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index adb29bc533..3623e26ec6 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -286,6 +286,9 @@ class Update extends Base $queueForEvents->setParam('functionId', $function->getId()); + // Purge function events cache when function is updated + $this->purgeFunctionEventsCache($project, $dbForProject); + $response->dynamic($function, Response::MODEL_FUNCTION); } }