Refactor usage metrics to stateless publisher pattern#11449
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces the StatsUsage queue/class with a usage Context and publisher/message model. Adds Appwrite\Usage\Context, Appwrite\Event\Message\Base, Appwrite\Event\Message\Usage, Appwrite\Event\Publisher\Base and Appwrite\Event\Publisher\Usage. Introduces public resources Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan for PR comments
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Security Scan Results for PRDocker Image Scan Results
Source Code Scan Results🎉 No vulnerabilities found! |
✨ Benchmark results
⚡ Benchmark Comparison
|
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Appwrite/Platform/Modules/Functions/Workers/Builds.php (1)
1181-1186:⚠️ Potential issue | 🔴 CriticalFix
sendUsage()type mismatch: signature declaresStatsUsagebut receivesContext.Line 1185 passes
$usage(aContextinstance) tosendUsage(queue: ...), but the method signature at line 1190 declaresStatsUsage $queue. This will fail with aTypeErrorat runtime when strict type checking is enforced. The method body correctly uses->addMetric(), whichContextimplements.Fix
- protected function sendUsage(Document $resource, Document $deployment, Document $project, StatsUsage $queue): void + protected function sendUsage(Document $resource, Document $deployment, Document $project, Context $queue): void🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php` around lines 1181 - 1186, The call to sendUsage passes $usage (a Context instance) but the method signature expects StatsUsage; update the sendUsage method signature to accept Context $queue (instead of StatsUsage) and update any related type hints/imports so the parameter type matches the object being passed ($usage), ensuring the body can still call ->addMetric() on $queue; alternatively, if you prefer to keep StatsUsage, change the caller to pass a StatsUsage instance, but the simplest fix is to change the sendUsage signature from StatsUsage to Context to match the usage.
🧹 Nitpick comments (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php (1)
145-145: Remove unused$usagefrom closure capture.
$usageis captured in thewithTransactionclosure but not used there.♻️ Optional cleanup
- $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, &$currentDocumentId, $queueForEvents, $usage, $queueForRealtime, $queueForFunctions, $queueForWebhooks, $authorization) { + $dbForProject->withTransaction(function () use ($dbForProject, $transactionState, $queueForDeletes, $transactionId, &$transaction, &$operations, &$totalOperations, &$databaseOperations, &$currentDocumentId, $queueForEvents, $queueForRealtime, $queueForFunctions, $queueForWebhooks, $authorization) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php` at line 145, The closure passed to $dbForProject->withTransaction in Update.php unnecessarily captures $usage; remove $usage from the closure's use(...) capture list in the withTransaction call (the anonymous function starting with function () use ($dbForProject, $transactionState, ...)) so the closure only captures variables it actually uses, and run a quick search for $usage inside that closure to confirm it isn't referenced before committing the change.src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php (1)
102-102: Consider removing unused$timelimitand$planfrom this action signature.They are currently injected but not referenced, which adds avoidable noise to the endpoint contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php` at line 102, The action method signature for Memberships::action currently includes unused parameters $timelimit and $plan; remove these two parameters from the action(string $teamId, ...) signature in Create::action to keep the endpoint contract minimal, and update any docblocks and unit tests referencing Create::action; also search for any caller or DI/invocation sites that pass $timelimit or $plan to Create::action and adjust those calls to no longer supply them so the parameter removal stays consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/controllers/shared/api.php`:
- Around line 972-981: The shutdown path is enqueuing usage messages
unconditionally when $usage->isEmpty() is false, bypassing the global
_APP_USAGE_STATS flag; update the shutdown logic in
app/controllers/shared/api.php to check the _APP_USAGE_STATS configuration (or
equivalent feature flag) before creating/enqueuing the
\Appwrite\Event\Message\Usage and calling $publisherForUsage->enqueue($message),
returning early if usage stats are disabled; make the same change for the second
similar block that enqueues usage messages (the block referencing $usage and
$publisherForUsage around the later lines) so non-`groups(['usage'])` requests
do not publish metrics when stats are turned off.
In
`@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php`:
- Around line 78-84: Remove the unused user injection and parameter: delete the
->inject('user') call from the route builder and remove the Document $user
parameter from the action method signature (public function action(... Document
$user, ...)), updating the parameter list order accordingly so other parameters
(e.g., Context $usage, TransactionState $transactionState, Authorization
$authorization) remain in place and types/signatures still match.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Line 10: The file imports the Appwrite\Usage\Context symbol twice causing a
fatal "already in use" error; open Create.php, find the duplicate import
statements for Appwrite\Usage\Context (the repeated "use
Appwrite\Usage\Context;") and remove the second occurrence so only a single use
Appwrite\Usage\Context; remains; ensure any references to Context in methods
like the membership creation handler still work after removing the duplicate
import.
In `@src/Appwrite/Platform/Workers/Messaging.php`:
- Around line 242-243: The Appwrite\Usage\Context class is missing the
setProject(Document $project): self and trigger(): void methods that are called
where $usage->setProject($project) and $usage->trigger() are used; add these
methods to Context (implement setProject to store the Document $project and
return $this, and implement trigger to flush/process queued metrics using
existing addMetric/addReduce internals) or alternatively change the callers in
Messaging.php to use the existing API (e.g., pass the project into
addMetric/addReduce directly and invoke whatever flush/reset method already
exists); locate the call sites around Messaging.php where $usage is referenced
and update either Context or those call sites accordingly so runtime fatal
errors are eliminated.
In `@src/Appwrite/Platform/Workers/Migrations.php`:
- Around line 107-109: The migration stats publisher ($publisherForUsage) is
received in action() but not passed into processMigration() or
processMigrationResourceStats(), causing an undefined variable when calling
$publisherForUsage->enqueue($message); update the method signatures of
processMigration(...) and processMigrationResourceStats(...) to accept an
additional \Appwrite\Event\Publisher\Usage $publisherForUsage parameter,
propagate that argument from action() into processMigration(), and from
processMigration() into processMigrationResourceStats(); then replace the
undefined $publisherForUsage usage inside processMigrationResourceStats() with
the new parameter so the enqueue call uses the passed-in publisher instance.
In `@src/Appwrite/Usage/Context.php`:
- Around line 61-68: The filtered metrics use array_filter in the return from
Context.php which preserves original numeric keys and can produce sparse arrays
that json_encode serializes as objects; fix by wrapping the array_filter result
with array_values so the returned value from the function (the filtered metrics)
is reindexed and always serializes as a JSON array (update the return that
currently calls array_filter on $this->metrics).
---
Outside diff comments:
In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`:
- Around line 1181-1186: The call to sendUsage passes $usage (a Context
instance) but the method signature expects StatsUsage; update the sendUsage
method signature to accept Context $queue (instead of StatsUsage) and update any
related type hints/imports so the parameter type matches the object being passed
($usage), ensuring the body can still call ->addMetric() on $queue;
alternatively, if you prefer to keep StatsUsage, change the caller to pass a
StatsUsage instance, but the simplest fix is to change the sendUsage signature
from StatsUsage to Context to match the usage.
---
Nitpick comments:
In
`@src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php`:
- Line 145: The closure passed to $dbForProject->withTransaction in Update.php
unnecessarily captures $usage; remove $usage from the closure's use(...) capture
list in the withTransaction call (the anonymous function starting with function
() use ($dbForProject, $transactionState, ...)) so the closure only captures
variables it actually uses, and run a quick search for $usage inside that
closure to confirm it isn't referenced before committing the change.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Line 102: The action method signature for Memberships::action currently
includes unused parameters $timelimit and $plan; remove these two parameters
from the action(string $teamId, ...) signature in Create::action to keep the
endpoint contract minimal, and update any docblocks and unit tests referencing
Create::action; also search for any caller or DI/invocation sites that pass
$timelimit or $plan to Create::action and adjust those calls to no longer supply
them so the parameter removal stays consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2ead9333-7fe9-4aeb-aef8-41a48474aafc
📒 Files selected for processing (48)
CONTRIBUTING.mdapp/cli.phpapp/controllers/api/account.phpapp/controllers/shared/api.phpapp/init/resources.phpapp/worker.phpsrc/Appwrite/Bus/Listeners/Usage.phpsrc/Appwrite/Event/Message/Base.phpsrc/Appwrite/Event/Message/Usage.phpsrc/Appwrite/Event/Publisher/Base.phpsrc/Appwrite/Event/Publisher/Usage.phpsrc/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.phpsrc/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Functions/Http/Executions/Create.phpsrc/Appwrite/Platform/Modules/Functions/Workers/Builds.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/Failed/Get.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/StatsUsage/Get.phpsrc/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.phpsrc/Appwrite/Platform/Workers/Messaging.phpsrc/Appwrite/Platform/Workers/Migrations.phpsrc/Appwrite/Platform/Workers/Webhooks.phpsrc/Appwrite/Usage/Context.php
| ->inject('usage') | ||
| ->inject('transactionState') | ||
| ->inject('authorization') | ||
| ->callback($this->action(...)); | ||
| } | ||
|
|
||
| public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void | ||
| public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user, Context $usage, TransactionState $transactionState, Authorization $authorization): void |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's confirm the file exists and check the method signature
cat -n src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php | sed -n '70,90p'Repository: appwrite/appwrite
Length of output: 2415
🏁 Script executed:
#!/bin/bash
# Search for all usages of $user in the XList.php file
rg -n --type=php '\$user\b' src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpRepository: appwrite/appwrite
Length of output: 356
🏁 Script executed:
#!/bin/bash
# Check for ->inject('user') in the constructor
rg -n --type=php "->inject\('user'\)" src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpRepository: appwrite/appwrite
Length of output: 465
🏁 Script executed:
#!/bin/bash
# Get a broader view of the action method signature and implementation
cat -n src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php | sed -n '78,150p'Repository: appwrite/appwrite
Length of output: 4449
🏁 Script executed:
#!/bin/bash
# Search for any usage of $user in the entire method body after line 84
rg -n '\$user' src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php | head -20Repository: appwrite/appwrite
Length of output: 356
🏁 Script executed:
#!/bin/bash
# Get the rest of the method to check for any $user usage
wc -l src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpRepository: appwrite/appwrite
Length of output: 151
🏁 Script executed:
#!/bin/bash
# Check the entire action method (likely ends before next public/private method)
cat -n src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php | sed -n '84,250p'Repository: appwrite/appwrite
Length of output: 9213
Remove the unused $user injection and signature parameter.
The $user parameter is injected (line 77) but never used in the method body. Remove both ->inject('user') and the Document $user parameter from the method signature (line 84).
🧰 Tools
🪛 PHPMD (2.15.0)
[warning] 84-84: Avoid unused parameters such as '$user'. (undefined)
(UnusedFormalParameter)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php`
around lines 78 - 84, Remove the unused user injection and parameter: delete the
->inject('user') call from the route builder and remove the Document $user
parameter from the action method signature (public function action(... Document
$user, ...)), updating the parameter list order accordingly so other parameters
(e.g., Context $usage, TransactionState $transactionState, Authorization
$authorization) remain in place and types/signatures still match.
src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php
Outdated
Show resolved
Hide resolved
src/Appwrite/Usage/Context.php
Outdated
| return array_filter($this->metrics, function ($metric) { | ||
| foreach ($this->disabled as $disabledMetric) { | ||
| if (str_ends_with($metric['key'], $disabledMetric)) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
PHP 8.x array_filter preserve numeric keys sparse array
💡 Result:
In PHP 8.x, array_filter() preserves the original array keys (including numeric keys). If you filter an indexed array, you can end up with a sparse array (gaps in numeric indexes). The manual explicitly notes this, and suggests array_values() to reindex. [1]
$a = [10, 20, 30, 40];
$b = array_filter($a, fn($v) => $v > 20);
// $b is [2 => 30, 3 => 40] (sparse numeric keys)
$c = array_values($b);
// $c is [0 => 30, 1 => 40] (reindexed)If you want to filter by key, use the $mode flags like ARRAY_FILTER_USE_KEY / ARRAY_FILTER_USE_BOTH—they change what the callback receives, not the key-preservation behavior. [1]
Sources: [1]
🌐 Web query:
PHP json_encode sparse numeric array JSON object behavior
💡 Result:
In PHP, json_encode() decides whether to output a JSON array ([...]) or JSON object ({...}) based on the array’s keys:
- If the PHP array’s keys are a continuous, zero-based integer sequence (
0,1,2,...), it encodes as a JSON array. - If the keys are non-sequential (sparse), don’t start at 0, or include any string key, it encodes as a JSON object, and the keys appear as strings in JSON (because JSON object member names are strings). (php.net)
Example (sparse numeric keys become an object): (php.net)
$a = ["first", "second", "third"];
unset($a[1]);
echo json_encode($a); // {"0":"first","2":"third"}How to force JSON array output
Reindex before encoding:
echo json_encode(array_values($a)); // ["first","third"]array_values() discards the original keys and rebuilds 0..n-1, causing json_encode() to emit a JSON array. (stackoverflow.com)
If you actually want an object
You can explicitly force object encoding:
echo json_encode($a, JSON_FORCE_OBJECT);This preserves keys (as JSON object properties) but produces {...} even for sequential arrays. (app.studyraid.com)
Citations:
- 1: https://www.php.net/manual/en/function.json-decode.php?utm_source=openai
- 2: https://www.php.net/manual/en/function.json-decode.php?utm_source=openai
- 3: https://stackoverflow.com/questions/11195692/how-to-json-encode-a-php-array-as-a-json-array-not-a-json-object?utm_source=openai
- 4: https://app.studyraid.com/en/read/15428/535798/preserving-array-types-during-json-conversion?utm_source=openai
Reindex filtered metrics to maintain JSON array shape.
array_filter() preserves numeric keys, so filtering metrics leaves sparse arrays with gaps. When json_encode() serializes sparse numeric-key arrays, they become JSON objects instead of arrays, changing the message shape for downstream consumers.
Proposed fix
public function getMetrics(): array
{
- return array_filter($this->metrics, function ($metric) {
+ return array_values(array_filter($this->metrics, function ($metric) {
foreach ($this->disabled as $disabledMetric) {
if (str_ends_with($metric['key'], $disabledMetric)) {
return false;
}
}
return true;
- });
+ }));
}Wrap array_filter() with array_values() to reindex the result and ensure it always serializes as a JSON array.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return array_filter($this->metrics, function ($metric) { | |
| foreach ($this->disabled as $disabledMetric) { | |
| if (str_ends_with($metric['key'], $disabledMetric)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }); | |
| return array_values(array_filter($this->metrics, function ($metric) { | |
| foreach ($this->disabled as $disabledMetric) { | |
| if (str_ends_with($metric['key'], $disabledMetric)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| })); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Appwrite/Usage/Context.php` around lines 61 - 68, The filtered metrics
use array_filter in the return from Context.php which preserves original numeric
keys and can produce sparse arrays that json_encode serializes as objects; fix
by wrapping the array_filter result with array_values so the returned value from
the function (the filtered metrics) is reindexed and always serializes as a JSON
array (update the return that currently calls array_filter on $this->metrics).
There was a problem hiding this comment.
Pull request overview
Refactors “usage” metric recording/publishing to separate metric accumulation (via a new Appwrite\Usage\Context) from queue publishing (via new Appwrite\Event\Publisher\Usage + Appwrite\Event\Message\Usage), and updates many HTTP endpoints/workers to use the new wiring.
Changes:
- Introduces
Appwrite\Usage\Contextfor accumulating metrics/reduce docs and filtering disabled metrics. - Adds a publisher/message abstraction for usage queue publishing (
Event\Publisher\Usage,Event\Message\Usage). - Rewires many HTTP routes, workers, health endpoints, and bootstrap resources to inject
usage/publisherForUsageinstead ofqueueForStatsUsage.
Reviewed changes
Copilot reviewed 48 out of 48 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Appwrite/Usage/Context.php | New usage metric accumulation context. |
| src/Appwrite/Platform/Workers/Webhooks.php | Worker switched to Context injection for usage. |
| src/Appwrite/Platform/Workers/Migrations.php | Worker partially updated to publish via new usage message/publisher. |
| src/Appwrite/Platform/Workers/Messaging.php | Worker switched to Context injection for usage. |
| src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php | HTTP endpoint switched to Context for usage metrics. |
| src/Appwrite/Platform/Modules/Health/Http/Health/Queue/StatsUsage/Get.php | Health endpoint now uses publisherForUsage->getSize(). |
| src/Appwrite/Platform/Modules/Health/Http/Health/Queue/Failed/Get.php | Failed-queue endpoint maps stats-usage to usage publisher. |
| src/Appwrite/Platform/Modules/Functions/Workers/Builds.php | Builds worker switched to Context injection for usage. |
| src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php | Function execution endpoint switched to Context for usage metrics. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php | TablesDB transaction update now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php | TablesDB rows list now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php | TablesDB rows upsert now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php | TablesDB rows update now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php | TablesDB rows get now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php | TablesDB rows delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php | TablesDB rows create now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php | TablesDB column increment now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php | TablesDB column decrement now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php | TablesDB bulk upsert now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php | TablesDB bulk update now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php | TablesDB bulk delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Delete.php | TablesDB delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php | DB transaction update switched to Context injection. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php | DB delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php | Documents list now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php | Document upsert now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php | Document update now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php | Document get now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php | Document delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php | Document create now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php | Bulk document upsert now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php | Bulk document update now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php | Bulk document delete now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php | Attribute increment now injects usage. |
| src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php | Attribute decrement now injects usage. |
| src/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.php | Screenshots endpoint now injects usage. |
| src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php | MFA challenge creation now injects usage. |
| src/Appwrite/Event/Publisher/Usage.php | New publisher wrapper for the usage queue. |
| src/Appwrite/Event/Publisher/Base.php | New base publisher abstraction. |
| src/Appwrite/Event/Message/Usage.php | New usage queue message DTO/serializer. |
| src/Appwrite/Event/Message/Base.php | New base message abstraction. |
| src/Appwrite/Bus/Listeners/Usage.php | Listener updated to publish via new message/publisher or accumulate in context. |
| app/worker.php | Worker bootstrap now registers usage + publisherForUsage resources. |
| app/init/resources.php | HTTP bootstrap now registers usage + publisherForUsage and rewires DB usage listener. |
| app/controllers/shared/api.php | API shutdown hook now publishes accumulated usage via publisherForUsage. |
| app/controllers/api/account.php | Account controller routes switched to Context injection for usage. |
| app/cli.php | CLI resource renamed to usage (currently still returns StatsUsage). |
| CONTRIBUTING.md | Docs updated for new context/publisher usage flow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $usage | ||
| ->setProject($project) | ||
| ->trigger(); |
There was a problem hiding this comment.
Context doesn't define setProject()/trigger() (those were on Event\StatsUsage). This will fatally error at runtime. For workers, inject publisherForUsage, build an Event\Message\Usage from the context (project/metrics/reduce), enqueue it, and reset the context (or switch back to StatsUsage).
| metrics: $usage->getMetrics(), | ||
| reduce: $usage->getReduce() | ||
| ); | ||
| $publisherForUsage->enqueue($message); |
There was a problem hiding this comment.
$publisherForUsage is used here but is not defined in this method's scope (and not passed as a parameter), which will cause an undefined variable error. Pass the publisher into processMigrationResourceStats(...) (or store it on the class) and use that reference to enqueue the message.
| @@ -1182,7 +1182,7 @@ protected function buildDeployment( | |||
| resource:$resource, | |||
| deployment: $deployment, | |||
| project: $project, | |||
| queue: $queueForStatsUsage | |||
| queue: $usage | |||
There was a problem hiding this comment.
sendUsage(...) is still type-hinted to accept StatsUsage, but the caller now passes a Context ($usage). This will cause a type error/undefined class issue (the StatsUsage import was removed) and the method still relies on trigger(). Update sendUsage to work with Context + publisherForUsage (publish and reset), or keep using StatsUsage consistently.
app/cli.php
Outdated
| return $publisher; | ||
| }, ['publisher']); | ||
| $setResource('queueForStatsUsage', function (Publisher $publisher) { | ||
| $setResource('usage', function (Publisher $publisher) { |
There was a problem hiding this comment.
The usage resource is now named like the new Appwrite\Usage\Context, but it still returns Event\StatsUsage. This is inconsistent with the rest of the refactor (and will break commands that inject usage expecting a Context). Either keep this as queueForStatsUsage (returning StatsUsage) or update CLI resources to provide both usage (Context) and publisherForUsage (new publisher).
| $setResource('usage', function (Publisher $publisher) { | |
| $setResource('queueForStatsUsage', function (Publisher $publisher) { |
src/Appwrite/Event/Message/Usage.php
Outdated
| public function toArray(): array | ||
| { | ||
| return [ | ||
| 'project' => $this->project->getArrayCopy(), |
There was a problem hiding this comment.
Usage::toArray() serializes the full project document via getArrayCopy(). Previously, Event::trigger() trimmed the project payload down to $id, $sequence, and database (Event::trimPayload()), which avoids queue bloat and reduces the risk of leaking project configuration data. Consider trimming the serialized project fields here (or passing a trimmed Document into the message).
| 'project' => $this->project->getArrayCopy(), | |
| 'project' => [ | |
| '$id' => $this->project->getAttribute('$id'), | |
| '$sequence' => $this->project->getAttribute('$sequence'), | |
| 'database' => $this->project->getAttribute('database'), | |
| ], |
| $usage | ||
| ->setProject($project) | ||
| ->addMetric(METRIC_MESSAGES, ($deliveredTotal + $errorTotal)) |
There was a problem hiding this comment.
Context doesn't have setProject(). This code path will error after replacing StatsUsage with Context. Either remove setProject() usage here (accumulate only) and publish via the API shutdown hook (HTTP) or, since this is a worker, inject publisherForUsage and publish an Event\Message\Usage explicitly.
| use Utopia\System\System; | ||
| use Utopia\Validator\ArrayList; | ||
| use Utopia\Validator\Text; | ||
| use Appwrite\Usage\Context; |
There was a problem hiding this comment.
Duplicate use Appwrite\Usage\Context; import. In PHP this causes a fatal error (name already in use). Remove one of the two import statements.
| $usage | ||
| ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) | ||
| ->setProject($project) | ||
| ->trigger(); |
There was a problem hiding this comment.
Context doesn't define setProject()/trigger(). After replacing StatsUsage with Context, this will fail. For HTTP routes, just accumulate metrics in the context and let the API shutdown hook publish; for non-HTTP execution, publish via publisherForUsage and reset the context.
| ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) | ||
| ->setProject($project) | ||
| ->trigger(); |
There was a problem hiding this comment.
Context doesn't have setProject()/trigger(). This code will error after the switch from StatsUsage to Context. For HTTP requests, only accumulate metrics on $usage and rely on the shared API shutdown hook to publish them.
| ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) | |
| ->setProject($project) | |
| ->trigger(); | |
| ->addMetric(METRIC_AUTH_METHOD_PHONE, 1); |
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
app/controllers/shared/api.php (1)
964-988:⚠️ Potential issue | 🟠 MajorUsage publishing still bypasses
_APP_USAGE_STATSin API shutdown.This same issue persists: Line 995 checks the
usageroute group, but Line 964-Line 988 publish usage from the generalapishutdown path without checking the global usage flag.🔧 Proposed fix
- // Publish usage metrics if context has data - if (!$usage->isEmpty()) { + $usageStatsEnabled = System::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled'; + // Publish usage metrics only when enabled and context has data + if ($usageStatsEnabled && !$usage->isEmpty()) { $metrics = $usage->getMetrics(); // Filter out API key disabled metrics using suffix pattern matching $disabledMetrics = $apiKey?->getDisabledMetrics() ?? []; if (!empty($disabledMetrics)) { @@ return true; })); } + + if (empty($metrics) && empty($usage->getReduce())) { + return; + } $message = new \Appwrite\Event\Message\Usage( project: $project, metrics: $metrics, reduce: $usage->getReduce() ); $publisherForUsage->enqueue($message); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/controllers/shared/api.php` around lines 964 - 988, The shutdown path publishes usage metrics regardless of the global usage toggle; update the block that builds and enqueues the Usage message (references: $usage, $usage->isEmpty(), $usage->getMetrics(), $usage->getReduce(), $apiKey?->getDisabledMetrics(), $publisherForUsage->enqueue) to first check the global flag (e.g. _APP_USAGE_STATS or the existing configuration getter used elsewhere for the "usage" route) and skip building/enqueueing when usage publishing is disabled; ensure the same feature-flag check used in the `usage` route group is applied here before filtering metrics and creating the \Appwrite\Event\Message\Usage message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/controllers/shared/api.php`:
- Line 40: The new import Appwrite\Usage\Context is out of PSR-12 alphabetical
order; update the use statements in app/controllers/shared/api.php so imports
are alphabetically ordered (and grouped) per PSR-12, placing
Appwrite\Usage\Context in its correct position among other Appwrite\* imports;
adjust any empty lines between groups if necessary to match the project's import
grouping conventions.
In `@app/init/resources.php`:
- Line 74: The new import "UsageContext" is out of PSR-12 ordered_imports order
and causes lint to fail; reorder the use statements in app/init/resources.php so
that "use Appwrite\Usage\Context as UsageContext;" is placed alphabetically
among the other use/import lines (keeping the same alias UsageContext) to
satisfy PSR-12 ordering and rerun the linter.
- Around line 153-156: The publisherForUsage resource currently constructs the
queue with new \Utopia\Queue\Queue($queueName) which omits the driver and can
cause mismatched backends; update the factory for publisherForUsage to pass the
explicit 'utopia-queue' driver when instantiating \Utopia\Queue\Queue (i.e.,
call the constructor with the driver parameter alongside $queueName) so it
matches the other instantiations in app/worker.php and app/cli.php and ensures
the Usage publisher and workers use the same queue backend.
In `@app/worker.php`:
- Line 50: The PSR-12 linter flags an out-of-order import: move the
Appwrite\Usage\Context use statement so imports are alphabetically ordered and
grouped consistently with the existing import block; update the import list near
where Context is declared (the use Appwrite\Usage\Context line) to its correct
position among other Appwrite\* imports to satisfy ordered_imports.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Around line 411-414: The code calls undefined methods setProject() and
trigger() on the Appwrite\Usage\Context instance ($usage) which will fatal;
change the chain so you only call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1)
(i.e., remove ->setProject($project)->trigger()) in Create.php (function
handling membership creation) and apply the same fix in
src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php
where the identical pattern appears.
---
Duplicate comments:
In `@app/controllers/shared/api.php`:
- Around line 964-988: The shutdown path publishes usage metrics regardless of
the global usage toggle; update the block that builds and enqueues the Usage
message (references: $usage, $usage->isEmpty(), $usage->getMetrics(),
$usage->getReduce(), $apiKey?->getDisabledMetrics(),
$publisherForUsage->enqueue) to first check the global flag (e.g.
_APP_USAGE_STATS or the existing configuration getter used elsewhere for the
"usage" route) and skip building/enqueueing when usage publishing is disabled;
ensure the same feature-flag check used in the `usage` route group is applied
here before filtering metrics and creating the \Appwrite\Event\Message\Usage
message.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9b8b29ef-948f-4d52-ab07-69516a7235eb
📒 Files selected for processing (11)
app/cli.phpapp/controllers/shared/api.phpapp/init/resources.phpapp/worker.phpsrc/Appwrite/Event/Publisher/Base.phpsrc/Appwrite/Event/Publisher/Usage.phpsrc/Appwrite/Event/StatsUsage.phpsrc/Appwrite/Platform/Modules/Functions/Workers/Builds.phpsrc/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.phpsrc/Appwrite/Usage/Context.phptests/e2e/General/UsageTest.php
💤 Files with no reviewable changes (1)
- src/Appwrite/Event/StatsUsage.php
🚧 Files skipped from review as they are similar to previous changes (2)
- src/Appwrite/Event/Publisher/Base.php
- src/Appwrite/Event/Publisher/Usage.php
src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php
Outdated
Show resolved
Hide resolved
🔄 PHP-Retry SummaryFlaky tests detected across commits: Commit
|
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.28s | Logs |
RealtimeCustomClientQueryTest::testDatabaseChannelWithQuery |
1 | 42.57s | Logs |
RealtimeCustomClientTest::testChannelsTablesDB |
1 | 30.27s | Logs |
Commit 9179be3 - 5 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.27s | Logs |
LegacyConsoleClientTest::testEnforceCollectionAndDocumentPermissions |
1 | 240.38s | Logs |
LegacyCustomServerTest::testAttributeResponseModels |
1 | 240.79s | Logs |
TablesDBCustomClientTest::testSpatialColCreateOnExistingDataWithDefaults |
1 | 241.26s | Logs |
TablesDBTransactionPermissionsCustomClientTest::testCollectionCreatePermissionDenied |
1 | 241.29s | Logs |
Commit 704fbe0 - 2 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.28s | Logs |
TablesDBTransactionsConsoleClientTest::testCommit |
1 | 240.66s | Logs |
Commit a804cba - 1 flaky test
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.26s | Logs |
Commit 0295e27 - 4 flaky tests
| Test | Retries | Total Time | Details |
|---|---|---|---|
UsageTest::testDatabaseStatsCollectionsAPI |
1 | 10.34s | Logs |
TablesDBCustomServerTest::testInvalidDocumentStructure |
1 | 241.73s | Logs |
LegacyTransactionsConsoleClientTest::testBulkUpsert |
1 | 241.08s | Logs |
LegacyTransactionsCustomClientTest::testConcurrentTransactionConflicts |
1 | 240.86s | Logs |
Note: Flaky test results are tracked for the last 5 commits
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/e2e/General/UsageTest.php (1)
46-77:⚠️ Potential issue | 🟡 MinorRemove duplicate trait alias declarations.
Several SitesBase method aliases are declared twice. While PHP accepts duplicate aliases, this appears to be a copy-paste error.
Duplicate lines:
- Line 51 duplicates line 50 (
listVariables as listVariablesSite)- Line 53 duplicates line 52 (
updateVariable as updateVariableSite)- Line 55 duplicates line 54 (
deleteVariable as deleteVariableSite)- Line 57 duplicates line 56 (
getDeployment as getDeploymentSite)- Line 59 duplicates line 58 (
listDeployments as listDeploymentsSite)- Line 61 duplicates line 60 (
deleteDeployment as deleteDeploymentSite)- Line 63 duplicates line 62 (
setupDuplicateDeployment as setupDuplicateDeploymentSite)- Line 65 duplicates line 64 (
createDuplicateDeployment as createDuplicateDeploymentSite)- Line 67 duplicates line 66 (
createTemplateDeployment as createTemplateDeploymentSite)- Line 69 duplicates line 68 (
getUsage as getUsageSite)- Line 71 duplicates line 70 (
getTemplate as getTemplateSite)- Line 73 duplicates line 72 (
getDeploymentDownload as getDeploymentDownloadSite)- Line 75 duplicates line 74 (
cancelDeployment as cancelDeploymentSite)🧹 Proposed fix to remove duplicates
SitesBase::createDeployment as createDeploymentSite; SitesBase::setupDeployment as setupDeploymentSite; SitesBase::createVariable as createVariableSite; SitesBase::getVariable as getVariableSite; SitesBase::listVariables as listVariablesSite; - SitesBase::listVariables as listVariablesSite; SitesBase::updateVariable as updateVariableSite; - SitesBase::updateVariable as updateVariableSite; SitesBase::deleteVariable as deleteVariableSite; - SitesBase::deleteVariable as deleteVariableSite; SitesBase::getDeployment as getDeploymentSite; - SitesBase::getDeployment as getDeploymentSite; SitesBase::listDeployments as listDeploymentsSite; - SitesBase::listDeployments as listDeploymentsSite; SitesBase::deleteDeployment as deleteDeploymentSite; - SitesBase::deleteDeployment as deleteDeploymentSite; SitesBase::setupDuplicateDeployment as setupDuplicateDeploymentSite; - SitesBase::setupDuplicateDeployment as setupDuplicateDeploymentSite; SitesBase::createDuplicateDeployment as createDuplicateDeploymentSite; - SitesBase::createDuplicateDeployment as createDuplicateDeploymentSite; SitesBase::createTemplateDeployment as createTemplateDeploymentSite; - SitesBase::createTemplateDeployment as createTemplateDeploymentSite; SitesBase::getUsage as getUsageSite; - SitesBase::getUsage as getUsageSite; SitesBase::getTemplate as getTemplateSite; - SitesBase::getTemplate as getTemplateSite; SitesBase::getDeploymentDownload as getDeploymentDownloadSite; - SitesBase::getDeploymentDownload as getDeploymentDownloadSite; SitesBase::cancelDeployment as cancelDeploymentSite; - SitesBase::cancelDeployment as cancelDeploymentSite; SitesBase::listSpecifications as listSpecificationsSite; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/General/UsageTest.php` around lines 46 - 77, The trait alias block contains duplicate alias declarations from SitesBase (e.g., "listVariables as listVariablesSite", "updateVariable as updateVariableSite", "deleteVariable as deleteVariableSite", "getDeployment as getDeploymentSite", "listDeployments as listDeploymentsSite", "deleteDeployment as deleteDeploymentSite", "setupDuplicateDeployment as setupDuplicateDeploymentSite", "createDuplicateDeployment as createDuplicateDeploymentSite", "createTemplateDeployment as createTemplateDeploymentSite", "getUsage as getUsageSite", "getTemplate as getTemplateSite", "getDeploymentDownload as getDeploymentDownloadSite", "cancelDeployment as cancelDeploymentSite"); remove the duplicated lines so each alias appears only once in the trait use block referencing SitesBase, leaving a single alias declaration for each unique method.
♻️ Duplicate comments (4)
src/Appwrite/Platform/Workers/Migrations.php (1)
107-108:⚠️ Potential issue | 🔴 CriticalCritical:
$publisherForUsagenot propagated through the call chain.The
$publisherForUsageparameter is received inaction()(line 108) but is never passed toprocessMigration()(lines 148-155), which in turn doesn't pass it toprocessMigrationResourceStats()(line 798). However, line 853 attempts to use$publisherForUsage->enqueue($message), which will cause a fatal "undefined variable" error when completed migrations attempt to publish usage statistics.Required fix to propagate $publisherForUsage
$this->processMigration( $migration, $queueForRealtime, $queueForMails, $usage, + $publisherForUsage, $platform, $authorization ); @@ protected function processMigration( Document $migration, Realtime $queueForRealtime, Mail $queueForMails, Context $usage, + \Appwrite\Event\Publisher\Usage $publisherForUsage, array $platform, Authorization $authorization, ): void { @@ $this->processMigrationResourceStats( $resource, $usage, $project, $migration->getAttribute('source'), $authorization, - $migration->getAttribute('resourceId') + $migration->getAttribute('resourceId'), + $publisherForUsage ); @@ - private function processMigrationResourceStats(array $resources, Context $usage, Document $projectDocument, string $source, Authorization $authorization, ?string $resourceId) + private function processMigrationResourceStats(array $resources, Context $usage, Document $projectDocument, string $source, Authorization $authorization, ?string $resourceId, \Appwrite\Event\Publisher\Usage $publisherForUsage): voidAlso applies to: 148-155, 340-340, 798-798, 848-854
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Migrations.php` around lines 107 - 108, The $publisherForUsage parameter passed into action() is not forwarded and thus is undefined where used; update the call chain to accept and propagate this dependency: add a $publisherForUsage parameter to processMigration() and any intermediate callers (and signatures that currently call processMigration()), and then pass that same $publisherForUsage into processMigrationResourceStats(); finally ensure processMigrationResourceStats() signature accepts and uses $publisherForUsage when calling ->enqueue($message). Update all affected function signatures and call sites (action(), processMigration(), processMigrationResourceStats() and any callers) to include and forward $publisherForUsage.src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php (1)
411-414:⚠️ Potential issue | 🔴 CriticalRemove invalid method chain on
Contextusage accumulator.Line 413 and Line 414 call methods that do not belong to
Appwrite\Usage\Context, which will fatal on the phone-invite flow.Proposed fix
$usage - ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) - ->setProject($project) - ->trigger(); + ->addMetric(METRIC_AUTH_METHOD_PHONE, 1);#!/bin/bash set -euo pipefail echo "=== Context API surface ===" rg -n --type=php 'function (addMetric|setProject|trigger)\(' src/Appwrite/Usage/Context.php echo echo "=== Usage chain in memberships create ===" sed -n '405,416p' src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php | cat -n🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php` around lines 411 - 414, The chain on the Context accumulator ($usage->addMetric(...)->setProject(...)->trigger()) is invalid because Appwrite\Usage\Context does not implement setProject or trigger; remove the chained calls and only call the methods that exist on the Context instance (e.g., call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1);). If you need to set the project or trigger processing, invoke those operations using the correct API/instance that exposes setProject/trigger (consult Appwrite\Usage\Context class for the proper methods) rather than chaining them off $usage.src/Appwrite/Platform/Workers/Messaging.php (1)
311-323:⚠️ Potential issue | 🔴 Critical
Contextis used with removed publish APIs (setProject/trigger) and can fatal at runtime.Line 312 and Line 322 call methods that are not part of the new accumulator-style
Appwrite\Usage\Context. This external-send path will break when executed. Keep this block to metric accumulation only, then publish viaAppwrite\Event\Publisher\Usage+Appwrite\Event\Message\Usageat worker completion.Suggested refactor direction
- $usage - ->setProject($project) - ->addMetric(...) - ... - ->trigger(); + $usage + ->addMetric(...) + ... + ; + + // after processing (once): + // $publisherForUsage->enqueue(new UsageMessage( + // project: $project, + // metrics: $usage->getMetrics(), + // reduce: $usage->getReduce() + // )); + // $usage->reset();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Messaging.php` around lines 311 - 323, The block using $usage currently calls removed publish APIs setProject() and trigger()—remove those calls and keep only the accumulation of metrics on $usage (the addMetric(...) calls that reference METRIC_MESSAGES, METRIC_MESSAGES_SENT, METRIC_MESSAGES_FAILED, METRIC_MESSAGES_TYPE*, METRIC_MESSAGES_TYPE_PROVIDER* and use $provider->getAttribute('type') and ->getAttribute('provider') as well as $deliveredTotal/$errorTotal); then ensure publishing is done later at worker completion via the new publisher APIs Appwrite\Event\Publisher\Usage and Appwrite\Event\Message\Usage (i.e. stop calling ->setProject(...) and ->trigger() here and move publication to the worker teardown using the Usage publishers).app/init/resources.php (1)
154-159:⚠️ Potential issue | 🟠 MajorUse consistent queue driver for
publisherForUsage.The
Queueconstructor at line 156 is missing the'utopia-queue'driver parameter. Based on past review comments, bothapp/worker.phpandapp/cli.phpinstantiate the same resource with'utopia-queue', which could cause usage messages to be published to a different backend than the workers consume from.Suggested fix
Http::setResource('publisherForUsage', function (Publisher $publisher) { $queueName = System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME); - $queue = new \Utopia\Queue\Queue($queueName); + $queue = new \Utopia\Queue\Queue($queueName, 'utopia-queue'); return new \Appwrite\Event\Publisher\Usage($publisher, $queue); }, ['publisher']);,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/init/resources.php` around lines 154 - 159, The publisherForUsage resource constructs a Queue without the driver, causing inconsistent backends; update the factory that returns new \Appwrite\Event\Publisher\Usage in publisherForUsage so the \Utopia\Queue\Queue is instantiated with the explicit 'utopia-queue' driver (i.e. pass the driver as the second argument to the Queue constructor) so the Usage publisher uses the same queue driver as app/worker.php and app/cli.php.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php`:
- Around line 210-213: The code is calling nonexistent Context methods
setProject() and trigger() on the $usage object after addMetric, causing a fatal
error; update the usage accumulation to only call
$usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) (no chaining) so metrics are
added to the Context and rely on the existing shutdown/completion flow to
publish metrics, keeping references to $usage, addMetric,
METRIC_AUTH_METHOD_PHONE and $project intact but removing setProject() and
trigger() calls.
In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`:
- Line 1256: Remove the unused local variable $key by deleting the assignment
line that sets $key = $resource->getAttribute('runtime'); in the Builds.php
worker code (the assignment is the only use of the local $key variable); if the
runtime attribute is needed elsewhere, access it directly where required (e.g.,
use $resource->getAttribute('runtime') inline) otherwise delete the assignment
to eliminate the dead variable.
In `@src/Appwrite/Platform/Workers/Webhooks.php`:
- Around line 196-198: The code incorrectly uses Context-style fluent calls
($usage->setProject(...)->trigger()) which don't exist for the new
Appwrite\Usage\Context accumulator; replace these with accumulating metrics via
the Context instance's addMetric() calls and then publish using
publisherForUsage with a Message\Usage payload. Specifically, stop calling
setProject and trigger on $usage, instead call addMetric(...) on the
Context/usage accumulator to register the project-related metric(s) and invoke
publisherForUsage(...)->publish(new Message\Usage(...)) (or the existing
publisher API) to emit the usage message. Ensure you reference the Context
accumulator variable ($usage), the addMetric() method for accumulation, and
publisherForUsage plus Message\Usage for publishing so webhook execution no
longer relies on nonexistent setProject/trigger methods.
In `@src/Appwrite/Usage/Context.php`:
- Around line 16-24: The Context object is being used with non-existent methods
setProject() and trigger() in Memberships/Create.php; replace that flow with the
established usage pattern: accumulate metrics on the Context via addMetric() and
addReduce(), then build a Usage message using $context->getMetrics() and
$context->getReduce(), enqueue it using $publisherForUsage->enqueue($message),
and finally call $context->reset(); ensure you remove calls to setProject() and
trigger() and follow the same sequence as other modules (see Context::addMetric,
Context::addReduce, Context::getMetrics, Context::getReduce, Context::reset and
publisherForUsage->enqueue for reference).
In `@tests/e2e/General/UsageTest.php`:
- Around line 188-189: The #[Depends] attribute values reference old camelCase
test names and must be updated to the actual snake_case method names so PHPUnit
can resolve dependencies; update each attribute (e.g., change
#[Depends('testPrepareUsersStats')] to #[Depends('test_prepare_users_stats')]
and similarly for testUsersStats → test_users_stats, testPrepareStorageStats →
test_prepare_storage_stats, testStorageStats → test_storage_stats,
testPrepareDatabaseStatsCollectionsAPI →
test_prepare_database_stats_collections_api, testDatabaseStatsCollectionsAPI →
test_database_stats_collections_api, testPrepareDatabaseStatsTablesAPI →
test_prepare_database_stats_tables_api, testDatabaseStatsTablesAPI →
test_database_stats_tables_api, testPrepareFunctionsStats →
test_prepare_functions_stats, testPrepareSitesStats → test_prepare_sites_stats,
and testFunctionsStats → test_functions_stats so each #[Depends] string exactly
matches its corresponding test method name (look for the attributes near the
methods test_users_stats, test_prepare_users_stats, test_storage_stats,
test_prepare_storage_stats, test_database_stats_collections_api,
test_prepare_database_stats_collections_api, test_database_stats_tables_api,
test_prepare_database_stats_tables_api, test_functions_stats,
test_prepare_functions_stats, and test_prepare_sites_stats).
---
Outside diff comments:
In `@tests/e2e/General/UsageTest.php`:
- Around line 46-77: The trait alias block contains duplicate alias declarations
from SitesBase (e.g., "listVariables as listVariablesSite", "updateVariable as
updateVariableSite", "deleteVariable as deleteVariableSite", "getDeployment as
getDeploymentSite", "listDeployments as listDeploymentsSite", "deleteDeployment
as deleteDeploymentSite", "setupDuplicateDeployment as
setupDuplicateDeploymentSite", "createDuplicateDeployment as
createDuplicateDeploymentSite", "createTemplateDeployment as
createTemplateDeploymentSite", "getUsage as getUsageSite", "getTemplate as
getTemplateSite", "getDeploymentDownload as getDeploymentDownloadSite",
"cancelDeployment as cancelDeploymentSite"); remove the duplicated lines so each
alias appears only once in the trait use block referencing SitesBase, leaving a
single alias declaration for each unique method.
---
Duplicate comments:
In `@app/init/resources.php`:
- Around line 154-159: The publisherForUsage resource constructs a Queue without
the driver, causing inconsistent backends; update the factory that returns new
\Appwrite\Event\Publisher\Usage in publisherForUsage so the \Utopia\Queue\Queue
is instantiated with the explicit 'utopia-queue' driver (i.e. pass the driver as
the second argument to the Queue constructor) so the Usage publisher uses the
same queue driver as app/worker.php and app/cli.php.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Around line 411-414: The chain on the Context accumulator
($usage->addMetric(...)->setProject(...)->trigger()) is invalid because
Appwrite\Usage\Context does not implement setProject or trigger; remove the
chained calls and only call the methods that exist on the Context instance
(e.g., call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1);). If you need to set
the project or trigger processing, invoke those operations using the correct
API/instance that exposes setProject/trigger (consult Appwrite\Usage\Context
class for the proper methods) rather than chaining them off $usage.
In `@src/Appwrite/Platform/Workers/Messaging.php`:
- Around line 311-323: The block using $usage currently calls removed publish
APIs setProject() and trigger()—remove those calls and keep only the
accumulation of metrics on $usage (the addMetric(...) calls that reference
METRIC_MESSAGES, METRIC_MESSAGES_SENT, METRIC_MESSAGES_FAILED,
METRIC_MESSAGES_TYPE*, METRIC_MESSAGES_TYPE_PROVIDER* and use
$provider->getAttribute('type') and ->getAttribute('provider') as well as
$deliveredTotal/$errorTotal); then ensure publishing is done later at worker
completion via the new publisher APIs Appwrite\Event\Publisher\Usage and
Appwrite\Event\Message\Usage (i.e. stop calling ->setProject(...) and
->trigger() here and move publication to the worker teardown using the Usage
publishers).
In `@src/Appwrite/Platform/Workers/Migrations.php`:
- Around line 107-108: The $publisherForUsage parameter passed into action() is
not forwarded and thus is undefined where used; update the call chain to accept
and propagate this dependency: add a $publisherForUsage parameter to
processMigration() and any intermediate callers (and signatures that currently
call processMigration()), and then pass that same $publisherForUsage into
processMigrationResourceStats(); finally ensure processMigrationResourceStats()
signature accepts and uses $publisherForUsage when calling ->enqueue($message).
Update all affected function signatures and call sites (action(),
processMigration(), processMigrationResourceStats() and any callers) to include
and forward $publisherForUsage.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2d1e1f6b-2665-4e04-8990-812222cfcdbe
📒 Files selected for processing (31)
app/cli.phpapp/controllers/api/account.phpapp/controllers/shared/api.phpapp/init/resources.phpapp/worker.phpsrc/Appwrite/Event/Message/Usage.phpsrc/Appwrite/Event/Publisher/Base.phpsrc/Appwrite/Event/Publisher/Usage.phpsrc/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.phpsrc/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Functions/Http/Executions/Create.phpsrc/Appwrite/Platform/Modules/Functions/Workers/Builds.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/StatsUsage/Get.phpsrc/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.phpsrc/Appwrite/Platform/Workers/Messaging.phpsrc/Appwrite/Platform/Workers/Migrations.phpsrc/Appwrite/Platform/Workers/Webhooks.phpsrc/Appwrite/Usage/Context.phptests/e2e/General/UsageTest.php
🚧 Files skipped from review as they are similar to previous changes (4)
- src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php
- src/Appwrite/Event/Publisher/Usage.php
- app/controllers/api/account.php
- src/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.php
src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php
Outdated
Show resolved
Hide resolved
| { | ||
| $runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []); | ||
| $key = $resource->getAttribute('runtime'); | ||
| $key = $resource->getAttribute('runtime'); |
There was a problem hiding this comment.
Remove unused local variable $key.
Line 1256 assigns $key but never uses it.
Proposed fix
- $key = $resource->getAttribute('runtime');
$runtime = match ($resource->getCollection()) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $key = $resource->getAttribute('runtime'); | |
| $runtime = match ($resource->getCollection()) { |
🧰 Tools
🪛 PHPMD (2.15.0)
[warning] 1256-1256: Avoid unused local variables such as '$key'. (undefined)
(UnusedLocalVariable)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php` at line 1256,
Remove the unused local variable $key by deleting the assignment line that sets
$key = $resource->getAttribute('runtime'); in the Builds.php worker code (the
assignment is the only use of the local $key variable); if the runtime attribute
is needed elsewhere, access it directly where required (e.g., use
$resource->getAttribute('runtime') inline) otherwise delete the assignment to
eliminate the dead variable.
e9a1923 to
38b2eed
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (7)
src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php (1)
416-419:⚠️ Potential issue | 🔴 Critical
Contexthas nosetProject()ortrigger()methods—this will cause a fatal error at runtime.The code still calls undefined methods on the
Contextinstance. Per the new pattern, onlyaddMetric()should be called. The publishing is handled separately by the shutdown hook viaPublisher\Usage.Proposed fix
- $usage - ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) - ->setProject($project) - ->trigger(); + $usage + ->addMetric(METRIC_AUTH_METHOD_PHONE, 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php` around lines 416 - 419, The code is calling undefined methods setProject() and trigger() on the $usage Context instance; remove those calls and only call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) so the Context usage metric is recorded and publishing is left to the shutdown hook (Publisher\Usage). Update the block around $usage (the Context instance used for usage metrics in Create.php) to stop chaining setProject() and trigger(), leaving a single addMetric(...) call.src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php (1)
77-84:⚠️ Potential issue | 🟡 MinorRemove the dead
$userdependency from this action.
Document $useris still unused after the usage-context swap, so->inject('user')just widens the signature and keeps PHPMD unhappy.🧹 Suggested cleanup
- ->inject('user') ->inject('usage') @@ - public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, Document $user, Context $usage, TransactionState $transactionState, Authorization $authorization): void + public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, int $ttl, UtopiaResponse $response, Database $dbForProject, Context $usage, TransactionState $transactionState, Authorization $authorization): void🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php` around lines 77 - 84, Remove the dead user dependency: delete the ->inject('user') call from the collection setup and remove the unused Document $user parameter from the action method signature (public function action(..., Context $usage, TransactionState $transactionState, Authorization $authorization): void) and any corresponding references, so the action no longer accepts or expects a user object and PHPMD warnings are resolved.src/Appwrite/Platform/Workers/Migrations.php (1)
148-155:⚠️ Potential issue | 🔴 CriticalPass the usage publisher through this call chain.
action()now receives$publisherForUsage, but this call still drops it. The completed-migration path later does$publisherForUsage->enqueue(...)out of scope inprocessMigrationResourceStats(), so successful migrations will crash as soon as they try to publish usage stats.🛠️ Proposed fix
$this->processMigration( $migration, $queueForRealtime, $queueForMails, $usage, + $publisherForUsage, $platform, $authorization ); @@ protected function processMigration( Document $migration, Realtime $queueForRealtime, Mail $queueForMails, Context $usage, + \Appwrite\Event\Publisher\Usage $publisherForUsage, array $platform, Authorization $authorization, ): void { @@ $this->processMigrationResourceStats( $resource, $usage, $project, $migration->getAttribute('source'), $authorization, - $migration->getAttribute('resourceId') + $migration->getAttribute('resourceId'), + $publisherForUsage ); @@ - private function processMigrationResourceStats(array $resources, Context $usage, Document $projectDocument, string $source, Authorization $authorization, ?string $resourceId) + private function processMigrationResourceStats( + array $resources, + Context $usage, + Document $projectDocument, + string $source, + Authorization $authorization, + ?string $resourceId, + \Appwrite\Event\Publisher\Usage $publisherForUsage, + ): void🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Migrations.php` around lines 148 - 155, The call to processMigration is not forwarding the new $publisherForUsage, causing action() to receive no publisher and later processMigrationResourceStats() to call $publisherForUsage->enqueue(...) on null; update the processMigration(...) invocation to include the $publisherForUsage argument and then propagate that parameter through processMigration's signature and any internal calls to action(), ensuring action() and processMigrationResourceStats() receive and use the same $publisherForUsage variable.src/Appwrite/Platform/Workers/Messaging.php (1)
311-323:⚠️ Potential issue | 🔴 CriticalReplace the old self-publishing usage API here.
This block still treats
ContextlikeStatsUsage. In the new design,Contextis only an accumulator, sosetProject()/trigger()are no longer the right API. This path will fail at runtime unless it builds a\Appwrite\Event\Message\Usageand enqueues it through\Appwrite\Event\Publisher\Usage.🛠️ Proposed fix
- ->inject('usage') + ->inject('usage') + ->inject('publisherForUsage') ->callback($this->action(...)); @@ - Device $deviceForFiles, - Context $usage + Device $deviceForFiles, + Context $usage, + \Appwrite\Event\Publisher\Usage $publisherForUsage ): void { @@ - $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $usage); + $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $usage, $publisherForUsage); @@ - Document $project, - Context $usage + Document $project, + Context $usage, + \Appwrite\Event\Publisher\Usage $publisherForUsage ): void { @@ - $usage - ->setProject($project) - ->addMetric(METRIC_MESSAGES, ($deliveredTotal + $errorTotal)) + $usage + ->addMetric(METRIC_MESSAGES, ($deliveredTotal + $errorTotal)) ->addMetric(METRIC_MESSAGES_SENT, $deliveredTotal) ->addMetric(METRIC_MESSAGES_FAILED, $errorTotal) ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE), ($deliveredTotal + $errorTotal)) ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE_SENT), $deliveredTotal) ->addMetric(str_replace('{type}', $provider->getAttribute('type'), METRIC_MESSAGES_TYPE_FAILED), $errorTotal) ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER), ($deliveredTotal + $errorTotal)) ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER_SENT), $deliveredTotal) - ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER_FAILED), $errorTotal) - ->trigger(); + ->addMetric(str_replace(['{type}', '{provider}'], [$provider->getAttribute('type'), $provider->getAttribute('provider')], METRIC_MESSAGES_TYPE_PROVIDER_FAILED), $errorTotal); + + $publisherForUsage->enqueue(new \Appwrite\Event\Message\Usage( + project: $project, + metrics: $usage->getMetrics(), + reduce: $usage->getReduce(), + )); + $usage->reset();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Messaging.php` around lines 311 - 323, The current block is still using the old StatsUsage API (calling setProject() and trigger() on the Context/usage object) which is invalid with the new Context-as-accumulator design; replace this by constructing a \Appwrite\Event\Message\Usage instance populated with project, metrics (METRIC_MESSAGES, METRIC_MESSAGES_SENT, METRIC_MESSAGES_FAILED, the type- and provider-scoped metrics) and totals (use $deliveredTotal/$errorTotal), then enqueue that message via \Appwrite\Event\Publisher\Usage instead of calling setProject()/trigger() on the Context/usage object; update the code that references setProject(), trigger(), and $usage to use the new Usage message and Publisher API.src/Appwrite/Platform/Workers/Webhooks.php (1)
38-39:⚠️ Potential issue | 🔴 Critical
Contextstill uses the removed self-publishing API here.
setProject()/trigger()do not belong to the new accumulator, so this will fatal on webhook delivery. Also,action()reuses the same$usageacross multiple matching webhooks, so publish once fromaction()viapublisherForUsagebefore the final exception check, or reset after each enqueue to avoid double-counting.🔧 One safe way to apply the new pattern
+use Appwrite\Event\Message\Usage as UsageMessage; +use Appwrite\Event\Publisher\Usage as UsagePublisher; use Appwrite\Usage\Context; @@ - ->inject('usage') + ->inject('publisherForUsage') + ->inject('usage') ->inject('log') ->inject('plan') ->callback($this->action(...)); @@ - public function action(Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, Context $usage, Log $log, array $plan): void + public function action(Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, UsagePublisher $publisherForUsage, Context $usage, Log $log, array $plan): void { $this->errors = []; @@ foreach ($project->getAttribute('webhooks', []) as $webhook) { if (array_intersect($webhook->getAttribute('events', []), $events)) { $this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForPlatform, $queueForMails, $usage, $plan); } } + + if ($usage->getMetrics() !== []) { + $publisherForUsage->enqueue(new UsageMessage( + project: $project, + metrics: $usage->getMetrics(), + reduce: $usage->getReduce(), + )); + $usage->reset(); + } if (!empty($this->errors)) { throw new Exception(\implode(" / \n\n", $this->errors)); } @@ - $usage - ->setProject($project) - ->trigger(); }#!/bin/bash set -euo pipefail echo "[Context API]" rg -n --type=php 'function\s+(addMetric|addReduce|getMetrics|getReduce|reset|setProject|trigger)\b' src/Appwrite/Usage/Context.php echo echo "[Current Webhooks calls that should disappear]" rg -n --type=php '\->(setProject|trigger)\(' src/Appwrite/Platform/Workers/Webhooks.php echo echo "[Current usage publisher pattern elsewhere in the repo]" rg -n --type=php 'publisherForUsage|new UsageMessage|->enqueue\(' app src/Appwrite | sed -n '1,120p'Expected result:
Context.phpshould expose accumulator methods likeaddMetric,getMetrics,getReduce, andreset, notsetProject/trigger;Webhooks.phpshould stop matching the second grep after the fix.Also applies to: 55-55, 200-202
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Webhooks.php` around lines 38 - 39, The Webhooks worker still calls the removed Context self-publishing API methods (setProject, trigger) and reuses the same $usage accumulator across multiple matching webhooks causing double-counting; update Webhooks.php to stop calling Context::setProject and Context::trigger and instead use the new accumulator methods (e.g., addMetric, getMetrics, getReduce, reset) on the Context/usage object, and ensure publishing happens once per webhook action by invoking publisherForUsage (or creating a new UsageMessage and enqueue) from action() before the final exception check or by resetting $usage after each enqueue to avoid reuse; locate references to setProject, trigger, action, publisherForUsage, and $usage in the file and replace with the new accumulator and publish pattern accordingly.tests/e2e/General/UsageTest.php (1)
188-188:⚠️ Potential issue | 🟠 MajorUpdate the stale
#[Depends]targets to the renamed snake_case tests.These attributes still point at the old camelCase method names, so PHPUnit will not resolve the dependency chain for the renamed tests.
🔧 Example fix pattern
- #[Depends('testPrepareUsersStats')] + #[Depends('test_prepare_users_stats')] public function test_users_stats(array $data): array - #[Depends('testPrepareStorageStats')] + #[Depends('test_prepare_storage_stats')] public function test_storage_stats(array $data): array - #[Depends('testFunctionsStats')] + #[Depends('test_functions_stats')] public function test_custom_domains_function_stats(array $data): voidApply the same snake_case rename to the remaining
#[Depends]strings in this file.#!/bin/bash set -euo pipefail # Verify that every #[Depends('...')] target exists as a declared method name. python - <<'PY' import pathlib, re path = pathlib.Path("tests/e2e/General/UsageTest.php") text = path.read_text() methods = set(re.findall(r'public function (\w+)\s*\(', text)) pairs = re.findall(r"#\[Depends\('([^']+)'\)\]\s*public function (\w+)", text, re.S) for dep, owner in pairs: status = "OK" if dep in methods else "MISSING" print(f"{owner}: depends on {dep} -> {status}") PYExpected result: every dependency reports
-> OK.Also applies to: 232-232, 365-365, 422-422, 597-597, 671-671, 852-852, 931-931, 1095-1095, 1233-1233, 1309-1309
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/General/UsageTest.php` at line 188, Update stale PHPUnit dependency attributes: replace camelCase Depends targets with the renamed snake_case test method names. Find occurrences like #[Depends('testPrepareUsersStats')] and change the string to the actual declared method name (e.g. test_prepare_users_stats) so PHPUnit can resolve dependencies; do the same for all other #[Depends('...')] entries in this file that still reference camelCase names, ensuring each Depends target exactly matches the corresponding public function method name.app/init/resources.php (1)
154-158:⚠️ Potential issue | 🟠 MajorPass the explicit queue driver into
publisherForUsage.Line 156 is still constructing the usage queue without the explicit
'utopia-queue'driver. If the worker/CLI side uses a driver-qualified queue, this publisher can write usage messages to a different backend than the consumers read from.Suggested fix
Http::setResource('publisherForUsage', function (Publisher $publisher) { $queueName = System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME); - $queue = new \Utopia\Queue\Queue($queueName); + $queue = new \Utopia\Queue\Queue($queueName, 'utopia-queue'); return new \Appwrite\Event\Publisher\Usage($publisher, $queue); }, ['publisher']);Verify that the usage queue is constructed consistently everywhere; expect
app/cli.phpandapp/worker.phpto pass'utopia-queue'explicitly while this factory does not.#!/bin/bash set -euo pipefail echo "=== publisherForUsage factory ===" sed -n '151,159p' app/init/resources.php echo echo "=== usage queue constructions across app/ ===" rg -n "STATS_USAGE_QUEUE_NAME|new \\\\Utopia\\\\Queue\\\\Queue\\(" app -C2 echo echo "=== usage publisher implementation ===" sed -n '1,160p' src/Appwrite/Event/Publisher/Usage.php🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/init/resources.php` around lines 154 - 158, The publisherForUsage factory currently constructs the queue with new \Utopia\Queue\Queue($queueName) which omits the explicit driver; update the Http::setResource('publisherForUsage') factory to construct the queue with the explicit 'utopia-queue' driver (e.g. pass the driver argument when creating \Utopia\Queue\Queue) so the returned new \Appwrite\Event\Publisher\Usage($publisher, $queue) writes to the same backend as the workers; mirror the same construction used in app/cli.php and app/worker.php to ensure consistency.
🧹 Nitpick comments (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php (1)
63-64: Drop the unusedusageinjection here.
action()never accepts aContext, so this route resolves a dependency it does not consume. Either wireContext $usageinto the callback or remove the injection; right now this reads like an incomplete migration.♻️ Suggested cleanup
- ->inject('usage') ->callback($this->action(...));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php` around lines 63 - 64, The route definition is injecting 'usage' but the handler method action() does not accept a Context or any parameter, so remove the unnecessary injection to avoid resolving an unused dependency; locate the route builder call that chains ->inject('usage') and ->callback($this->action(...)) and delete the ->inject('usage') call (or alternatively update the action method signature to accept Context $usage if you intend to use it), ensuring the callback and injection match.app/controllers/shared/api.php (1)
971-996: Skip enqueueing when API-key filtering removes every metric.After the suffix filter,
$metricscan be empty even though$usagewas not. Enqueuing an empty usage message just adds avoidable queue work at shutdown.Proposed fix
- $message = new \Appwrite\Event\Message\Usage( - project: $project, - metrics: $metrics, - reduce: $usage->getReduce() - ); - - $publisherForUsage->enqueue($message); + if (! empty($metrics)) { + $message = new \Appwrite\Event\Message\Usage( + project: $project, + metrics: $metrics, + reduce: $usage->getReduce() + ); + + $publisherForUsage->enqueue($message); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/controllers/shared/api.php` around lines 971 - 996, After filtering out disabled metrics, $metrics may become empty and you should avoid creating/enqueueing an empty usage message; update the block handling $usage (use symbols $metrics, $usage, $disabledMetrics, str_ends_with, \Appwrite\Event\Message\Usage, and $publisherForUsage->enqueue) to check if array is non-empty after the suffix filtering and only construct the Usage message and call $publisherForUsage->enqueue when $metrics is not empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CONTRIBUTING.md`:
- Around line 503-506: The snippet constructs a UsageMessage with project:
$project but never shows where $project comes from; before calling new
UsageMessage(...) add the line that obtains the project from the worker flow
(for example, retrieve it from the incoming message payload or worker context
such as $payload['project'] or $worker->getProject()), then pass that variable
into UsageMessage; update the example to show the project extraction step that
precedes UsageMessage so readers can copy the flow end-to-end and reference the
same symbol ($project) used in the constructor.
- Around line 494-500: The compute metrics are currently multiplied by 1000,
producing milliseconds while the docs expect seconds; update both occurrences
that set METRIC_BUILDS_COMPUTE and METRIC_FUNCTION_ID_BUILDS_COMPUTE so they use
the raw duration in seconds (e.g., (int)$build->getAttribute('duration', 0) or
equivalent) instead of multiplying by 1000, ensuring both the
METRIC_BUILDS_COMPUTE and METRIC_FUNCTION_ID_BUILDS_COMPUTE assignments are
changed.
- Around line 502-509: The publish block can skip $usage->reset() if
$publisherForUsage->enqueue($message) throws, causing stale metrics to leak into
the next worker job; wrap the enqueue call in a try/finally so that
$usage->reset() always runs (create the UsageMessage as before, call
$publisherForUsage->enqueue($message) inside the try, and call $usage->reset()
in the finally block) to ensure metrics are cleared even on enqueue failure.
In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`:
- Around line 1231-1239: The MB-seconds metrics currently multiply by
$spec['memory'] which underreports builds when memory was bumped to a minimum;
change those three metrics (METRIC_BUILDS_MB_SECONDS,
METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS,
METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS) to use the resolved memory value used
to create the runtime (i.e., the result of max($spec['memory'], $minMemory) –
the same resolved memory used when building the runtime) instead of
$spec['memory'], keeping the rest of the calculations (buildDuration, cpus)
unchanged and update both the resource-type and resource-id metric lines that
reference MB_SECONDS.
- Around line 396-403: The mv command is double-quoting arguments because the
code wraps escapeshellarg($from) and escapeshellarg($to) in extra double quotes,
causing literal quotes to be passed; update the Console::execute call in the
block handling $rootDirectory with spaces to call mv using the unquoted
escapeshellarg values (i.e., remove the surrounding " characters around
escapeshellarg($from) and escapeshellarg($to)) so mv receives correctly escaped,
quoted tokens for $from and $to.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Line 101: The action method in Create.php declares unused parameters
$timelimit and $plan; remove these two parameters from the action(string
$teamId, ...) signature in the Create class (and any related docblock/type
hints) so the method only accepts the used arguments, and update all call sites
that invoke Create::action to stop passing those two arguments; also update any
tests or factory/invocation code and imports that reference those parameters to
keep signatures consistent.
---
Duplicate comments:
In `@app/init/resources.php`:
- Around line 154-158: The publisherForUsage factory currently constructs the
queue with new \Utopia\Queue\Queue($queueName) which omits the explicit driver;
update the Http::setResource('publisherForUsage') factory to construct the queue
with the explicit 'utopia-queue' driver (e.g. pass the driver argument when
creating \Utopia\Queue\Queue) so the returned new
\Appwrite\Event\Publisher\Usage($publisher, $queue) writes to the same backend
as the workers; mirror the same construction used in app/cli.php and
app/worker.php to ensure consistency.
In
`@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php`:
- Around line 77-84: Remove the dead user dependency: delete the
->inject('user') call from the collection setup and remove the unused Document
$user parameter from the action method signature (public function action(...,
Context $usage, TransactionState $transactionState, Authorization
$authorization): void) and any corresponding references, so the action no longer
accepts or expects a user object and PHPMD warnings are resolved.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Around line 416-419: The code is calling undefined methods setProject() and
trigger() on the $usage Context instance; remove those calls and only call
$usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) so the Context usage metric is
recorded and publishing is left to the shutdown hook (Publisher\Usage). Update
the block around $usage (the Context instance used for usage metrics in
Create.php) to stop chaining setProject() and trigger(), leaving a single
addMetric(...) call.
In `@src/Appwrite/Platform/Workers/Messaging.php`:
- Around line 311-323: The current block is still using the old StatsUsage API
(calling setProject() and trigger() on the Context/usage object) which is
invalid with the new Context-as-accumulator design; replace this by constructing
a \Appwrite\Event\Message\Usage instance populated with project, metrics
(METRIC_MESSAGES, METRIC_MESSAGES_SENT, METRIC_MESSAGES_FAILED, the type- and
provider-scoped metrics) and totals (use $deliveredTotal/$errorTotal), then
enqueue that message via \Appwrite\Event\Publisher\Usage instead of calling
setProject()/trigger() on the Context/usage object; update the code that
references setProject(), trigger(), and $usage to use the new Usage message and
Publisher API.
In `@src/Appwrite/Platform/Workers/Migrations.php`:
- Around line 148-155: The call to processMigration is not forwarding the new
$publisherForUsage, causing action() to receive no publisher and later
processMigrationResourceStats() to call $publisherForUsage->enqueue(...) on
null; update the processMigration(...) invocation to include the
$publisherForUsage argument and then propagate that parameter through
processMigration's signature and any internal calls to action(), ensuring
action() and processMigrationResourceStats() receive and use the same
$publisherForUsage variable.
In `@src/Appwrite/Platform/Workers/Webhooks.php`:
- Around line 38-39: The Webhooks worker still calls the removed Context
self-publishing API methods (setProject, trigger) and reuses the same $usage
accumulator across multiple matching webhooks causing double-counting; update
Webhooks.php to stop calling Context::setProject and Context::trigger and
instead use the new accumulator methods (e.g., addMetric, getMetrics, getReduce,
reset) on the Context/usage object, and ensure publishing happens once per
webhook action by invoking publisherForUsage (or creating a new UsageMessage and
enqueue) from action() before the final exception check or by resetting $usage
after each enqueue to avoid reuse; locate references to setProject, trigger,
action, publisherForUsage, and $usage in the file and replace with the new
accumulator and publish pattern accordingly.
In `@tests/e2e/General/UsageTest.php`:
- Line 188: Update stale PHPUnit dependency attributes: replace camelCase
Depends targets with the renamed snake_case test method names. Find occurrences
like #[Depends('testPrepareUsersStats')] and change the string to the actual
declared method name (e.g. test_prepare_users_stats) so PHPUnit can resolve
dependencies; do the same for all other #[Depends('...')] entries in this file
that still reference camelCase names, ensuring each Depends target exactly
matches the corresponding public function method name.
---
Nitpick comments:
In `@app/controllers/shared/api.php`:
- Around line 971-996: After filtering out disabled metrics, $metrics may become
empty and you should avoid creating/enqueueing an empty usage message; update
the block handling $usage (use symbols $metrics, $usage, $disabledMetrics,
str_ends_with, \Appwrite\Event\Message\Usage, and $publisherForUsage->enqueue)
to check if array is non-empty after the suffix filtering and only construct the
Usage message and call $publisherForUsage->enqueue when $metrics is not empty.
In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php`:
- Around line 63-64: The route definition is injecting 'usage' but the handler
method action() does not accept a Context or any parameter, so remove the
unnecessary injection to avoid resolving an unused dependency; locate the route
builder call that chains ->inject('usage') and ->callback($this->action(...))
and delete the ->inject('usage') call (or alternatively update the action method
signature to accept Context $usage if you intend to use it), ensuring the
callback and injection match.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 66078bc2-c8ec-443b-a381-ea4354d7779f
📒 Files selected for processing (50)
CONTRIBUTING.mdapp/cli.phpapp/controllers/api/account.phpapp/controllers/shared/api.phpapp/init/resources.phpapp/worker.phpsrc/Appwrite/Bus/Listeners/Usage.phpsrc/Appwrite/Event/Message/Base.phpsrc/Appwrite/Event/Message/Usage.phpsrc/Appwrite/Event/Publisher/Base.phpsrc/Appwrite/Event/Publisher/Usage.phpsrc/Appwrite/Event/StatsUsage.phpsrc/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.phpsrc/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Functions/Http/Executions/Create.phpsrc/Appwrite/Platform/Modules/Functions/Workers/Builds.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/Failed/Get.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/StatsUsage/Get.phpsrc/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.phpsrc/Appwrite/Platform/Workers/Messaging.phpsrc/Appwrite/Platform/Workers/Migrations.phpsrc/Appwrite/Platform/Workers/Webhooks.phpsrc/Appwrite/Usage/Context.phptests/e2e/General/UsageTest.php
💤 Files with no reviewable changes (1)
- src/Appwrite/Event/StatsUsage.php
🚧 Files skipped from review as they are similar to previous changes (11)
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php
- src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php
- src/Appwrite/Event/Publisher/Base.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php
- src/Appwrite/Event/Message/Usage.php
- src/Appwrite/Usage/Context.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php
- src/Appwrite/Event/Message/Base.php
| // Publish the accumulated metrics (workers don't have shutdown hooks) | ||
| $message = new UsageMessage( | ||
| project: $project, | ||
| metrics: $usage->getMetrics(), | ||
| reduce: $usage->getReduce() | ||
| ); | ||
| $publisherForUsage->enqueue($message); | ||
| $usage->reset(); |
There was a problem hiding this comment.
Reset the usage context in a finally block.
If enqueue() fails here, $usage->reset() is skipped and the next worker job can inherit stale metrics. The guide should model this as try/finally to avoid cross-job leakage.
Proposed doc fix
// Publish the accumulated metrics (workers don't have shutdown hooks)
$message = new UsageMessage(
project: $project,
metrics: $usage->getMetrics(),
reduce: $usage->getReduce()
);
-$publisherForUsage->enqueue($message);
-$usage->reset();
+try {
+ $publisherForUsage->enqueue($message);
+} finally {
+ $usage->reset();
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Publish the accumulated metrics (workers don't have shutdown hooks) | |
| $message = new UsageMessage( | |
| project: $project, | |
| metrics: $usage->getMetrics(), | |
| reduce: $usage->getReduce() | |
| ); | |
| $publisherForUsage->enqueue($message); | |
| $usage->reset(); | |
| // Publish the accumulated metrics (workers don't have shutdown hooks) | |
| $message = new UsageMessage( | |
| project: $project, | |
| metrics: $usage->getMetrics(), | |
| reduce: $usage->getReduce() | |
| ); | |
| try { | |
| $publisherForUsage->enqueue($message); | |
| } finally { | |
| $usage->reset(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CONTRIBUTING.md` around lines 502 - 509, The publish block can skip
$usage->reset() if $publisherForUsage->enqueue($message) throws, causing stale
metrics to leak into the next worker job; wrap the enqueue call in a try/finally
so that $usage->reset() always runs (create the UsageMessage as before, call
$publisherForUsage->enqueue($message) inside the try, and call $usage->reset()
in the finally block) to ensure metrics are cleared even on enqueue failure.
cd9fe93 to
936d4ce
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php (1)
63-67:⚠️ Potential issue | 🟡 MinorMismatch between injected dependency and action signature.
Line 63 injects
'usage'but theaction()method signature at line 67 doesn't include aContext $usageparameter. This injection is effectively unused.If database deletion should track usage metrics, add the parameter:
- public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void + public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Context $usage): voidIf no metrics tracking is needed for this endpoint, remove the injection:
->inject('queueForEvents') - ->inject('usage') ->callback($this->action(...));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php` around lines 63 - 67, The route builder injects 'usage' but the handler signature for action() does not accept it, so either add a Context $usage parameter to the action method (public function action(string $databaseId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Context $usage): void) so the injected usage context is consumed when tracking deletion metrics, or remove the ->inject('usage') call from the route chain to avoid an unused injection; update references inside action() accordingly (use $usage for metrics if added, or delete any code assuming it if you remove the injection).src/Appwrite/Platform/Workers/Messaging.php (1)
311-322:⚠️ Potential issue | 🔴 Critical
Contextclass has nosetProject()ortrigger()methods—this will fatal at runtime.The
Appwrite\Usage\Contextclass only provides accumulation methods (addMetric(),addReduce(), etc.), not publishing methods. Lines 312 and 322 callsetProject()andtrigger()which don't exist and will cause fatal errors during message processing.For workers, the correct pattern (per the CONTRIBUTING.md documentation) is to:
- Accumulate metrics via
$usage->addMetric(...)- Construct a
UsageMessagewith the accumulated metrics- Publish via
$publisherForUsage->enqueue($message)- Reset with
$usage->reset()This worker needs
publisherForUsageinjected and the publishing logic refactored.🔧 Suggested refactor direction
public function __construct() { $this ->desc('Messaging worker') ->inject('message') ->inject('project') ->inject('log') ->inject('dbForProject') ->inject('deviceForFiles') ->inject('usage') + ->inject('publisherForUsage') ->callback($this->action(...)); }Then in the batch closure, replace:
- $usage - ->setProject($project) - ->addMetric(...) - ... - ->trigger(); + $usage + ->addMetric(...) + ...;And publish accumulated metrics at the end of
sendExternalMessage():$message = new UsageMessage( project: $project, metrics: $usage->getMetrics(), reduce: $usage->getReduce() ); try { $publisherForUsage->enqueue($message); } finally { $usage->reset(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Messaging.php` around lines 311 - 322, The code incorrectly calls non-existent Context methods setProject() and trigger() on $usage; replace this publish pattern in sendExternalMessage() by accumulating metrics with $usage->addMetric(...), then build a UsageMessage using UsageMessage(project: $project, metrics: $usage->getMetrics(), reduce: $usage->getReduce()), enqueue it via the injected publisherForUsage->enqueue($message) inside a try/finally, and call $usage->reset() in the finally block; remove calls to $usage->setProject() and $usage->trigger() and ensure publisherForUsage is injected into the worker.
♻️ Duplicate comments (13)
src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php (1)
77-84:⚠️ Potential issue | 🟡 MinorRemove the unused
$userinjection and parameter.The
$userparameter is injected (line 77) but never used in the method body. Remove both->inject('user')and theDocument $userparameter from the method signature.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php` around lines 77 - 84, Remove the unused user injection and parameter: delete the ->inject('user') call in the service builder chain and remove the Document $user parameter from the action(...) method signature (the action method defined as public function action(..., Document $user, Context $usage, ...)). Update any related parameter ordering/typing in the action declaration to keep Context $usage and subsequent parameters in the correct positions and ensure no references to $user remain in the method body.src/Appwrite/Platform/Workers/Migrations.php (1)
148-155:⚠️ Potential issue | 🔴 CriticalUndefined variable:
$publisherForUsagenot propagated through call chain.
$publisherForUsageis received inaction()(line 108) but never passed toprocessMigration()orprocessMigrationResourceStats(). At line 862,$publisherForUsage->enqueue($message)will fail with an undefined variable error when completed migrations attempt to publish usage statistics.Proposed fix
try { $this->processMigration( $migration, $queueForRealtime, $queueForMails, $usage, + $publisherForUsage, $platform, $authorization );protected function processMigration( Document $migration, Realtime $queueForRealtime, Mail $queueForMails, Context $usage, + \Appwrite\Event\Publisher\Usage $publisherForUsage, array $platform, Authorization $authorization, ): void {foreach ($aggregatedResources as $resource) { $this->processMigrationResourceStats( $resource, $usage, $project, $migration->getAttribute('source'), $authorization, - $migration->getAttribute('resourceId') + $migration->getAttribute('resourceId'), + $publisherForUsage ); }- private function processMigrationResourceStats(array $resources, Context $usage, Document $projectDocument, string $source, Authorization $authorization, ?string $resourceId) + private function processMigrationResourceStats(array $resources, Context $usage, Document $projectDocument, string $source, Authorization $authorization, ?string $resourceId, \Appwrite\Event\Publisher\Usage $publisherForUsage): voidAlso applies to: 346-352, 503-510, 807-807, 857-863
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Migrations.php` around lines 148 - 155, The variable $publisherForUsage from action() is not passed into processMigration() or processMigrationResourceStats(), causing an undefined variable when processMigrationResourceStats() tries to call $publisherForUsage->enqueue(...). Fix by adding a $publisherForUsage parameter to processMigration() and processMigrationResourceStats(), update all internal calls (e.g., where processMigration(...) is invoked) to pass the $publisherForUsage value through, and ensure any helper calls within processMigration that call processMigrationResourceStats also forward the same $publisherForUsage parameter so the enqueue call uses the propagated publisher instance.src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php (1)
209-212:⚠️ Potential issue | 🔴 CriticalFatal error:
setProject()andtrigger()do not exist onContext.The
Contextclass only providesaddMetric(),addReduce(),getMetrics(),getReduce(),isEmpty(), andreset()methods. Lines 211-212 callsetProject($project)->trigger()which will cause a runtime fatal error.In the new pattern, metrics should be accumulated via
Contextwithout callingsetProject()ortrigger(). The publishing is handled by the API shutdown hook.Proposed fix
$usage - ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) - ->setProject($project) - ->trigger(); + ->addMetric(METRIC_AUTH_METHOD_PHONE, 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php` around lines 209 - 212, The code is calling non-existent Context methods setProject() and trigger() on $usage; remove the chaining and only call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) so metrics are accumulated by the Context, not published here—do not call setProject() or trigger(), the API shutdown hook will handle publishing. Ensure no other code in Create.php tries to chain setProject() or trigger() on the Context instance.CONTRIBUTING.md (3)
500-507:⚠️ Potential issue | 🟠 MajorReset the usage context in a
finallyblock.If
enqueue()throws,$usage->reset()is skipped and the next worker job may inherit stale metrics. Model this astry/finallyto prevent cross-job leakage.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CONTRIBUTING.md` around lines 500 - 507, Wrap the enqueue call in a try/finally so $usage->reset() always runs even if $publisherForUsage->enqueue($message) throws: create the UsageMessage as shown, call $publisherForUsage->enqueue($message) inside a try block, and move $usage->reset() into the finally block to guarantee the usage context is cleared between jobs.
501-504:⚠️ Potential issue | 🟡 MinorShow where
$projectcomes from in the worker flow.
UsageMessageis constructed withproject: $project, but this example never introduces that variable. Show how the worker obtains the project so the snippet is copy-paste ready.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CONTRIBUTING.md` around lines 501 - 504, The snippet constructs a UsageMessage with project: $project but never shows how $project is obtained; before the UsageMessage creation, add and show the assignment that populates $project in the worker flow (e.g., retrieving it from the job payload or context such as $project = $job->getProject() or $project = $this->getProjectFromContext()), so readers can copy-paste the example; reference the UsageMessage construction and the exact variable name $project so the source of that variable is clear.
492-498:⚠️ Potential issue | 🟡 MinorAlign the compute metric units.
The example records
duration * 1000(milliseconds), but the metrics table at lines 388-390 documentsbuilds.compute/executions.computeas seconds. The snippet and table should agree.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CONTRIBUTING.md` around lines 492 - 498, The compute metric units are inconsistent: the example multiplies duration by 1000 (milliseconds) while the docs list builds.compute/executions.compute as seconds; update the example so both uses of duration (the calls to addMetric with METRIC_BUILDS_COMPUTE and METRIC_FUNCTION_ID_BUILDS_COMPUTE) record seconds instead of milliseconds by removing the * 1000 on $build->getAttribute('duration', 0) so the sample matches the documented units.src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php (1)
415-418:⚠️ Potential issue | 🔴 Critical
Contextclass has nosetProject()ortrigger()methods—this will fatal at runtime.The
Appwrite\Usage\Contextclass only providesaddMetric(),addReduce(),getMetrics(),getReduce(),isEmpty(), andreset(). Lines 417-418 call undefined methods which will cause a fatal error when this code path executes.Remove the chained
setProject()andtrigger()calls. In the new pattern, metrics are accumulated viaaddMetric()and published automatically by the shutdown hook (for API requests) or explicitly viaUsagePublisher(for workers).🐛 Proposed fix
$usage - ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) - ->setProject($project) - ->trigger(); + ->addMetric(METRIC_AUTH_METHOD_PHONE, 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php` around lines 415 - 418, Remove the invalid chained calls on the Context instance: replace the chain that calls setProject() and trigger() after addMetric() with a single call to addMetric() on the $usage object (i.e., call $usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) only), since Appwrite\Usage\Context does not implement setProject() or trigger(); metrics will be published by the existing shutdown hook or via UsagePublisher where appropriate.src/Appwrite/Platform/Workers/Webhooks.php (1)
200-202:⚠️ Potential issue | 🔴 Critical
Contextclass has nosetProject()ortrigger()methods—this will fatal at runtime.The
Appwrite\Usage\Contextclass doesn't havesetProject()ortrigger()methods. This code will cause a fatal error when webhooks are processed.For workers, metrics should be accumulated via
addMetric()and published viaUsagePublisher. This worker needspublisherForUsageinjected and the publishing logic updated.🔧 Suggested refactor
- Inject
publisherForUsagein the constructor- Replace the invalid calls:
- $usage - ->setProject($project) - ->trigger(); + // Publish accumulated metrics + $message = new \Appwrite\Event\Message\Usage( + project: $project, + metrics: $usage->getMetrics(), + reduce: $usage->getReduce() + ); + try { + $publisherForUsage->enqueue($message); + } finally { + $usage->reset(); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Webhooks.php` around lines 200 - 202, The code incorrectly calls nonexistent Context::setProject() and Context::trigger(); update the worker to accumulate metrics using Context::addMetric() and publish via the UsagePublisher instance instead: inject a UsagePublisher (named publisherForUsage) into the constructor, replace the $usage->setProject($project)->trigger() sequence by adding appropriate metrics to the Context via $usage->addMetric(...) and then call $this->publisherForUsage->publish($usage) (or the publisher's publish method) to send metrics; ensure you reference the existing Context instance ($usage) and the injected publisherForUsage in the worker class.tests/e2e/General/UsageTest.php (1)
188-189:⚠️ Potential issue | 🔴 CriticalUpdate the
#[Depends]targets to the renamed snake_case tests.These attributes still point at the old camelCase method names. PHPUnit resolves
#[Depends]by exact method name, so the renamed tests in this file will not satisfy these dependencies.🔧 Example fix
- #[Depends('testPrepareUsersStats')] + #[Depends('test_prepare_users_stats')] public function test_users_stats(array $data): array- #[Depends('testUsersStats')] + #[Depends('test_users_stats')] public function test_prepare_storage_stats(array $data): arrayApply the same rename to the other
#[Depends]strings in this file.#!/bin/bash python - <<'PY' from pathlib import Path import re path = Path("tests/e2e/General/UsageTest.php") text = path.read_text() methods = set(re.findall(r'public function (\w+)\s*\(', text)) depends = re.findall(r"#\[Depends\('([^']+)'\)\]", text) for dep in depends: if dep not in methods: print(f"unresolved Depends target: {dep}") PYAlso applies to: 232-233, 365-366, 422-423, 597-598, 671-672, 852-854, 931-932, 1095-1096, 1233-1234, 1309-1310
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/General/UsageTest.php` around lines 188 - 189, Update all #[Depends] attribute targets that still reference old camelCase test method names to the renamed snake_case method names so PHPUnit can resolve them; for example change #[Depends('testPrepareUsersStats')] to #[Depends('test_prepare_users_stats')] for the dependency used by public function test_users_stats, and apply the same pattern for the other mismatched targets in this file (ensure each Depends string exactly matches its corresponding public function name like test_prepare_*), then run the supplied verification script to confirm there are no unresolved Depends entries.app/init/resources.php (1)
154-158:⚠️ Potential issue | 🟠 MajorPass the explicit usage queue driver here.
This factory still builds the usage queue without the
'utopia-queue'driver. The worker/CLI wiring uses the driver-specific constructor, so this can publish usage messages to a different backend than the usage worker is polling.Suggested fix
Http::setResource('publisherForUsage', function (Publisher $publisher) { $queueName = System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME); - $queue = new \Utopia\Queue\Queue($queueName); + $queue = new \Utopia\Queue\Queue($queueName, 'utopia-queue'); return new \Appwrite\Event\Publisher\Usage($publisher, $queue); }, ['publisher']);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/init/resources.php` around lines 154 - 158, The factory Http::setResource('publisherForUsage', ...) currently constructs the usage queue without specifying a driver; update it to pass the explicit 'utopia-queue' driver to the \Utopia\Queue\Queue constructor (so the Usage publisher created by new \Appwrite\Event\Publisher\Usage($publisher, $queue) will publish to the same backend the worker polls). Locate the anonymous function creating the queue and supply the driver identifier as the first constructor argument when constructing \Utopia\Queue\Queue.src/Appwrite/Platform/Modules/Functions/Workers/Builds.php (3)
1296-1296:⚠️ Potential issue | 🟡 MinorDrop the unused
$keyassignment.Line 1296 assigns
$keyand never reads it. It’s just dead state at this point.Proposed fix
- $key = $resource->getAttribute('runtime'); $runtime = match ($resource->getCollection()) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php` at line 1296, Remove the dead assignment to $key by deleting the statement that assigns $resource->getAttribute('runtime') (i.e., the "$key = $resource->getAttribute('runtime');" line) inside the Builds.php worker code so no unused variable remains; ensure no other code references $key before or after removal and run tests to confirm no regressions.
396-403:⚠️ Potential issue | 🟠 MajorRemove the extra quotes around
escapeshellarg()inmv.Line 400 still wraps
escapeshellarg($from)andescapeshellarg($to)in"again. That makesmvreceive literal quote characters, so the rename path for directories with spaces fails instead of fixing them.Proposed fix
- $exit = Console::execute('mv "'.\escapeshellarg($from).'" "'.\escapeshellarg($to).'"', '', $stdout, $stderr); + $exit = Console::execute('mv '.\escapeshellarg($from).' '.\escapeshellarg($to), '', $stdout, $stderr);#!/bin/bash sed -n '396,402p' src/Appwrite/Platform/Modules/Functions/Workers/Builds.php🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php` around lines 396 - 403, The mv command is incorrectly wrapping the outputs of escapeshellarg($from) and escapeshellarg($to) in additional double quotes, causing literal quote characters to be passed; update the Console::execute call in the block handling $rootDirectory with spaces so it uses mv '.escapeshellarg($from).' '.escapeshellarg($to) (i.e., remove the extra '"' wrappers around escapeshellarg results) while keeping escapeshellarg to properly escape $from and $to variables and preserve the existing error handling (the surrounding code using $tmpDirectory, $rootDirectoryWithoutSpaces, $from, $to and the subsequent exception should remain unchanged).
1231-1239:⚠️ Potential issue | 🟠 MajorUse the resolved build memory for
*_MB_SECONDS.The runtime is created with the floored memory from Line 593, but Lines 1231, 1235, and 1239 still multiply only the raw spec memory. That underreports sites and frameworks whose builds are bumped to the minimum memory floor.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php` around lines 1231 - 1239, The MB-seconds metrics currently multiply by the raw spec memory expression (($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT)) which underreports when the runtime memory was floored earlier; locate the variable used when the runtime was created (the resolved/floored memory computed around the runtime creation at ~line 593 — e.g. the variable like $memory, $runtimeMemory or $runtime->getAttribute('memory')) and replace the raw spec expression in the three addMetric calls (the METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS, METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS and the first METRIC_BUILDS_MB_SECONDS) so they multiply by that resolved/floored memory value instead.
🧹 Nitpick comments (2)
src/Appwrite/Platform/Workers/Webhooks.php (1)
91-94: Missing@param Context $usagein docblock.The
execute()method signature includesContext $usagebut the docblock only lists@param array $plan. Add the missing parameter documentation for consistency.📝 Proposed fix
/** * `@param` array $events * `@param` string $payload * `@param` Document $webhook * `@param` Document $user * `@param` Document $project * `@param` Database $dbForPlatform * `@param` Mail $queueForMails + * `@param` Context $usage * `@param` array $plan * `@return` void */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Workers/Webhooks.php` around lines 91 - 94, The docblock for the execute() method is missing the `@param` Context $usage entry; update the method's PHPDoc above execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForPlatform, Mail $queueForMails, Context $usage, array $plan): void to include a line documenting the Context $usage parameter (e.g., "@param Context $usage Usage/context for the webhook execution") alongside the existing `@param` array $plan to keep docblock consistent with the signature.src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php (1)
101-101: Remove unused injected parameters$timelimitand$plan.Both parameters are declared in the method signature but never referenced in the method body. Removing them simplifies the signature and avoids confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php` at line 101, The action method signature declares unused injected parameters $timelimit and $plan in Create::action; remove these two parameters from the method signature to simplify and avoid confusion, then update any callers or dependency injection/route bindings that reference this signature (e.g., controller instantiation, service wiring) to match the new signature so no runtime parameter mismatch occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/General/UsageTest.php`:
- Around line 853-854: The test uses the attribute #[Retry(count: 1)] but misses
its import, causing PHP to resolve it to the wrong namespace; add the import
"use Appwrite\Tests\Retry;" to the top import block of UsageTest.php so the
Retry attribute used on test_database_stats_tables_api (and any other tests in
this file) references the correct class Appwrite\Tests\Retry.
---
Outside diff comments:
In `@src/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.php`:
- Around line 63-67: The route builder injects 'usage' but the handler signature
for action() does not accept it, so either add a Context $usage parameter to the
action method (public function action(string $databaseId, UtopiaResponse
$response, Database $dbForProject, EventDatabase $queueForDatabase, Event
$queueForEvents, Context $usage): void) so the injected usage context is
consumed when tracking deletion metrics, or remove the ->inject('usage') call
from the route chain to avoid an unused injection; update references inside
action() accordingly (use $usage for metrics if added, or delete any code
assuming it if you remove the injection).
In `@src/Appwrite/Platform/Workers/Messaging.php`:
- Around line 311-322: The code incorrectly calls non-existent Context methods
setProject() and trigger() on $usage; replace this publish pattern in
sendExternalMessage() by accumulating metrics with $usage->addMetric(...), then
build a UsageMessage using UsageMessage(project: $project, metrics:
$usage->getMetrics(), reduce: $usage->getReduce()), enqueue it via the injected
publisherForUsage->enqueue($message) inside a try/finally, and call
$usage->reset() in the finally block; remove calls to $usage->setProject() and
$usage->trigger() and ensure publisherForUsage is injected into the worker.
---
Duplicate comments:
In `@app/init/resources.php`:
- Around line 154-158: The factory Http::setResource('publisherForUsage', ...)
currently constructs the usage queue without specifying a driver; update it to
pass the explicit 'utopia-queue' driver to the \Utopia\Queue\Queue constructor
(so the Usage publisher created by new
\Appwrite\Event\Publisher\Usage($publisher, $queue) will publish to the same
backend the worker polls). Locate the anonymous function creating the queue and
supply the driver identifier as the first constructor argument when constructing
\Utopia\Queue\Queue.
In `@CONTRIBUTING.md`:
- Around line 500-507: Wrap the enqueue call in a try/finally so $usage->reset()
always runs even if $publisherForUsage->enqueue($message) throws: create the
UsageMessage as shown, call $publisherForUsage->enqueue($message) inside a try
block, and move $usage->reset() into the finally block to guarantee the usage
context is cleared between jobs.
- Around line 501-504: The snippet constructs a UsageMessage with project:
$project but never shows how $project is obtained; before the UsageMessage
creation, add and show the assignment that populates $project in the worker flow
(e.g., retrieving it from the job payload or context such as $project =
$job->getProject() or $project = $this->getProjectFromContext()), so readers can
copy-paste the example; reference the UsageMessage construction and the exact
variable name $project so the source of that variable is clear.
- Around line 492-498: The compute metric units are inconsistent: the example
multiplies duration by 1000 (milliseconds) while the docs list
builds.compute/executions.compute as seconds; update the example so both uses of
duration (the calls to addMetric with METRIC_BUILDS_COMPUTE and
METRIC_FUNCTION_ID_BUILDS_COMPUTE) record seconds instead of milliseconds by
removing the * 1000 on $build->getAttribute('duration', 0) so the sample matches
the documented units.
In
`@src/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.php`:
- Around line 209-212: The code is calling non-existent Context methods
setProject() and trigger() on $usage; remove the chaining and only call
$usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) so metrics are accumulated by the
Context, not published here—do not call setProject() or trigger(), the API
shutdown hook will handle publishing. Ensure no other code in Create.php tries
to chain setProject() or trigger() on the Context instance.
In
`@src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php`:
- Around line 77-84: Remove the unused user injection and parameter: delete the
->inject('user') call in the service builder chain and remove the Document $user
parameter from the action(...) method signature (the action method defined as
public function action(..., Document $user, Context $usage, ...)). Update any
related parameter ordering/typing in the action declaration to keep Context
$usage and subsequent parameters in the correct positions and ensure no
references to $user remain in the method body.
In `@src/Appwrite/Platform/Modules/Functions/Workers/Builds.php`:
- Line 1296: Remove the dead assignment to $key by deleting the statement that
assigns $resource->getAttribute('runtime') (i.e., the "$key =
$resource->getAttribute('runtime');" line) inside the Builds.php worker code so
no unused variable remains; ensure no other code references $key before or after
removal and run tests to confirm no regressions.
- Around line 396-403: The mv command is incorrectly wrapping the outputs of
escapeshellarg($from) and escapeshellarg($to) in additional double quotes,
causing literal quote characters to be passed; update the Console::execute call
in the block handling $rootDirectory with spaces so it uses mv
'.escapeshellarg($from).' '.escapeshellarg($to) (i.e., remove the extra '"'
wrappers around escapeshellarg results) while keeping escapeshellarg to properly
escape $from and $to variables and preserve the existing error handling (the
surrounding code using $tmpDirectory, $rootDirectoryWithoutSpaces, $from, $to
and the subsequent exception should remain unchanged).
- Around line 1231-1239: The MB-seconds metrics currently multiply by the raw
spec memory expression (($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT)) which
underreports when the runtime memory was floored earlier; locate the variable
used when the runtime was created (the resolved/floored memory computed around
the runtime creation at ~line 593 — e.g. the variable like $memory,
$runtimeMemory or $runtime->getAttribute('memory')) and replace the raw spec
expression in the three addMetric calls (the
METRIC_RESOURCE_TYPE_BUILDS_MB_SECONDS,
METRIC_RESOURCE_TYPE_ID_BUILDS_MB_SECONDS and the first
METRIC_BUILDS_MB_SECONDS) so they multiply by that resolved/floored memory value
instead.
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Around line 415-418: Remove the invalid chained calls on the Context instance:
replace the chain that calls setProject() and trigger() after addMetric() with a
single call to addMetric() on the $usage object (i.e., call
$usage->addMetric(METRIC_AUTH_METHOD_PHONE, 1) only), since
Appwrite\Usage\Context does not implement setProject() or trigger(); metrics
will be published by the existing shutdown hook or via UsagePublisher where
appropriate.
In `@src/Appwrite/Platform/Workers/Migrations.php`:
- Around line 148-155: The variable $publisherForUsage from action() is not
passed into processMigration() or processMigrationResourceStats(), causing an
undefined variable when processMigrationResourceStats() tries to call
$publisherForUsage->enqueue(...). Fix by adding a $publisherForUsage parameter
to processMigration() and processMigrationResourceStats(), update all internal
calls (e.g., where processMigration(...) is invoked) to pass the
$publisherForUsage value through, and ensure any helper calls within
processMigration that call processMigrationResourceStats also forward the same
$publisherForUsage parameter so the enqueue call uses the propagated publisher
instance.
In `@src/Appwrite/Platform/Workers/Webhooks.php`:
- Around line 200-202: The code incorrectly calls nonexistent
Context::setProject() and Context::trigger(); update the worker to accumulate
metrics using Context::addMetric() and publish via the UsagePublisher instance
instead: inject a UsagePublisher (named publisherForUsage) into the constructor,
replace the $usage->setProject($project)->trigger() sequence by adding
appropriate metrics to the Context via $usage->addMetric(...) and then call
$this->publisherForUsage->publish($usage) (or the publisher's publish method) to
send metrics; ensure you reference the existing Context instance ($usage) and
the injected publisherForUsage in the worker class.
In `@tests/e2e/General/UsageTest.php`:
- Around line 188-189: Update all #[Depends] attribute targets that still
reference old camelCase test method names to the renamed snake_case method names
so PHPUnit can resolve them; for example change
#[Depends('testPrepareUsersStats')] to #[Depends('test_prepare_users_stats')]
for the dependency used by public function test_users_stats, and apply the same
pattern for the other mismatched targets in this file (ensure each Depends
string exactly matches its corresponding public function name like
test_prepare_*), then run the supplied verification script to confirm there are
no unresolved Depends entries.
---
Nitpick comments:
In `@src/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.php`:
- Line 101: The action method signature declares unused injected parameters
$timelimit and $plan in Create::action; remove these two parameters from the
method signature to simplify and avoid confusion, then update any callers or
dependency injection/route bindings that reference this signature (e.g.,
controller instantiation, service wiring) to match the new signature so no
runtime parameter mismatch occurs.
In `@src/Appwrite/Platform/Workers/Webhooks.php`:
- Around line 91-94: The docblock for the execute() method is missing the `@param`
Context $usage entry; update the method's PHPDoc above execute(array $events,
string $payload, Document $webhook, Document $user, Document $project, Database
$dbForPlatform, Mail $queueForMails, Context $usage, array $plan): void to
include a line documenting the Context $usage parameter (e.g., "@param Context
$usage Usage/context for the webhook execution") alongside the existing `@param`
array $plan to keep docblock consistent with the signature.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1669772c-0982-40cc-bc59-3082c7c89535
📒 Files selected for processing (50)
CONTRIBUTING.mdapp/cli.phpapp/controllers/api/account.phpapp/controllers/shared/api.phpapp/init/resources.phpapp/worker.phpsrc/Appwrite/Bus/Listeners/Usage.phpsrc/Appwrite/Event/Message/Base.phpsrc/Appwrite/Event/Message/Usage.phpsrc/Appwrite/Event/Publisher/Base.phpsrc/Appwrite/Event/Publisher/Usage.phpsrc/Appwrite/Event/StatsUsage.phpsrc/Appwrite/Platform/Modules/Account/Http/Account/MFA/Challenges/Create.phpsrc/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.phpsrc/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.phpsrc/Appwrite/Platform/Modules/Functions/Http/Executions/Create.phpsrc/Appwrite/Platform/Modules/Functions/Workers/Builds.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/Failed/Get.phpsrc/Appwrite/Platform/Modules/Health/Http/Health/Queue/StatsUsage/Get.phpsrc/Appwrite/Platform/Modules/Teams/Http/Memberships/Create.phpsrc/Appwrite/Platform/Workers/Messaging.phpsrc/Appwrite/Platform/Workers/Migrations.phpsrc/Appwrite/Platform/Workers/Webhooks.phpsrc/Appwrite/Usage/Context.phptests/e2e/General/UsageTest.php
💤 Files with no reviewable changes (1)
- src/Appwrite/Event/StatsUsage.php
🚧 Files skipped from review as they are similar to previous changes (15)
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php
- src/Appwrite/Event/Message/Base.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php
- src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Transactions/Update.php
- src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php
- src/Appwrite/Platform/Modules/Avatars/Http/Screenshots/Get.php
- src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php
- src/Appwrite/Event/Publisher/Usage.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php
- src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php
df4e6e3 to
a403059
Compare
9179be3 to
704fbe0
Compare
Co-Authored-By: Claude Opus 4.6 <[email protected]>
704fbe0 to
a804cba
Compare
…arg double-quoting Use $memory (which includes minMemory floor) and $cpus instead of raw spec values in MB-seconds metrics, fixing underreporting for sites and frameworks bumped to the minimum memory. Also remove redundant double quotes around escapeshellarg() calls in mv command. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Overview
This PR refactors the usage metrics system from a stateful event-based pattern to a cleaner, stateless publisher pattern that aligns with how all other events work in the codebase.
The Problem
The
StatsUsageevent class was architecturally inconsistent with other event types:StatsUsage (Inconsistent):
trigger()on itselfOther Events (Correct Pattern):
This inconsistency made the codebase harder to understand and maintain.
The Solution
Split responsibilities into three components matching the existing event pattern:
1. Context - Mutable accumulator (request/worker scope)
2. Message\Usage - Immutable DTO (data snapshot)
3. Publisher\Usage - Stateless publisher (handles enqueueing)
Key Changes
During Request/Worker Execution
Before:
After:
The new pattern is simpler during execution - you just accumulate metrics without worrying about project context or publishing.
At Shutdown/Completion
Before:
After:
The new pattern is explicit and consistent with how all other events work (Audit, Events, Webhooks, etc.).
Disabled Metrics Filtering
An additional improvement - filtering is now decoupled and happens at publish time:
Previously this filtering was tightly coupled to the StatsUsage event object. Now it's a simple inline filter that's easy to modify or extend.
Benefits
🎯 Architectural Consistency
All events now follow the same pattern - no special cases to remember
🔍 Clearer Data Flow
Accumulation → Snapshot → Publish is explicit at each step
🔓 Decoupled Filtering
API key concerns separated from metric collection
🧪 Better Testability
Stateless components are easier to test and debug
📦 Transparent Dependencies
Workers explicitly inject what they need, no hidden state
Real-World Example: Builds Worker
Before:
After:
The new pattern makes it crystal clear: accumulate metrics, create immutable message, publish via publisher, reset for reuse.
Testing
All usage metrics tests passing:
Test Improvements
Migration Impact
This is an internal refactoring with no breaking changes:
The refactoring is completely transparent to API consumers.
Files Changed
src/Appwrite/Event/StatsUsage.php(old stateful event)src/Appwrite/Usage/Context.php(pure accumulator)src/Appwrite/Event/Publisher/Usage.php(readonly property issue)Net change: -99 lines of code
🤖 Generated with Claude Code