Skip to content

Fix users events & missed publisher logic for Functions #10348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 22, 2025

Conversation

ItzNotABug
Copy link
Member

What does this PR do?

Users.*.create not fired on Cloud and also seems to have a different event handling logic.

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Screenshots may also be helpful.)

Related PRs and Issues

  • (Related PR or issue)

Checklist

  • Have you read the Contributing Guidelines on issues?
  • If the PR includes a change to an API's metadata (desc, label, params, etc.), does it also include updated API specs and example docs?

@ItzNotABug ItzNotABug self-assigned this Aug 21, 2025
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

📝 Walkthrough

Walkthrough

Adds a new BrokerPool-backed resource alias publisherFunctions (and consumerFunctions in init) across CLI, worker, and init resource registration. Health API routes for queue checks now inject publisherFunctions and call getQueueSize with Event::FUNCTIONS_QUEUE_NAME. Shared API wiring builds the functions queue from publisherFunctions and creates a cloned events queue for the database listener. ScheduleBase gains a publisherFunctions property and constructor injection; ScheduleExecutions and ScheduleFunctions now construct Func with publisherFunctions. No other control flow or logic changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • loks0n
  • abnegate

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.1.17)

Note: Using configuration file /phpstan.neon.
Invalid entry in excludePaths:
Path "/app/sdks" is neither a directory, nor a file path, nor a fnmatch pattern.

If the excluded path can sometimes exist, append (?)
to its config entry to mark it as optional. Example:

parameters:
excludePaths:
analyseAndScan:
- app/sdks (?)

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-users-events

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Aug 21, 2025

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
libecpg 17.5-r0 CVE-2025-8714 HIGH
libecpg 17.5-r0 CVE-2025-8715 HIGH
libecpg-dev 17.5-r0 CVE-2025-8714 HIGH
libecpg-dev 17.5-r0 CVE-2025-8715 HIGH
libpq 17.5-r0 CVE-2025-8714 HIGH
libpq 17.5-r0 CVE-2025-8715 HIGH
libpq-dev 17.5-r0 CVE-2025-8714 HIGH
libpq-dev 17.5-r0 CVE-2025-8715 HIGH
postgresql17-dev 17.5-r0 CVE-2025-8714 HIGH
postgresql17-dev 17.5-r0 CVE-2025-8715 HIGH
golang.org/x/crypto v0.31.0 CVE-2025-22869 HIGH
golang.org/x/oauth2 v0.24.0 CVE-2025-22868 HIGH
stdlib 1.22.10 CVE-2025-47907 HIGH

Source Code Scan Results

🎉 No vulnerabilities found!

Copy link

github-actions bot commented Aug 21, 2025

✨ Benchmark results

  • Requests per second: 1,278
  • Requests with 200 status code: 230,048
  • P99 latency: 0.151358535

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,278 1,050
200 230,048 188,993
P99 0.151358535 0.185588851

@ItzNotABug ItzNotABug changed the title Fix users events Fix users events & missed publisher logic for Functions Aug 22, 2025
@ItzNotABug ItzNotABug marked this pull request as ready for review August 22, 2025 05:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
src/Appwrite/Platform/Tasks/ScheduleExecutions.php (2)

46-49: Inverted enqueue window check: schedules in-window are skipped.

For items due before or at intervalEnd, we should enqueue; the current condition continues instead. This prevents any execution from being queued within the intended window.

Apply:

-            if ($scheduledAt <= $intervalEnd) {
-                continue;
-            }
+            // Only enqueue items that fall within the next ENQUEUE_TIMER window
+            if ($scheduledAt > $intervalEnd) {
+                continue;
+            }

56-63: Clamp negative “$delay” before sleeping
Clock skew or load spikes can make the computed $delay slightly negative, causing Co::sleep() to receive invalid values (and skewing your enqueue-delay metrics). Let’s ensure we never sleep for less than zero seconds:

• File src/Appwrite/Platform/Tasks/ScheduleExecutions.php, inside enqueueResources() before the \go() call

