Skip to content

Add executionId and client IP to execution headers #10344

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

Closed
wants to merge 1 commit into from

Conversation

vermakhushboo
Copy link
Member

@vermakhushboo vermakhushboo commented Aug 20, 2025

What does this PR do?

Fixes #9316

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

  • 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?

@vermakhushboo vermakhushboo requested a review from Meldiron August 20, 2025 11:06
Copy link
Contributor

coderabbitai bot commented Aug 20, 2025

📝 Walkthrough

Walkthrough

The change standardizes execution ID handling and client IP propagation. A single executionId is generated early, added to the request as x-appwrite-execution-id, and reused as the $id for Execution documents. A new x-appwrite-client-ip header is added (from request IP where available; empty otherwise). Workers’ fail and execute paths set/propagate these headers and reuse or create executionId as needed. E2E tests are updated to assert APPWRITE_FUNCTION_EXECUTION_ID matches the execution’s $id and that APPWRITE_FUNCTION_CLIENT_IP is present. The sample function now returns these values from headers and coerces statusCode to a number.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

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 feat-extend-function-headers

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

Security Scan Results for PR

Docker Image Scan Results

Package Version Vulnerability Severity
binutils 2.42-r0 CVE-2025-0840 HIGH
git 2.45.3-r0 CVE-2025-46334 HIGH
git 2.45.3-r0 CVE-2025-48384 HIGH
git 2.45.3-r0 CVE-2025-48385 HIGH
git-init-template 2.45.3-r0 CVE-2025-46334 HIGH
git-init-template 2.45.3-r0 CVE-2025-48384 HIGH
git-init-template 2.45.3-r0 CVE-2025-48385 HIGH
icu 74.2-r0 CVE-2025-5222 HIGH
icu-data-en 74.2-r0 CVE-2025-5222 HIGH
icu-dev 74.2-r0 CVE-2025-5222 HIGH
icu-libs 74.2-r0 CVE-2025-5222 HIGH
libexpat 2.6.4-r0 CVE-2024-8176 HIGH
libxml2 2.12.7-r0 CVE-2024-56171 HIGH
libxml2 2.12.7-r0 CVE-2025-24928 HIGH
libxml2 2.12.7-r0 CVE-2025-27113 HIGH
libxml2 2.12.7-r0 CVE-2025-32414 HIGH
libxml2 2.12.7-r0 CVE-2025-32415 HIGH
pyc 3.12.9-r0 CVE-2024-12718 HIGH
pyc 3.12.9-r0 CVE-2025-4138 HIGH
pyc 3.12.9-r0 CVE-2025-4330 HIGH
pyc 3.12.9-r0 CVE-2025-4517 HIGH
python3 3.12.9-r0 CVE-2024-12718 HIGH
python3 3.12.9-r0 CVE-2025-4138 HIGH
python3 3.12.9-r0 CVE-2025-4330 HIGH
python3 3.12.9-r0 CVE-2025-4517 HIGH
python3-pyc 3.12.9-r0 CVE-2024-12718 HIGH
python3-pyc 3.12.9-r0 CVE-2025-4138 HIGH
python3-pyc 3.12.9-r0 CVE-2025-4330 HIGH
python3-pyc 3.12.9-r0 CVE-2025-4517 HIGH
python3-pycache-pyc0 3.12.9-r0 CVE-2024-12718 HIGH
python3-pycache-pyc0 3.12.9-r0 CVE-2025-4138 HIGH
python3-pycache-pyc0 3.12.9-r0 CVE-2025-4330 HIGH
python3-pycache-pyc0 3.12.9-r0 CVE-2025-4517 HIGH
sqlite-libs 3.45.3-r1 CVE-2025-29087 HIGH
xz 5.6.2-r0 CVE-2025-31115 HIGH
xz-libs 5.6.2-r0 CVE-2025-31115 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
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

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/Workers/Functions.php (1)

402-412: Don’t clobber client IP provided by the caller

In execute(), you overwrite x-appwrite-client-ip with an empty string, discarding the value populated upstream (Create.php or router). Preserve it if already present.

