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 previous direct Execution queue usage with a new Bus event system. Adds Bus, Event, Listener base classes, ExecutionCompleted event, and PersistExecution listener. Introduces a bus resource/service and app/listeners.php to load listeners. Updates controllers, router handlers, worker and function worker code to accept Bus and dispatch ExecutionCompleted (including execution and project payloads). Removes queueForExecutions resource registrations and trigger calls. Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 1 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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! |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@app/controllers/general.php`:
- Around line 706-712: The call to $bus->dispatch(new ExecutionCompleted(...))
in the finally block can throw (e.g. PersistExecutionLog::handle) and mask an
earlier exception; wrap the dispatch call in its own try-catch that catches
Throwable, logs the dispatch error (using the controller's logger or error_log)
and does not rethrow so the original exception from the catch block is
preserved; specifically, around $bus->dispatch(...) for the ($type ===
'function' || $type === 'site') case, add try { $bus->dispatch(new
ExecutionCompleted(...)); } catch (\Throwable $e) { /* log error about
ExecutionCompleted dispatch failure including $e->getMessage()/trace */ }.
In `@src/Appwrite/Bus/Event.php`:
- Line 5: The interface declaration `interface Event {}` violates PSR-12 brace
placement; change it so the opening brace is on its own line for the `Event`
interface (i.e., declare `interface Event` then put `{` on the next line) to
satisfy the linter and Pint's `class_definition`/`braces_position` rule.
In `@src/Appwrite/Bus/ExecutionCompleted.php`:
- Around line 7-10: The constructor in class ExecutionCompleted uses inline
empty braces which triggers the PSR-12 braces_position lint; update the
__construct declaration in class ExecutionCompleted so the opening brace is on
the next line and the closing brace is on its own line (i.e., place the method
body braces on separate lines even if empty) to satisfy Pint/PSR-12.
In `@src/Appwrite/Bus/Listener.php`:
- Line 9: The Listener::$callback property is untyped and can be null while
getCallback() declares a non-nullable callable return; add a runtime guard and a
stronger type: declare the property as protected $callback (typed as ?callable
if your PHP version allows) and update getCallback() in class Listener to check
if $this->callback is callable and throw a clear LogicException (or
InvalidArgumentException) if not, so callers like Bus::dispatch never receive
null; alternatively, add a Listener constructor or initialize method that
validates/sets the callback (e.g., require a callable in Listener::__construct
or call setCallback) and ensure subclasses (like PersistExecutionLog) call it.
🧹 Nitpick comments (3)
src/Appwrite/Bus/Bus.php (1)
24-30: Consider isolating listener failures to prevent one listener from blocking others.Currently, if a listener throws an exception,
dispatch()stops iterating and the exception propagates to the caller. As more listeners are added to the bus, a failure in one listener would prevent subsequent listeners from executing. Wrapping each listener invocation in a try-catch would make the bus more resilient.Proposed approach
public function dispatch(Event $event): void { foreach ($this->listeners[$event::class] ?? [] as $listener) { - $deps = array_map($this->resolver, $listener->getInjections()); - ($listener->getCallback())($event, ...$deps); + try { + $deps = array_map($this->resolver, $listener->getInjections()); + ($listener->getCallback())($event, ...$deps); + } catch (\Throwable $e) { + // Log and continue so other listeners are not affected + \Utopia\Console::error('[Bus] Listener ' . $listener::getName() . ' failed: ' . $e->getMessage()); + } } }src/Appwrite/Bus/Listener.php (1)
18-22:desc()is set but never accessible — is this intentional?There is no public
getDesc()method, so$descis write-only. If it's meant for introspection (e.g., listing registered listeners), consider adding a public accessor. If it's purely decorative/internal documentation, this is fine — just flagging for awareness.src/Appwrite/Platform/Workers/Functions.php (1)
254-276: PHPDoc forfail()is out of sync with the actual signature.The
@paramblock (lines 255–263) doesn't listDocument $projectorBus $bus, both of which are in the method signature. Similarly,execute()'s docblock (lines 317–337) is missingWebhook $queueForWebhooks,Bus $bus, andarray $platform.Suggested additions for fail()
/** * `@param` string $message + * `@param` Document $project + * `@param` Bus $bus * `@param` Document $function
src/Utopia/Bus/Listener.php
Outdated
| { | ||
| protected ?string $desc = null; | ||
| protected array $injections = []; | ||
| protected $callback; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check the actual Listener.php file to see the property and method signatures
cat -n src/Appwrite/Bus/Listener.php | head -50Repository: appwrite/appwrite
Length of output: 1251
🏁 Script executed:
# Also search for all classes that extend Listener
rg 'extends Listener' --type=phpRepository: appwrite/appwrite
Length of output: 154
🏁 Script executed:
# Check the Bus::dispatch method to see how it uses the callback
rg -A 5 'dispatch' src/Appwrite/Bus/Bus.php | head -30Repository: appwrite/appwrite
Length of output: 330
🏁 Script executed:
# Check the PersistExecutionLog subclass to see if it properly sets the callback
cat -n src/Appwrite/Bus/Listeners/PersistExecutionLog.phpRepository: appwrite/appwrite
Length of output: 1301
🏁 Script executed:
# Also verify the Bus class structure to confirm dispatch behavior
cat -n src/Appwrite/Bus/Bus.php | head -50Repository: appwrite/appwrite
Length of output: 1010
🏁 Script executed:
# Check if there are any other Listener subclasses besides PersistExecutionLog
rg 'extends Listener' --type=phpRepository: appwrite/appwrite
Length of output: 154
🏁 Script executed:
# Also check for any instantiations of Listener subclasses to confirm usage patterns
rg 'new (Persist|.*Listener)' --type=php | head -20Repository: appwrite/appwrite
Length of output: 13422
$callback is untyped and uninitialized — getCallback() declares callable as the return type but could return null.
The property has no type hint and no default value. If a Listener subclass forgets to call $this->callback(...) during initialization, getCallback() will return null, violating the declared return type. When Bus::dispatch tries to invoke it (($listener->getCallback())($event, ...)), this will throw a TypeError.
Currently, PersistExecutionLog (the only subclass) properly initializes the callback in its constructor, preventing this issue. However, the design is fragile and should be hardened for future subclasses.
Consider either:
- Adding a type hint with a runtime guard in
getCallback(), or - Validating in a constructor or setup method that
$callbackis set before the listener can be used.
Example: Guard in getCallback()
+ protected ?callable $callback = null;
+
public function getCallback(): callable
{
+ if ($this->callback === null) {
+ throw new \LogicException(static::class . ' must set a callback via $this->callback()');
+ }
return $this->callback;
}🤖 Prompt for AI Agents
In `@src/Appwrite/Bus/Listener.php` at line 9, The Listener::$callback property is
untyped and can be null while getCallback() declares a non-nullable callable
return; add a runtime guard and a stronger type: declare the property as
protected $callback (typed as ?callable if your PHP version allows) and update
getCallback() in class Listener to check if $this->callback is callable and
throw a clear LogicException (or InvalidArgumentException) if not, so callers
like Bus::dispatch never receive null; alternatively, add a Listener constructor
or initialize method that validates/sets the callback (e.g., require a callable
in Listener::__construct or call setCallback) and ensure subclasses (like
PersistExecutionLog) call it.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/Appwrite/Bus/Bus.php`:
- Around line 25-31: The dispatch method in Bus::dispatch silently uses a null
resolver and will pass raw injection names when setResolver() hasn't been
called, and an exception in one listener aborts remaining listeners; fix by
adding an explicit guard at the start of dispatch that throws a clear
RuntimeException if $this->resolver is null (or unset) so callers must call
setResolver() first, and wrap the per-listener invocation (the
($listener->getCallback())($event, ...$deps) call) in a try/catch to log or
rethrow per policy so one listener's exception doesn't silently stop others (or
alternatively add a docblock above dispatch stating fail-fast behavior if you
opt to keep current behavior); reference Bus::dispatch, setResolver,
$this->resolver, $listener->getInjections(), and $listener->getCallback().
✨ Benchmark results
⚡ Benchmark Comparison
|
c82f174 to
623d5ea
Compare
There was a problem hiding this comment.
Pull request overview
Refactors execution completion handling by introducing an in-process event Bus with class-based listeners, moving execution log persistence and usage-metric recording into dedicated listeners triggered by an ExecutionCompleted event.
Changes:
- Added
Utopia\Bus\Bus,Event, andListenerabstractions plus anAppwrite\Bus\ExecutionCompletedevent. - Updated HTTP controller and Functions worker to dispatch
ExecutionCompletedinstead of directly triggering execution/usage queues. - Wired Bus + listeners into HTTP/worker/CLI resource containers and enabled PHPStan scanning for the new namespaces.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Utopia/Bus/Listener.php | Introduces a base Listener API for event handling (injections + callback). |
| src/Utopia/Bus/Event.php | Adds a marker interface for bus events. |
| src/Utopia/Bus/Bus.php | Implements event subscription and asynchronous dispatch with dependency resolution. |
| src/Appwrite/Platform/Workers/Functions.php | Dispatches ExecutionCompleted events instead of directly triggering execution log/usage queues. |
| src/Appwrite/Bus/Listeners/Usage.php | New listener to record execution usage metrics on completion. |
| src/Appwrite/Bus/Listeners/Log.php | New listener to persist execution logs via the existing queue. |
| src/Appwrite/Bus/ExecutionCompleted.php | Defines the completion event payload (execution, project, spec). |
| phpstan.neon | Adds new Bus directories to PHPStan analysis paths. |
| composer.json | Adds PSR-4 autoload mapping for Utopia\Bus\. |
| app/worker.php | Registers a bus resource and sets resolver using Server resources. |
| app/listeners.php | Declares listeners to be subscribed to the bus. |
| app/init/resources.php | Removes queueForExecutions HTTP resource wiring (replaced by bus dispatch). |
| app/init/registers.php | Creates and registers the bus and subscribes listeners. |
| app/http.php | Registers bus resource and sets resolver using Http resources. |
| app/controllers/general.php | Dispatches ExecutionCompleted and removes inline execution usage metric additions/disable logic. |
| app/cli.php | Registers bus resource and sets resolver using CLI resources. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
e616afc to
af5820d
Compare
Introduce a generic event bus (Utopia\Bus) with typed events, listener base class, Span instrumentation, and coroutine dispatch. Replace all direct queueForExecutions and inline execution usage calls with ExecutionCompleted event and dedicated listeners (Log, Usage). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fa22ab5 to
577a2bc
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… optional deployment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a Bus with class-based event routing and Listener base class to decouple execution completion signals from their side effects. Controllers/workers now dispatch ExecutionCompleted events, and the PersistExecutionLog listener handles queue persistence.
What does this PR do?
(Provide a description of what this PR does and why it's needed.)
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
Checklist