-            $delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp();
+            $delay = max(0, $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp());

It might also help to add a small test asserting that under tight timing windows, $delay is never negative.

src/Appwrite/Platform/Tasks/ScheduleFunctions.php (3)

79-83: Use coroutine-friendly sleep to avoid blocking the reactor thread.

Inside a coroutine, prefer Swoole coroutine sleep. Also ensure the import exists.

Patch within this block:

-                \sleep($delay); // in seconds
+                Co::sleep($delay); // in seconds

And add the import near the top:

+use Swoole\Coroutine as Co;

85-87: Don't abort the entire batch when a single schedule is missing.

Returning here stops processing remaining schedules with the same delay. Continue instead.

-                    if (!\array_key_exists($scheduleKey, $this->schedules)) {
-                        return;
-                    }
+                    if (!\array_key_exists($scheduleKey, $this->schedules)) {
+                        continue;
+                    }

67-71: Clamp $delay to zero to guard against negative waits.

Boundary timing can produce negative delays; clamp to zero before sleeping.

-            $delay = $executionStart - $promiseStart; // Time to wait from now until execution needs to be queued
+            $delay = max(0, $executionStart - $promiseStart); // Clamp to avoid negatives
app/controllers/api/account.php (2)

1718-1767: Nit: wrong constant reference 'TYPE::EMAIL' will fatally error

Within OAuth2 redirect session creation, factors include TYPE::EMAIL instead of the imported Type::EMAIL. This will fatal at runtime.

Apply this minimal fix:

-                'factors' => [TYPE::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks
+                'factors' => [Type::EMAIL, 'oauth2'], // include a special oauth2 factor to bypass MFA checks

3918-3930: Verify token source in phone verification update

This action fetches the target profile by userId but verifies the token against $user->getAttribute('tokens', []) rather than $profile->getAttribute('tokens', []). If the session user differs or is empty, verification will fail or pass incorrectly.

Apply this fix:

-        $verifiedToken = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_PHONE, $secret);
+        $verifiedToken = Auth::tokenVerify($profile->getAttribute('tokens', []), Auth::TOKEN_TYPE_PHONE, $secret);
🧹 Nitpick comments (12)
src/Appwrite/Platform/Tasks/ScheduleFunctions.php (1)

110-114: Follow-up on lastEnqueueUpdate tracking.

Comment notes enabling this breaks scheduling. Please track a task to properly re-enable drift correction or remove the field to avoid confusion. If needed, I can draft a minimal fix that updates it at the end of the tick and adjusts the timeFrame math accordingly.

app/cli.php (2)

212-214: Future-proof queueForFunctions to depend on publisherFunctions.

To keep the decoupling effective if/when publisher and publisherFunctions diverge, wire queueForFunctions to the new alias.

-CLI::setResource('queueForFunctions', function (Publisher $publisher) {
-    return new Func($publisher);
-}, ['publisher']);
+CLI::setResource('queueForFunctions', function (Publisher $publisherFunctions) {
+    return new Func($publisherFunctions);
+}, ['publisherFunctions']);

191-199: Consider adding consumerFunctions alias for symmetry (optional).

If any CLI tasks ever consume from the functions pool directly, mirroring consumerFunctions keeps naming consistent with app/init/resources.php.

Proposed addition (outside this hunk):

CLI::setResource('consumerFunctions', function (BrokerPool $consumer) {
    return $consumer;
}, ['consumer']);
app/worker.php (2)

318-321: Wire queueForFunctions to publisherFunctions for consistency.

Bind queueForFunctions to the new alias so future split pools won’t require sweeping changes.

-Server::setResource('queueForFunctions', function (Publisher $publisher) {
-    return new Func($publisher);
-}, ['publisher']);
+Server::setResource('queueForFunctions', function (Publisher $publisherFunctions) {
+    return new Func($publisherFunctions);
+}, ['publisherFunctions']);