Apply:

-        $headers['x-appwrite-execution-id'] = $executionId ?? '';
+        $headers['x-appwrite-execution-id'] = $executionId ?? '';
         $headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
         $headers['x-appwrite-trigger'] = $trigger;
         $headers['x-appwrite-event'] = $event ?? '';
         $headers['x-appwrite-user-id'] = $user->getId() ?? '';
         $headers['x-appwrite-user-jwt'] = $jwt ?? '';
         $headers['x-appwrite-country-code'] = '';
         $headers['x-appwrite-continent-code'] = '';
         $headers['x-appwrite-continent-eu'] = 'false';
-        $headers['x-appwrite-client-ip'] = '';
+        $headers['x-appwrite-client-ip'] = $headers['x-appwrite-client-ip'] ?? '';

This ensures async HTTP executions also expose the client IP to the function, consistent with the PR goal.

🧹 Nitpick comments (4)
src/Appwrite/Platform/Workers/Functions.php (1)

413-418: Avoid DB read when executionId is empty

Calling getDocument with an empty ID is unnecessary. Guard the lookup and fall back to a new Document.

Apply:

-        $execution = $dbForProject->getDocument('executions', $executionId ?? '');
-        if ($execution->isEmpty()) {
+        $execution = new Document();
+        if (!empty($executionId)) {
+            $execution = $dbForProject->getDocument('executions', $executionId);
+        }
+        if ($execution->isEmpty()) {
             $executionId = ID::unique();
             $headers['x-appwrite-execution-id'] = $executionId;
tests/e2e/Services/Functions/FunctionsCustomClientTest.php (1)

167-179: Consider adding async-path assertions for executionId/client IP

To fully cover the async flow, fetch the execution after completion and assert the responseBody contains the same APPWRITE_FUNCTION_EXECUTION_ID and a non-empty APPWRITE_FUNCTION_CLIENT_IP, similar to the sync assertions above.

I can propose a patch to extend the assertEventually block to parse responseBody and assert both fields—let me know if you want me to draft it.

tests/e2e/Services/Functions/FunctionsCustomServerTest.php (2)

1139-1144: Harden assertions for clearer failures and type safety

To avoid noisy “undefined index” notices and to get stricter equality on IDs, assert key presence first and use assertSame for the ID comparison.

Apply this diff:

-        // Test execution ID and client IP
-        $executionId = $execution['body']['$id'] ?? '';
-        $this->assertNotEmpty($output['APPWRITE_FUNCTION_EXECUTION_ID']);
-        $this->assertEquals($executionId, $output['APPWRITE_FUNCTION_EXECUTION_ID']);
-        $this->assertNotEmpty($output['APPWRITE_FUNCTION_CLIENT_IP']);
+        // Test execution ID and client IP
+        $executionId = $execution['body']['$id'] ?? '';
+        $this->assertArrayHasKey('APPWRITE_FUNCTION_EXECUTION_ID', $output);
+        $this->assertNotEmpty($output['APPWRITE_FUNCTION_EXECUTION_ID']);
+        $this->assertSame($executionId, $output['APPWRITE_FUNCTION_EXECUTION_ID']);
+        $this->assertArrayHasKey('APPWRITE_FUNCTION_CLIENT_IP', $output);
+        $this->assertNotEmpty($output['APPWRITE_FUNCTION_CLIENT_IP']);

1136-1138: Unify type expectations for CPUS/MEMORY across tests

Line 1136-1138 and Line 1170-1171 assert integers for APPWRITE_FUNCTION_CPUS/MEMORY, while Line 1962-1963 assert strings for the same fields. This inconsistency can lead to brittle tests if runtimes or helpers change serialization.

Consider standardizing on one approach (prefer assertSame with explicit types), or cast before asserting to make intent explicit.

Example options:

  • Make all strict integer comparisons:
    • $this->assertSame(1, (int) $output['APPWRITE_FUNCTION_CPUS']);
    • $this->assertSame(1024, (int) $output['APPWRITE_FUNCTION_MEMORY']);
  • Or, if you intend to assert raw string values everywhere, switch to assertSame('1', ...) and assertSame('1024', ...), and update Line 1962-1963 accordingly for consistency.

Also applies to: 1170-1171, 1962-1963

📜 Review details

Configuration used: .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 7eaecd8 and ed16808.

📒 Files selected for processing (6)
  • app/controllers/general.php (1 hunks)
  • src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php (2 hunks)
  • src/Appwrite/Platform/Workers/Functions.php (3 hunks)
  • tests/e2e/Services/Functions/FunctionsCustomClientTest.php (1 hunks)
  • tests/e2e/Services/Functions/FunctionsCustomServerTest.php (1 hunks)
  • tests/resources/functions/basic/index.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
tests/e2e/Services/Functions/FunctionsCustomServerTest.php (1)
src/Appwrite/Utopia/Response.php (1)
  • output (658-740)
src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php (1)
src/Appwrite/Event/Audit.php (1)
  • getIP (113-116)
app/controllers/general.php (1)
src/Appwrite/Event/Audit.php (1)
  • getIP (113-116)
src/Appwrite/Platform/Workers/Functions.php (1)
src/Appwrite/Event/Event.php (1)
  • trigger (337-358)
⏰ 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: scan
  • GitHub Check: Setup & Build Appwrite Image
🔇 Additional comments (7)
app/controllers/general.php (2)

358-367: Single source-of-truth execution ID + client IP propagation looks good

Generating the executionId once and propagating via x-appwrite-execution-id and x-appwrite-client-ip is correct and aligns with the PR objective.


358-367: Optional: prefer proxy-derived client IP

Request::getIP() is inherited from Utopia\Swoole\Request and may not account for X-Forwarded-For/X-Real-IP. Please verify its implementation in Utopia\Swoole\Request. If it doesn’t handle proxy headers, consider falling back to them before calling getIP(), for example:

$clientIp = $request->getHeader('X-Forwarded-For')
    ?: $request->getHeader('X-Real-IP')
    ?: $request->getIP();

$headers = \array_merge([], $requestHeaders);
$headers['x-appwrite-execution-id'] = $executionId ?? '';
$headers['x-appwrite-user-id']      = '';
$headers['x-appwrite-country-code'] = '';
$headers['x-appwrite-continent-code'] = '';
$headers['x-appwrite-continent-eu']   = 'false';
$headers['x-appwrite-client-ip']      = $clientIp;
src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php (2)

224-234: Early executionId generation + client IP header: LGTM

Reusing the same executionId for the header and document ID is correct. Adding x-appwrite-client-ip from the request is aligned with the objective.


224-234: Ensure getIP() Returns the Actual Client IP Behind Proxies

The snippet in src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php relies on $request->getIP(). Before shipping, please verify that Utopia\Swoole\Request::getIP() correctly parses X-Forwarded-For/X-Real-IP (with your trusted‐proxy settings) so it doesn’t surface an internal proxy’s IP to user functions.

Points to verify:

  • Inspect the implementation of Utopia\Swoole\Request::getIP() (in your vendor or Utopia package).
  • Confirm your Swoole/server proxy‐trust configuration aligns with how getIP() selects headers.
  • (Optional) Add a test or integration check that getIP() returns the expected public client address when behind a reverse proxy.
tests/resources/functions/basic/index.js (1)

44-46: Expose executionId and client IP in function output: LGTM

Reading x-appwrite-execution-id and x-appwrite-client-ip from headers and returning them is correct. Coercing statusCode to a number is also a good fix.

tests/e2e/Services/Functions/FunctionsCustomClientTest.php (1)

162-166: Solid assertions for execution ID and client IP

Asserting that APPWRITE_FUNCTION_EXECUTION_ID matches the execution’s $id and APPWRITE_FUNCTION_CLIENT_IP is non-empty verifies the end-to-end contract.

tests/e2e/Services/Functions/FunctionsCustomServerTest.php (1)

1139-1144: Good addition: validates executionId propagation and client IP exposure end-to-end

These assertions directly cover the PR goal: ensuring APPWRITE_FUNCTION_EXECUTION_ID matches the execution document $id and APPWRITE_FUNCTION_CLIENT_IP is populated. This is valuable e2e coverage for both header propagation and runtime exposure.

Comment on lines +358 to 367
$executionId = ID::unique();

$headers = \array_merge([], $requestHeaders);
$headers['x-appwrite-execution-id'] = $executionId ?? '';
$headers['x-appwrite-user-id'] = '';
$headers['x-appwrite-country-code'] = '';
$headers['x-appwrite-continent-code'] = '';
$headers['x-appwrite-continent-eu'] = 'false';
$headers['x-appwrite-client-ip'] = $request->getIP();

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure new headers are persisted/observable where intended

If you expect x-appwrite-execution-id and x-appwrite-client-ip to appear under the execution.requestHeaders for observability/debugging, confirm they are included in FUNCTION_ALLOWLIST_HEADERS_REQUEST; otherwise they’ll be filtered out.

Run this to verify and adjust if needed:


🏁 Script executed:

#!/bin/bash
# Check where FUNCTION_ALLOWLIST_HEADERS_REQUEST/RESPONSE are defined and whether they include the new headers.
rg -nP -C3 'FUNCTION_ALLOWLIST_HEADERS_(REQUEST|RESPONSE)'
echo
echo "Checking for the new headers in allowlists:"
rg -nP -C2 'x-appwrite-(execution-id|client-ip)'

Length of output: 10238


Add x-appwrite-execution-id and x-appwrite-client-ip to the request-header allowlist

We’re currently merging and filtering request headers in app/controllers/general.php (lines 393–396) against FUNCTION_ALLOWLIST_HEADERS_REQUEST (defined in app/init/constants.php), but that constant only contains:

const FUNCTION_ALLOWLIST_HEADERS_REQUEST = [
    'content-type',
    'agent',
    'content-length',
    'host'
];

As a result, both x-appwrite-execution-id and x-appwrite-client-ip will be dropped and won’t surface in execution.requestHeaders. To fix this, update the allowlist to include them:

• File: app/init/constants.php
Update around line 147:

- const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
+ const FUNCTION_ALLOWLIST_HEADERS_REQUEST = [
+     'content-type',
+     'agent',
+     'content-length',
+     'host',
+     'x-appwrite-execution-id',
+     'x-appwrite-client-ip',
+ ];
🤖 Prompt for AI Agents
In app/init/constants.php around line 147, the
FUNCTION_ALLOWLIST_HEADERS_REQUEST array is missing 'x-appwrite-execution-id'
and 'x-appwrite-client-ip', so those headers are filtered out and won't appear
in execution.requestHeaders; update the array to include these two header names
(lowercased) so the header merge in app/controllers/general.php (lines 358–367
and the later filtering at 393–396) will preserve them.

Comment on lines +224 to 234
$executionId = ID::unique();
$headers['x-appwrite-execution-id'] = $executionId ?? '';
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
$headers['x-appwrite-country-code'] = '';
$headers['x-appwrite-continent-code'] = '';
$headers['x-appwrite-continent-eu'] = 'false';
$headers['x-appwrite-client-ip'] = $request->getIP();

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm allowlists include the new headers

If you want x-appwrite-execution-id and x-appwrite-client-ip visible in execution.requestHeaders, ensure they’re in FUNCTION_ALLOWLIST_HEADERS_REQUEST.

Run:


🏁 Script executed:

#!/bin/bash
# Verify the allowlist includes the new headers
rg -nP -C3 'const\s+FUNCTION_ALLOWLIST_HEADERS_REQUEST|define\s*\(\s*[\'"]FUNCTION_ALLOWLIST_HEADERS_REQUEST'
rg -nP -C1 'x-appwrite-execution-id|x-appwrite-client-ip'

Length of output: 116


I’ll re-run the search with proper quoting and broaden the context to ensure we find the constant definition and its contents:


🏁 Script executed:

#!/bin/bash
# 1) Locate where FUNCTION_ALLOWLIST_HEADERS_REQUEST is defined
rg -n --type=php "FUNCTION_ALLOWLIST_HEADERS_REQUEST" -C3

# 2) Inspect that definition for the two new headers
#    (display up to 10 lines around the definition and filter for our headers)
rg -n --type=php "FUNCTION_ALLOWLIST_HEADERS_REQUEST" -C10 | rg -n -F "'x-appwrite-execution-id'"
rg -n --type=php "FUNCTION_ALLOWLIST_HEADERS_REQUEST" -C10 | rg -n -F "'x-appwrite-client-ip'"

Length of output: 3741


Add missing headers to FUNCTION_ALLOWLIST_HEADERS_REQUEST

The allowlist defined in app/init/constants.php currently only includes:

  • content-type
  • agent
  • content-length
  • host

It must be updated to include both x-appwrite-execution-id and x-appwrite-client-ip, otherwise these headers won’t be visible in execution.requestHeaders.

• File: app/init/constants.php

- const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
+ const FUNCTION_ALLOWLIST_HEADERS_REQUEST = [
+     'content-type',
+     'agent',
+     'content-length',
+     'host',
+     'x-appwrite-execution-id',
+     'x-appwrite-client-ip',
+ ];
🤖 Prompt for AI Agents
In src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php around
lines 224 to 234, the code sets x-appwrite-execution-id and x-appwrite-client-ip
headers but the global allowlist in app/init/constants.php
(FUNCTION_ALLOWLIST_HEADERS_REQUEST) is missing these entries; update
app/init/constants.php to add 'x-appwrite-execution-id' and
'x-appwrite-client-ip' to the FUNCTION_ALLOWLIST_HEADERS_REQUEST array (match
casing/format used by other entries, typically lowercase header names) so these
headers are preserved and exposed in execution.requestHeaders.

Comment on lines +267 to 274
$executionId = ID::unique();
$headers['x-appwrite-execution-id'] = $executionId ?? '';
$headers['x-appwrite-trigger'] = $trigger;
$headers['x-appwrite-event'] = $event ?? '';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
$headers['x-appwrite-client-ip'] = '';

Copy link
Contributor

@coderabbitai coderabbitai bot Aug 20, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: $headers used before initialization in fail()

$headers is written to without being initialized, which will raise a notice and rely on PHP’s implicit array creation. Initialize it explicitly.

Apply:

     ): void {
-        $executionId = ID::unique();
+        $executionId = ID::unique();
+        $headers = [];
         $headers['x-appwrite-execution-id'] = $executionId ?? '';
         $headers['x-appwrite-trigger'] = $trigger;
         $headers['x-appwrite-event'] = $event ?? '';
         $headers['x-appwrite-user-id'] = $user->getId() ?? '';
         $headers['x-appwrite-user-jwt'] = $jwt ?? '';
-        $headers['x-appwrite-client-ip'] = '';
+        $headers['x-appwrite-client-ip'] = '';
🤖 Prompt for AI Agents
In src/Appwrite/Platform/Workers/Functions.php around lines 267 to 274, the
$headers array is written to before being initialized which can trigger PHP
notices; initialize $headers as an empty array (e.g., $headers = []; ) before
assigning values and then proceed to set the x-appwrite-* keys so the array
exists explicitly and avoids implicit creation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Verify your comment

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@vermakhushboo Looking at the code, I can confirm my comment is accurate. In the fail() method:

  1. Method signature has no $headers parameter: The method signature is private function fail(string $message, Database $dbForProject, Document $function, string $trigger, string $path, string $method, Document $user, string $jwt = null, string $event = null,): void

  2. First use of $headers is an assignment: Line 268 shows $headers['x-appwrite-execution-id'] = $executionId ?? ''; without any prior initialization

  3. PHP behavior: This relies on PHP's implicit array creation, which works but generates a notice in strict error reporting and is not considered best practice

  4. Later usage confirms it's treated as array: The method later uses foreach ($headers as $key => $value) confirming $headers is expected to be an array

The fix I suggested ($headers = []; before the first assignment) would explicitly initialize the array and eliminate any potential PHP notices while maintaining the same functionality.

My comment and suggested fix are correct and should be applied for cleaner, more explicit code.


🏁 Script executed:

#!/bin/bash
# Let's verify the fail() method implementation to confirm $headers initialization
ast-grep --pattern $'private function fail(
  $$$
) {
  $$$
}'

Length of output: 63


@vermakhushboo I can now definitively verify my comment is correct. Looking at the fail() method implementation:

The issue confirmed:

  • Line 267: $executionId = ID::unique();
  • Line 268: $headers['x-appwrite-execution-id'] = $executionId ?? '';First use of $headers without initialization
  • Lines 269-272: More assignments to $headers array elements
  • Line 280: foreach ($headers as $key => $value)Used as an array later

The problem:
The $headers variable is used as an array starting at line 268 without being explicitly declared/initialized first. This relies on PHP's implicit array creation behavior.

My suggested fix is correct: Adding $headers = []; before line 268 would explicitly initialize the array, following PHP best practices and avoiding any potential notices in strict error reporting modes.

The comment and suggested fix are accurate and should be implemented for cleaner, more maintainable code.


🏁 Script executed:

#!/bin/bash
# Search for the fail() method implementation
rg -n -A 20 "private function fail" src/Appwrite/Platform/Workers/Functions.php

Length of output: 894


@vermakhushboo Verification confirmed - my comment is accurate.

Looking at the exact code from the fail() method:

Lines 256-266: Method signature has no $headers parameter
Line 268: $headers['x-appwrite-execution-id'] = $executionId ?? '';First use of $headers without initialization
Lines 269-273: Subsequent assignments to $headers array elements
Line 276: foreach ($headers as $key => $value)Used as array

The issue is confirmed:

  • $headers is accessed as an array starting at line 268
  • No prior initialization of $headers exists
  • This relies on PHP's implicit array creation (generates notices in strict mode)

My suggested fix is correct:

$executionId = ID::unique();
$headers = [];  // ← Add this line
$headers['x-appwrite-execution-id'] = $executionId ?? '';

This follows PHP best practices by explicitly initializing variables before use. The comment stands as accurate and the fix should be implemented.


🧬 Code Graph Analysis Results
  • File: src/Appwrite/Event/Event.php

    • Lines: 10-593
    • Summary:
      • Defines the central Event system used by all workers.
      • Key concepts:
        • Event constants for queue names and class names (e.g., DATABASE_QUEUE_NAME, ... FUNCTIONS_QUEUE_NAME, etc.).
        • Properties for queue, class, event, payload, context, project, user, userId, etc.
        • preparePayload(): builds a payload including project, user, type, collection, document, database, and generated events.
        • trigger(): enqueues the prepared payload to the appropriate queue, with error handling that respects the critical flag.
        • preparePayload merges base payload with trimmed values and supports masking sensitive info.
        • Static generateEvents(pattern, params): expands a base event pattern into all concrete events, substituting placeholders with provided params or wildcards, validating required params, and returning an array of event strings.
        • parseEventPattern(pattern): parses a single event pattern into its components (type, resource, subResource, action, etc.).
        • from(Event): copies state from another Event instance.
      • Notable behavior:
        • If an event is non-critical and enqueue fails, trigger() returns false instead of throwing.
        • The event-generation logic supports complex nested resources and placeholders.
  • File: src/Executor/Executor.php

    • Lines: 10-459
    • Summary:
      • Provides runtime orchestration for executing functions/deployments.
      • Key methods:
        • createRuntime(projectId, deploymentId, source, image, version, cpus, memory, timeout, ...): deploys a runtime container for execution and returns runtime metadata.
        • getLogs(deploymentId, projectId, timeout, callback): streams logs from a runtime.
        • deleteRuntime(projectId, deploymentId, suffix): deletes a runtime instance.
        • createExecution(projectId, deploymentId, body, variables, timeout, image, source, entrypoint, version, path, method, headers, cpus, memory, logging, runtimeEntrypoint, requestTimeout): creates and starts an execution request for a deployment.
          • Builds a request payload with runtimeId, variables, timeout, path, method, headers, image, source, entrypoint, resources, version, and additional fields.
          • Sends request to /runtimes/{runtimeId}/executions via HTTP (multipart/form-data).
          • Validates HTTP status, decodes response, and normalizes headers/duration/startTime in the response body.
        • createCommand(...): creates a runtime command.
        • Private call(endpoint, method, path, headers, params, decode, timeout, callback): low-level HTTP client using curl.
          • Supports JSON and multipart/form-data body formats.
          • Handles streaming callbacks, response parsing, and error propagation.
          • Decodes JSON/multipart responses, extracts status codes, and surfaces errors.
      • Notable behavior:
        • Throws on HTTP error statuses (>= 400) with the status code.
        • Supports self-signed certificates via a flag.
        • Returns structured response body containing statusCode, headers, body, duration, etc.
  • File: src/Appwrite/Platform/Workers/StatsUsage.php

    • Lines: 16-481
    • Summary:
      • Stats usage worker collects and aggregates usage metrics per project.
      • Key aspects:
        • getName(): returns 'stats-usage'.
        • Constructor wires in dependencies and sets up the callback for the action.
        • action(Message $message, callable $getProjectDB, callable $getLogsDB, Registry $register): core entrypoint.
          • Aggregates metrics from payload['metrics'] and reduces per project using payload['reduce'] data.
          • Batches metrics and commits to DB when thresholds are reached or interval elapses.
          • Maintains internal state: $stats, $lastTriggeredTime, $keys, and $projects for batched writes.
        • commitToDb(callable $getProjectDB): writes batched stats into each project’s DB using stats collection; also triggers logs writing for usage data.
        • prepareForLogsDB()/writeToLogsDB(): optionally dual-writes metrics to logs DB depending on environment variable _APP_STATS_USAGE_DUAL_WRITING.
        • Maintains a set of base metrics to skip from dual-writing (skipBaseMetrics) and a set of metrics to avoid parent-id variants (skipParentIdMetrics).
      • Notable behavior:
        • Aggregates and persists metrics such as deployments, builds, executions, storage, and more across time periods (1h, 1d, inf).
        • Designed for batch writes to improve performance.
  • File: src/Appwrite/Event/Database.php

    • Lines: 9-142
    • Summary:
      • Defines a Database event type used by the database-related workers.
      • Key methods:
        • setType(string $type): self
        • getType(): string
        • setDatabase(Document $database): self
        • getDatabase(): ?Document
        • setCollection(Document $collection): self
        • getCollection(): ?Document
        • setDocument(Document $document): self
        • getDocument(): ?Document
        • getQueue(): string
        • preparePayload(): array
      • preparePayload() returns a payload including project, user, type, collection, document, database, and events generated via Event::generateEvents($this->getEvent(), $this->getParams()).
      • Notable behavior:
        • getQueue() derives a queue name from the project's database DSN, with a fallback for legacy setups (namespace-based queue naming).
  • File: src/Appwrite/Event/Realtime.php

    • Lines: 47-51
    • Code (complete snippet):
      public function setSubscribers(array $subscribers): self
      {
      $this->subscribers = $subscribers;
      return $this;
      }
  • File: src/Appwrite/Platform/Workers/Webhooks.php

    • Lines: 22-25
    • Code (complete snippet):
      public static function getName(): string
      {
      return 'webhooks';
      }

Copy link

✨ Benchmark results

  • Requests per second: 965
  • Requests with 200 status code: 173,739
  • P99 latency: 0.191911091

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 965 983
200 173,739 176,978
P99 0.191911091 0.199240177

@vermakhushboo
Copy link
Member Author

Closing as I fixed #9147

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.

1 participant