chore: log rank-check live call failures with error code and keyword, warn on upstream flakes (#542)

This commit is contained in:
Ben Senescu 2026-08-26 11:51:26 -04:00 committed by GitHub
parent 824e914eb1
commit c7ff28c30a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 25 additions and 15 deletions

View File

@ -138,10 +138,11 @@ function createAuthenticatedFetch(
); );
error.name = "DataForSEOHttpError"; error.name = "DataForSEOHttpError";
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log // UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
// what they capture, so log here to keep the provider's failure rate // what they capture, so warn here to keep the provider's failure rate
// visible in Workers Observability. // visible in Workers Observability. Warn, not error: there is nothing in
// the app to fix.
if (code === "UPSTREAM_UNAVAILABLE") if (code === "UPSTREAM_UNAVAILABLE")
console.error("dataforseo.upstream-http-failed", { console.warn("dataforseo.upstream-http-failed", {
path, path,
status: response.status, status: response.status,
}); });

View File

@ -246,10 +246,11 @@ export function assertOk<T extends DataforseoTaskLike>(
const detailedMessage = describeInvalidField(message, task); const detailedMessage = describeInvalidField(message, task);
const isUpstreamFailure = isUpstreamServerErrorTask(task); const isUpstreamFailure = isUpstreamServerErrorTask(task);
// UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log // UPSTREAM_UNAVAILABLE is non-reportable, and the error handlers only log
// what they capture, so log here to keep the provider's failure rate — and // what they capture, so warn here to keep the provider's failure rate — and
// the only remaining record of the message — visible in Workers Observability. // the only remaining record of the message — visible in Workers
// Observability. Warn, not error: there is nothing in the app to fix.
if (isUpstreamFailure) if (isUpstreamFailure)
console.error("dataforseo.upstream-task-failed", { console.warn("dataforseo.upstream-task-failed", {
path, path,
status: task.status_code, status: task.status_code,
message: task.status_message, message: task.status_message,

View File

@ -10,6 +10,7 @@ import type {
RankCheckResult, RankCheckResult,
RankCheckTaskInput, RankCheckTaskInput,
} from "@/server/lib/dataforseo"; } from "@/server/lib/dataforseo";
import { AppError } from "@/server/lib/errors";
import type { RankTrackingConfig } from "@/types/schemas/rank-tracking"; import type { RankTrackingConfig } from "@/types/schemas/rank-tracking";
import { KEYWORDS_PER_BATCH } from "@/shared/rank-tracking"; import { KEYWORDS_PER_BATCH } from "@/shared/rank-tracking";
import { pgStep } from "@/server/workflows/pgStep"; import { pgStep } from "@/server/workflows/pgStep";
@ -100,16 +101,23 @@ async function checkBatchLive(
), ),
); );
const results: RankCheckResultWithDevice[] = []; const results: RankCheckResultWithDevice[] = [];
for (const outcome of settled) { settled.forEach((outcome, index) => {
if (outcome.status === "fulfilled") { if (outcome.status === "fulfilled") {
results.push(outcome.value); results.push(outcome.value);
} else { return;
console.error( }
`[rank-check] ${ctx.runId} live call failed:`, const reason = outcome.reason;
outcome.reason, const code = reason instanceof AppError ? reason.code : "UNKNOWN";
const message = reason instanceof Error ? reason.message : String(reason);
// DataForSEO erring on its own side is a provider flake, not our bug: the
// keyword just misses this run and finalize reports it to the user. Every
// other rejection (no credits, bad API key) is ours and stays at error.
const log = code === "UPSTREAM_UNAVAILABLE" ? console.warn : console.error;
const task = tasks[index];
log(
`[rank-check] ${ctx.runId} live call failed (${code}) keyword="${task.keyword}" device=${task.device}: ${message}`,
); );
} });
}
if (results.length > 0) { if (results.length > 0) {
await RankTrackingRepository.insertSnapshots( await RankTrackingRepository.insertSnapshots(
mapResultsToSnapshotRows(ctx.runId, results), mapResultsToSnapshotRows(ctx.runId, results),

View File

@ -39,8 +39,8 @@ const NON_REPORTABLE_ERROR_CODES = new Set<ErrorCode>([
"AUDIT_ALREADY_RUNNING", "AUDIT_ALREADY_RUNNING",
// An external provider (DataForSEO) failing on its own side. Nothing in the // An external provider (DataForSEO) failing on its own side. Nothing in the
// app to fix, and it drowned real exceptions. Note the error handlers only // app to fix, and it drowned real exceptions. Note the error handlers only
// log what they capture, so every throw site logs its own line to keep the // log what they capture, so every throw site warns on its own line to keep
// failure rate visible in Workers Observability. // the failure rate visible in Workers Observability.
"UPSTREAM_UNAVAILABLE", "UPSTREAM_UNAVAILABLE",
]); ]);