266-280: Optional: add consumerFunctions alias to mirror app/init.

For parity with app/init/resources.php, consider:

Server::setResource('consumerFunctions', function (BrokerPool $consumer) {
    return $consumer;
}, ['consumer']);
app/init/resources.php (1)

144-146: Bind queueForFunctions to publisherFunctions to honor the split.

Small DI tweak to ensure the queue uses the dedicated functions pool if/when it diverges.

-App::setResource('queueForFunctions', function (Publisher $publisher) {
-    return new Func($publisher);
-}, ['publisher']);
+App::setResource('queueForFunctions', function (Publisher $publisherFunctions) {
+    return new Func($publisherFunctions);
+}, ['publisherFunctions']);
app/controllers/api/account.php (2)

265-267: Also set Event.userId alongside params for consistent payloads and downstream consumers

Across many routes you correctly add setParam('userId', ...). However, Event payloads expose a top-level userId (via setUserId) that some workers/consumers rely on. Today preparePayload() includes userId separately from params. To avoid missing userId in payloads, please set both.

Example patch (apply the same pattern to other touched sites):

@@
-    $queueForEvents->setParam('userId', $user->getId());
+    $queueForEvents
+        ->setUserId($user->getId())
+        ->setParam('userId', $user->getId());

And where sessionId/tokenId/targetId are set, keep setParam as you already do.

If you prefer avoiding duplication, we can modify Event to fall back to params['userId'] when userId is null. Otherwise, setting both keeps behavior explicit and backward-compatible. Want me to open a small follow-up for the Event fallback?

Also applies to: 438-439, 761-765, 852-856, 983-986, 1114-1116, 1719-1723, 1763-1767, 2873-2874, 2960-2961, 3057-3058, 3143-3144, 3181-3182, 3219-3221, 3500-3503, 3633-3667, 3880-3883, 3946-3949, 4051-4051, 4161-4161, 4251-4251, 4314-4314, 4380-4381, 4505-4506, 4940-4942, 5005-5007, 5060-5063


147-153: Nit: incorrect PHP DateTime format 'YYYY' in email variables

In sendSessionAlert(), DateTime::format('YYYY') will literally output "YYYY". PHP uses 'Y' for a 4-digit year.

Apply this change:

-        'year' => (new \DateTime())->format('YYYY'),
+        'year' => (new \DateTime())->format('Y'),
app/controllers/shared/api.php (1)

666-693: Emitting functions/webhooks/realtime from a single route-level event may limit multi-entity actions

The shutdown block emits one event per request. For actions affecting multiple entities (e.g., bulk session delete), this can under-report events. If parity with previous behavior is desired, consider allowing per-entity emission in-route using cloned events, skipping the global emission for that request.

I can draft a small helper to clone+trigger per entity while preserving audits and usage behavior. Interested?

app/controllers/api/users.php (3)

199-200: Attach userId to the event: good placement and minimal surface.

Setting userId on $queueForEvents immediately after persistence ensures route-level label interpolation (users.[userId].create) has data at publish time. No payload is sent, which is consistent with other user mutation routes that avoid exposing PII in event payloads.

If you want future maintainers to discover this intentionally minimal event envelope quickly, consider a tiny docblock noting that we only set userId here (and why), e.g.:

-/** TODO: Remove function when we move to using utopia/platform */
+/** 
+ * TODO: Remove function when we move to using utopia/platform
+ * Note: We intentionally only attach `userId` to the route Event for `users.*.create`
+ * to avoid sending user PII in event payloads.
+ */

62-75: Add a short docblock to reflect the new parameter and intent.

This helper now carries an Event dependency. A small docblock improves readability until the TODO is resolved.

Apply:

 /** TODO: Remove function when we move to using utopia/platform */
