Skip to content

refactor: replace CLI and migration logError callbacks with spans - #4

Open
anurag6569201 wants to merge 1 commit into
qa/agent-appwrite-appwrite/pr-04-13491/basefrom
qa/agent-appwrite-appwrite/pr-04-13491/head
Open

anurag6569201 wants to merge 1 commit into
qa/agent-appwrite-appwrite/pr-04-13491/basefrom
qa/agent-appwrite-appwrite/pr-04-13491/head

Conversation

@anurag6569201

Copy link
Copy Markdown

What does this PR do?

Remove logError callback injection from CLI tasks and migration workers. Global CLI/worker error handlers export terminal failures once; recovered migration and embedding failures attach warning attributes using Span::add(). The sampler exports warning listener spans and marks recovered failures as warnings without attaching a Throwable or changing successful HTTP responses into Sentry errors. Partial embedding responses also expose the existing error count on the span.

CLI success now finishes its operation span, and worker errors before project/span initialization still get an error span. Pagination read failures propagate instead of potentially reprocessing the previous page; skipValidation() restores validation on both success and failure.

Realtime and presence reporting keep their existing handling for this first pass.

Test Plan

  • Existing migration tests: 12 tests / 84 assertions pass locally and in the Swoole Docker runtime.
  • Actual CLI entrypoint in Docker: version produces one success span; an unknown task produces one terminal error span and exits 1.
  • Manual sampler/pagination drill: recovered listener warning is exported without an exception; terminal error retains error severity; next listener has no stale warning; failed second page stops traversal and restores database validation. Reverting the sampler and pagination hunks fails both checks; restoring them passes.
  • Pint and targeted PHPStan pass for all changed files.

No new HTTP/queue end-to-end drill or staging deployment has been performed. Existing migration HTTP E2E remains a CI gate; partial embedding warning behavior has not been exercised against a live embedding backend.

Related PRs and Issues

Cloud follow-up: https://github.com/appwrite-labs/cloud/pull/5670 (held while this CE change goes first). Cloud overrides Migrations::action(): remove its logger parameter and forwarded argument when updating its server-ce dependency. Do not bump Cloud to this CE commit without that coordinated signature update.

Source merge-base: 231e47ac41b57498f6c92e80ebaaeed3c3c504fb
Source head: 44fc6355bfbd90f09631c7997415fad734836c65

@shipwright-agent

Copy link
Copy Markdown

⛔ Shipwright · Blocked

Recommendation: do not merge PR #4 · Tier T3
Checks: 0 total · 0 needing attention

Next step: resolve the blocking findings before merge.

Findings (18)

  • CRITICAL Removing the try/catch in Action::foreachDocument changes error semantics: database find failures now propagate instead of being logged and skipped. · src/Appwrite/Platform/Action.php:54
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL The sampler returns true for any span with an error before applying selective tracing filters. · app/init/span.php:22
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH In Action.php, the previous try/catch around '$database->find' swallowed exceptions and invoked the logError callback. · src/Appwrite/Platform/Action.php:54
    • Fix: Fix the review finding before release.
  • HIGH The test asserts assertNotInstanceOf(Throwable::class, $span->getError()) on a span that was never finished with an error, so the assertion is trivially true and does not verify th · tests/unit/Migration/MigrationsWorkerExportTest.php:292
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH CLI init unconditionally calls Span::init("task.$taskName"). · app/cli.php:202
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH Worker error handler finishes the current span with an error. · app/worker.php:155
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH Embedding batch error handling overwrites warning.message and warning.code on each failed batch, so only the last error is retained and earlier diagnostic information is lost. · src/Appwrite/Platform/Modules/Databases/Http/Embeddings/Text/Create.php:115
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH Migration error handling now calls Span::add directly without the previous try/catch around reportError. · src/Appwrite/Platform/Workers/Migrations.php:593
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • …and 10 more findings in the check details.

Fireworks usage: 17,469 input · 1,862 output · 19,331 total tokens · $0.0051 · 24s · 0 fix iteration(s)

Open the Shipwright check for full evidence and the audit bundle. Use /shipwright rerun to verify again.

array_unshift($newQueries, Query::cursorAfter($latestDocument));
}
$newQueries[] = Query::limit($limit);
$results = $database->skipValidation(fn () => $database->find($collection, $newQueries));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

Removing the try/catch in Action::foreachDocument changes error semantics: database find failures now propagate instead of being logged and skipped.

Impact: Removing the try/catch in Action::foreachDocument changes error semantics: database find failures now propagate instead of being logged and skipped. Any transient DB error aborts the entire document iteration, potentially leaving partial processing and breaking callers that relied on continuation.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Comment thread app/init/span.php

$sampler = function (Span $span) use ($traceEnabled, $traceProjectId, $traceFunctionId): bool {
// Terminal diagnostics must survive tracing filters, including failures
// before project or function resolution.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

The sampler returns true for any span with an error before applying selective tracing filters.

Impact: The sampler returns true for any span with an error before applying selective tracing filters. This bypasses _APP_TRACE_PROJECT_ID/_APP_TRACE_FUNCTION_ID and exports error spans for unrelated projects/functions, leaking error details across tenants.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

array_unshift($newQueries, Query::cursorAfter($latestDocument));
}
$newQueries[] = Query::limit($limit);
$results = $database->skipValidation(fn () => $database->find($collection, $newQueries));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

In Action.php, the previous try/catch around '$database->find' swallowed exceptions and invoked the logError callback.

Impact: In Action.php, the previous try/catch around '$database->find' swallowed exceptions and invoked the logError callback. The new code removes the catch entirely and uses 'skipValidation'. Any exception from 'find' now propagates out of 'foreachDocument', changing the observable behavior for callers that previously relied on the error being logged and the loop continuing.

Suggested fix: Fix the review finding before release.

$this->assertSame(1, $reported);
$this->assertSame('Mail unavailable', $span->get('warning.message'));
$this->assertSame('migration-id', $span->get('migration.id'));
$this->assertNotInstanceOf(\Throwable::class, $span->getError());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

The test asserts assertNotInstanceOf(Throwable::class, $span->getError()) on a span that was never finished with an error, so the assertion is trivially true and does not verify th

Impact: The test asserts assertNotInstanceOf(Throwable::class, $span->getError()) on a span that was never finished with an error, so the assertion is trivially true and does not verify the warning-reporting behavior it claims to test.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Comment thread app/cli.php
});

$cli->shutdown()->action(function () {
Span::current()?->finish();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

CLI init unconditionally calls Span::init("task.$taskName").

Impact: CLI init unconditionally calls Span::init("task.$taskName"). If a span already exists, this creates a duplicate/nested span, and shutdown only finishes the current span, leaving the original span unfinished or misattributing task duration.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Comment thread app/worker.php
Console::error('[Error] Line: ' . $error->getLine());
->action(function (Throwable $error) use ($workerName) {
// Initialization can fail before the message span or project is available.
$span = Span::current() ?? Span::init("worker.{$workerName}");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

Worker error handler finishes the current span with an error.

Impact: Worker error handler finishes the current span with an error. If the framework expects to finish that span later, this can cause double-finish or loss of subsequent span attributes.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

} catch (\Exception $e) {
$totalErrors += \count($batch);
$this->logError($e, $model);
Span::add('embedding.model', $model);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

Embedding batch error handling overwrites warning.message and warning.code on each failed batch, so only the last error is retained and earlier diagnostic information is lost.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

'source' => $migration->getAttribute('source') ?? '',
'destination' => $migration->getAttribute('destination') ?? '',
];
Span::add('warning.message', $th->getMessage());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

Migration error handling now calls Span::add directly without the previous try/catch around reportError.

Impact: Migration error handling now calls Span::add directly without the previous try/catch around reportError. If span storage or add throws, the migration error path itself can throw and escape the catch block.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Comment thread app/init/span.php

$sampler = function (Span $span) use ($traceEnabled, $traceProjectId, $traceFunctionId): bool {
// Terminal diagnostics must survive tracing filters, including failures
// before project or function resolution.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

The sampler now returns true for any span with an error before applying the listener or selective tracing filters.

Impact: The sampler now returns true for any span with an error before applying the listener or selective tracing filters. This means terminal error spans bypass the _APP_TRACE_PROJECT_ID/_APP_TRACE_FUNCTION_ID filters and are always exported, which may leak error details for unrelated projects/functions and contradicts the comment that only non-terminal spans are filtered.

Suggested fix: Fix the review finding before release.

Comment thread app/cli.php
});

$cli->shutdown()->action(function () {
Span::current()?->finish();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

In cli.php, the init action calls 'Span::init("task.$taskName")' unconditionally.

Impact: In cli.php, the init action calls 'Span::init("task.$taskName")' unconditionally. If the CLI task has already started a span elsewhere, this creates a nested/duplicate span and the shutdown action finishes only the current span, potentially leaving the original span unfinished or misattributing the task duration.

Suggested fix: Fix the review finding before release.

Comment thread app/worker.php
Console::error('[Error] Line: ' . $error->getLine());
->action(function (Throwable $error) use ($workerName) {
// Initialization can fail before the message span or project is available.
$span = Span::current() ?? Span::init("worker.{$workerName}");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

The worker error handler now calls '$span->finish(error: $error)' on the current span.

Impact: The worker error handler now calls '$span->finish(error: $error)' on the current span. If the current span is the message span that the worker framework expects to remain open for subsequent processing or for the framework to finish, finishing it here can cause the framework to attempt to finish an already-finished span or lose subsequent span attributes.

Suggested fix: Fix the review finding before release.

} catch (\Exception $e) {
$totalErrors += \count($batch);
$this->logError($e, $model);
Span::add('embedding.model', $model);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

The embedding error handling now calls 'Span::add('warning.message', $e->getMessage())' and 'Span::add('warning.code', $e->getCode())' inside a loop over batches.

Impact: The embedding error handling now calls 'Span::add('warning.message', $e->getMessage())' and 'Span::add('warning.code', $e->getCode())' inside a loop over batches. If multiple batches fail, each iteration overwrites the previous warning message and code, so only the last error is retained in the span, losing diagnostic information for earlier failures.

Suggested fix: Fix the review finding before release.

'source' => $migration->getAttribute('source') ?? '',
'destination' => $migration->getAttribute('destination') ?? '',
];
Span::add('warning.message', $th->getMessage());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

The migration error handling now records warnings via 'Span::add' but no longer calls '$this->reportError'.

Impact: The migration error handling now records warnings via 'Span::add' but no longer calls '$this->reportError'. The previous 'reportError' had a try/catch around the logging callback to prevent logging failures from escaping. The new direct 'Span::add' calls are not wrapped, so if the span storage or add operation throws, the migration error path itself can throw and escape the catch block.

Suggested fix: Fix the review finding before release.

$this->assertSame(1, $reported);
$this->assertSame('Mail unavailable', $span->get('warning.message'));
$this->assertSame('migration-id', $span->get('migration.id'));
$this->assertNotInstanceOf(\Throwable::class, $span->getError());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · MEDIUM

The test 'testNotificationFailureIsReportedAsWarning' sets a span storage and initializes a span, but the assertion '$this->assertNotInstanceOf(\Throwable::class, $span->getError()

Impact: The test 'testNotificationFailureIsReportedAsWarning' sets a span storage and initializes a span, but the assertion '$this->assertNotInstanceOf(\Throwable::class, $span->getError())' is always true because the span was never finished with an error. The test does not actually verify that the notification failure is reported as a warning in the way the production code would, making the test misleading.

Suggested fix: Fix the review finding before release.

Comment thread app/init/span.php
return true;
}

$warning = $span->get('warning.message') !== null || (int) $span->get('embedding.errors') > 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · LOW

The sampler's warning detection is coupled to an embedding-specific attribute, embedding.errors, inside a generic span initializer.

Impact: The sampler's warning detection is coupled to an embedding-specific attribute, embedding.errors, inside a generic span initializer. This makes the generic tracing path depend on a domain-specific key and is confusing for future maintainers.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Console::error($error->getMessage() . ' Migration: ' . $migration->getId());
$this->reportError($error, $migration);
Console::error('Invalid initiating user sequence for export migration. Migration: ' . $migration->getId());
Span::add('warning.message', 'Invalid initiating user sequence for export migration.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · LOW

resolveExportUser records warning.code as integer 0, but the sampler does not use warning.code to decide warning export.

Impact: resolveExportUser records warning.code as integer 0, but the sampler does not use warning.code to decide warning export. This value appears unused and may mislead readers into thinking it drives warning behavior.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Comment thread app/init/span.php
return true;
}

$warning = $span->get('warning.message') !== null || (int) $span->get('embedding.errors') > 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · LOW

The warning detection uses '(int) $span->get('embedding.errors') > 0'.

Impact: The warning detection uses '(int) $span->get('embedding.errors') > 0'. If 'embedding.errors' is not set, 'get' returns null and '(int) null' is 0, which is safe. However, if the value is a non-numeric string, the cast silently becomes 0 and the warning is not emitted. More importantly, the same key is later set as an integer in Create.php, so this is consistent, but the sampler's warning logic is coupled to an…

Suggested fix: Fix the review finding before release.

Console::error($error->getMessage() . ' Migration: ' . $migration->getId());
$this->reportError($error, $migration);
Console::error('Invalid initiating user sequence for export migration. Migration: ' . $migration->getId());
Span::add('warning.message', 'Invalid initiating user sequence for export migration.');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · LOW

In 'resolveExportUser', the new code calls 'Span::add('warning.code', 0)' with an integer literal 0.

Impact: In 'resolveExportUser', the new code calls 'Span::add('warning.code', 0)' with an integer literal 0. The sampler's warning detection checks '(int) $span->get('embedding.errors') > 0', but this is a different key. The warning.code value of 0 is not used by the sampler, so these warnings may not be exported as warnings unless another mechanism reads warning.code.

Suggested fix: Fix the review finding before release.

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