+/**
+ * Creates a user and attaches `userId` to the route event queue for
+ * `users.[userId].create`. Avoids sending PII in event payloads.
+ *
+ * @param string              $hash
+ * @param array|string        $hashOptions
+ * @param string              $userId
+ * @param ?string             $email
+ * @param ?string             $password
+ * @param ?string             $phone
+ * @param string              $name
+ * @param Document            $project
+ * @param Database            $dbForProject
+ * @param Event               $queueForEvents Event publisher for route labels
+ * @param Hooks               $hooks
+ * @return Document
+ */
 function createUser(

204-239: Consider adding an integration test to assert event emission on all 8 create routes.

Given the PR goal (fixing Users.*.create events), a thin end-to-end assertion per route variant would guard against future regressions.

I can draft a small test suite that:

  • hits each route with a unique ID,
  • stubs the event publisher (or inspects the emitted label/params),
  • asserts one event emitted with label users.[userId].create and param userId.

Would you like me to open a follow-up PR with these tests?

Also applies to: 241-276, 278-314, 315-351, 352-395, 396-432, 433-481, 483-522

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fd13250 and 60093bf.

📒 Files selected for processing (10)
  • app/cli.php (1 hunks)
  • app/controllers/api/account.php (3 hunks)
  • app/controllers/api/health.php (1 hunks)
  • app/controllers/api/users.php (18 hunks)
  • app/controllers/shared/api.php (2 hunks)
  • app/init/resources.php (2 hunks)
  • app/worker.php (1 hunks)
  • src/Appwrite/Platform/Tasks/ScheduleBase.php (3 hunks)
  • src/Appwrite/Platform/Tasks/ScheduleExecutions.php (1 hunks)
  • src/Appwrite/Platform/Tasks/ScheduleFunctions.php (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
src/Appwrite/Platform/Tasks/ScheduleBase.php (4)
src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php (1)
  • action (100-506)
src/Appwrite/Platform/Tasks/ScheduleExecutions.php (1)
  • getSupportedResource (19-22)
src/Appwrite/Platform/Tasks/ScheduleFunctions.php (1)
  • getSupportedResource (24-27)
src/Appwrite/Platform/Tasks/ScheduleMessages.php (1)
  • getSupportedResource (18-21)
app/controllers/api/users.php (1)
src/Appwrite/Event/Event.php (2)
  • Event (10-641)
  • setParam (282-287)
app/controllers/api/account.php (1)
src/Appwrite/Event/Event.php (2)
  • Event (10-641)
  • setParam (282-287)
app/controllers/api/health.php (2)
src/Appwrite/Platform/Tasks/ScheduleBase.php (1)
  • action (72-110)
src/Appwrite/Event/Event.php (1)
  • Event (10-641)
app/controllers/shared/api.php (2)
src/Appwrite/Utopia/Response.php (1)
  • Response (151-963)
src/Appwrite/Event/Event.php (1)
  • Event (10-641)
🔇 Additional comments (15)
src/Appwrite/Platform/Tasks/ScheduleExecutions.php (1)

33-34: Correct broker: switch to publisherFunctions is right.

Using the dedicated functions broker pool here aligns this task with the new DI and isolates function traffic from other publishers. Good change.

src/Appwrite/Platform/Tasks/ScheduleFunctions.php (1)

93-96: Correct broker: constructing Func with publisherFunctions.

Matches the new separation and keeps function enqueues on the dedicated pool. Looks good.

app/cli.php (1)

197-199: LGTM: publisherFunctions CLI resource mirrors publisher.

Good forward-compat alias to decouple functions traffic. No issues spotted.

app/worker.php (1)

254-257: LGTM: publisherFunctions worker resource alias added.

This keeps worker DI consistent with the new aliasing approach.

app/init/resources.php (2)

90-93: LGTM: publisherFunctions alias registered.

Matches the new DI shape and enables clean separation. Looks good.


105-108: LGTM: consumerFunctions alias registered.

Good to have symmetry with publisher side; no issues found.

app/controllers/api/account.php (2)

293-299: Good wiring of users.[userId].create route event and DI of queueForEvents

Labeling the route with event users.[userId].create and injecting Event $queueForEvents is the right move to restore Users.*.create emissions post-database-listener removal. Setting the userId param inside the action ensures proper pattern substitution.

Also applies to: 327-329, 438-439


606-641: Clarify event emission scope for session deletions

The proposed refactor correctly addresses the bug where $session outside the loop could be undefined or point to the last iterated session, ensuring that the final sessionId is explicitly captured for the “current” session within the loop.

Please confirm which behavior is desired:

  • Emit exactly one “users.{userId}.sessions.{sessionId}.delete” event for the current session (as identified by the matching secret), or
  • Emit a delete event for each session being removed.

If you intend the former, apply the diff below. If the latter, consider triggering your event inside the loop (e.g. cloning or re-instantiating the queue per session) rather than relying on shutdown.

Apply this diff for the single-event approach:

@@ -605,7 +605,10 @@
-        foreach ($sessions as $session) {/** @var Document $session */
+        $eventSessionId = null;
+        foreach ($sessions as $session) {/** @var Document $session */
             $dbForProject->deleteDocument('sessions', $session->getId());
@@ -619,6 +622,7 @@
             if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) {
                 $session->setAttribute('current', true);
+                $eventSessionId = $session->getId();

                 // If current session delete the cookies too
                 $response
@@ -639,9 +643,15 @@
         $dbForProject->purgeCachedDocument('users', $user->getId());

-        $queueForEvents
-            ->setParam('userId', $user->getId())
-            ->setParam('sessionId', $session->getId());
+        if ($eventSessionId !== null) {
+            $queueForEvents
+                ->setParam('userId', $user->getId())
+                ->setParam('sessionId', $eventSessionId);
+        }

Let me know which emission pattern you’d like to support so we can finalize the implementation.

src/Appwrite/Platform/Tasks/ScheduleBase.php (1)

27-31: Verified publisherFunctions DI wiring across the application

All checks confirm the new publisherFunctions resource is registered, injected, and used correctly:

• Resource registration found in
app/worker.php (line 254)
app/cli.php (line 197)
app/init/resources.php (line 90)

• Injection into controllers confirmed in
app/controllers/api/health.php (line 691)

• Scheduler classes consume the new pool via constructor:
src/Appwrite/Platform/Tasks/ScheduleFunctions.php (line 93)
src/Appwrite/Platform/Tasks/ScheduleExecutions.php (line 33)

• No scheduler subclass still uses $this->publisher for enqueueing function jobs

The wiring is complete—no outstanding references to the old publisher resource remain.

app/controllers/api/health.php (1)

690-697: Functions queue health now uses dedicated publisherFunctions and correct queue name

Switching to ->inject('publisherFunctions') and Event::FUNCTIONS_QUEUE_NAME aligns with the new publisher alias and keeps queue sizing accurate. No issues spotted.

app/controllers/shared/api.php (2)

394-409: Route-level event model aligns with removing the DB event listener; ensure all routes needing events set labels

Moving event emission to route labels + queueForEvents is sound. Double-check that all endpoints relying on Users.* events (create/update/delete plus token/verification flows) have event labels post-removal of the DB listener. I noticed recoveries/verification have labels; consider adding labels for createEmailToken/createPhoneToken as well if those were previously emitted via DB listener.

I can scan the codebase and propose a list of routes without labels that still set queueForEvents payloads so you can decide case-by-case. Want me to run that?


503-509: Usage metrics hook on DOCUMENTS_UPSERT looks good

Adding the upsert hook ensures usage metrics remain accurate after the listener refactor. No changes requested.

app/controllers/api/users.php (3)

207-207: All 8 user-create routes consistently labeled – LGTM

Verified that each of the following lines in app/controllers/api/users.php includes

->label('event', 'users.[userId].create')

ensuring uniform event taxonomy for plaintext, bcrypt, md5, argon2, sha, phpass, scrypt, and scrypt-modified variants:

  • Line 207
  • Line 244
  • Line 281
  • Line 318
  • Line 355
  • Line 399
  • Line 436
  • Line 486

No further changes needed.


233-236: Approve DI wiring: queueForEvents injection is consistent
Ran rg -nP --type=php "\->inject('queueForEvents')" -C1 across all modified files and confirmed the container binding name is used exactly and consistently in every route and closure. The closures correctly type-hint Event $queueForEvents, and all calls to createUser() pass $queueForEvents in the proper position for the updated signature.


63-75: Signature extended to accept Event; call sites are up-to-date

The new parameter order ( …, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks ) has been applied consistently in all downstream calls within app/controllers/api/users.php. I verified that each invocation at lines 235, 271, 308, 345, 389, 426, 476, and 516 passes the full set of 11 arguments—including the new Event $queueForEvents—so there are no stale call sites remaining.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/controllers/shared/api.php (1)

64-69: Helpful rationale; minor wording polish (optional)

The intent is clear; consider a concise phrasing to aid future readers.

 /**
- * This single-event based on a `Database::EVENT_DOCUMENT_CREATE` listener may look odd, but it is **intentional**.
- *
- * Accounts can be created in many ways beyond `createAccount`
- * (anonymous, OAuth, phone, etc.), and those flows are probably not covered in event tests; so we handle this here.
+ * Intentionally handle user creation via a Database::EVENT_DOCUMENT_CREATE listener.
+ * Accounts can be created outside `createAccount` (anonymous, OAuth, phone, etc.),
+ * and those flows may be missed by route-based event tests; handle them here.
  */
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 60093bf and 07edb4e.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • app/controllers/shared/api.php (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: Setup & Build Appwrite Image
  • GitHub Check: scan
🔇 Additional comments (1)
app/controllers/shared/api.php (1)

32-32: BrokerPool import verified and compatibility confirmed

  • composer.lock lists utopia-php/queue at version 0.11.0, which provides Utopia\Queue\Broker\Pool.
  • The same alias (Pool as BrokerPool) is already imported and used in multiple locations (e.g., ScheduleBase, Doctor, worker.php, cli.php, resources.php), ensuring consistency.
  • No discrepancies found in how dependencies are constructed elsewhere.

No further changes needed—approving as is.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
app/controllers/shared/api.php (1)

418-419: New DI injection 'publisherFunctions' — verify cross-file resource registration and usage.

Good addition. Please double‑check the DI registrations so that:

  • publisherFunctions is defined and bound to the functions queue brokers.
  • Any queueForFunctions resource definitions consume publisherFunctions (not the legacy publisher).

Run:

#!/bin/bash
set -euo pipefail

echo "Where is publisherFunctions registered/injected?"
rg -nP --type=php -C2 "(publisherFunctions)\b" app || true

echo
echo "Check queueForFunctions wiring across entrypoints:"
rg -nP --type=php -C3 "\b(queueForFunctions)\b" app | sed -n '1,200p'
🧹 Nitpick comments (2)
app/controllers/shared/api.php (2)

64-69: Nit: Align the doc comment with the actual event name format.

You emit users.[userId].create (with a path parameter) below, while the comment mentions users.*.create. Consider updating to avoid confusion.

- * This isolated event handling for `users.*.create` which is based on a `Database::EVENT_DOCUMENT_CREATE` listener may look odd, but it is **intentional**.
+ * This isolated event handling for `users.[userId].create`, which is based on a `Database::EVENT_DOCUMENT_CREATE` listener, may look odd, but it is **intentional**.

If you’d like, I can also add a brief note here explaining how the event param is substituted (setParam('userId', ...)) to help future readers.


556-564: Remove redundant from() at call-site; the listener calls from() again.

You pre-apply ->from($queueForEvents) before passing the queues into $eventDatabaseListener, and then call ->from($queueForEvents) again inside the listener. This is harmless but noisy and could confuse readers.

             ->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener(
                 $project,
                 $document,
                 $response,
                 $queueForEventsClone->from($queueForEvents),
-                $queueForFunctions->from($queueForEvents),
-                $queueForWebhooks->from($queueForEvents),
-                $queueForRealtime->from($queueForEvents)
+                $queueForFunctions,
+                $queueForWebhooks,
+                $queueForRealtime
             ));
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 07edb4e and 4b6d252.

📒 Files selected for processing (1)
  • app/controllers/shared/api.php (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: The Utopia\Queue\Broker\Pool class (aliased as BrokerPool) implements both Publisher and Consumer interfaces, making it directly compatible with constructors expecting Publisher parameters like Func::__construct(protected Publisher $publisher). No wrapping is needed.
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: In the Utopia Queue system, the Pool class implements both Publisher and Consumer interfaces, so BrokerPool (Pool) can be passed directly to constructors expecting Publisher parameters like Func::__construct(protected Publisher $publisher).
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: In Appwrite's codebase, Utopia\Queue\Broker\Pool (aliased as BrokerPool) is a readonly class that implements both Publisher and Consumer interfaces, allowing it to be used directly in constructors expecting either interface type. This is extensively used throughout the codebase for queue operations.
📚 Learning: 2025-08-22T06:23:32.002Z
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: In Appwrite's codebase, Utopia\Queue\Broker\Pool (aliased as BrokerPool) is a readonly class that implements both Publisher and Consumer interfaces, allowing it to be used directly in constructors expecting either interface type. This is extensively used throughout the codebase for queue operations.

Applied to files:

  • app/controllers/shared/api.php
📚 Learning: 2025-08-22T06:23:32.002Z
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: The Utopia\Queue\Broker\Pool class (aliased as BrokerPool) implements both Publisher and Consumer interfaces, making it directly compatible with constructors expecting Publisher parameters like Func::__construct(protected Publisher $publisher). No wrapping is needed.

Applied to files:

  • app/controllers/shared/api.php
📚 Learning: 2025-08-22T06:23:32.002Z
Learnt from: ItzNotABug
PR: appwrite/appwrite#10348
File: app/controllers/shared/api.php:546-546
Timestamp: 2025-08-22T06:23:32.002Z
Learning: In the Utopia Queue system, the Pool class implements both Publisher and Consumer interfaces, so BrokerPool (Pool) can be passed directly to constructors expecting Publisher parameters like Func::__construct(protected Publisher $publisher).

Applied to files:

  • app/controllers/shared/api.php
🧬 Code graph analysis (1)
app/controllers/shared/api.php (2)
src/Appwrite/PubSub/Adapter/Pool.php (1)
  • Pool (9-46)
src/Appwrite/Platform/Tasks/ScheduleBase.php (1)
  • action (72-110)
🔇 Additional comments (3)
app/controllers/shared/api.php (3)

32-32: BrokerPool alias import is correct and necessary for the new injection.

Importing Utopia\Queue\Broker\Pool as BrokerPool aligns with the new $publisherFunctions DI parameter typed as BrokerPool. No issues spotted here.


434-434: Action closure parameterization looks consistent.

BrokerPool $publisherFunctions matches the new injection and keeps $publisher (events) separate from $publisherFunctions (functions). Parameter order also tracks the inject list. LGTM.


546-547: Approved: Valid use of BrokerPool as Publisher for Func

Verified that Appwrite\Event\Func::__construct requires a Publisher and that Utopia\Queue\Broker\Pool implements the Publisher interface (alongside Consumer). No wrapper is needed, and passing your $publisherFunctions directly is correct.

Key confirmations:

  • src/Appwrite/Event/Func.php defines
    public function __construct(protected Publisher $publisher)
  • vendor/utopia-php/queue/src/Queue/Broker/Pool.php reads
    readonly class Pool implements Publisher, Consumer

@ItzNotABug ItzNotABug requested a review from abnegate August 22, 2025 07:12
@abnegate abnegate merged commit 45e31f2 into 1.8.x Aug 22, 2025
161 of 162 checks passed
@abnegate abnegate deleted the fix-users-events branch August 22, 2025 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants