Fix test failure and address Greptile review comments
- Fix "keeps alive" test: set lastOutputAt to current time so idle reaper doesn't kill the test run (seed used a stale date) - Wire up lastOutputAt and startedAt params in test seed fixture - Remove dead wasIdle variable in clearDetachedRunWarning - Fix deferred lastOutputAt flush to use actual last output timestamp instead of wall-clock time at flush Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
942d023148
commit
aa88db7238
2 changed files with 11 additions and 7 deletions
|
|
@ -129,7 +129,8 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => {
|
||||||
processLossRetryCount: input?.processLossRetryCount ?? 0,
|
processLossRetryCount: input?.processLossRetryCount ?? 0,
|
||||||
errorCode: input?.runErrorCode ?? null,
|
errorCode: input?.runErrorCode ?? null,
|
||||||
error: input?.runError ?? null,
|
error: input?.runError ?? null,
|
||||||
startedAt: now,
|
startedAt: input?.startedAt ?? now,
|
||||||
|
lastOutputAt: input?.lastOutputAt ?? null,
|
||||||
updatedAt: new Date("2026-03-19T00:00:00.000Z"),
|
updatedAt: new Date("2026-03-19T00:00:00.000Z"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -159,6 +160,7 @@ describeEmbeddedPostgres("heartbeat orphaned process recovery", () => {
|
||||||
const { runId, wakeupRequestId } = await seedRunFixture({
|
const { runId, wakeupRequestId } = await seedRunFixture({
|
||||||
processPid: child.pid ?? null,
|
processPid: child.pid ?? null,
|
||||||
includeIssue: false,
|
includeIssue: false,
|
||||||
|
lastOutputAt: new Date(),
|
||||||
});
|
});
|
||||||
const heartbeat = heartbeatService(db);
|
const heartbeat = heartbeatService(db);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1506,7 +1506,6 @@ export function heartbeatService(db: Db) {
|
||||||
.then((rows) => rows[0] ?? null);
|
.then((rows) => rows[0] ?? null);
|
||||||
if (!updated) return null;
|
if (!updated) return null;
|
||||||
|
|
||||||
const wasIdle = updated.errorCode === null; // errorCode was cleared
|
|
||||||
await appendRunEvent(updated, await nextRunEventSeq(updated.id), {
|
await appendRunEvent(updated, await nextRunEventSeq(updated.id), {
|
||||||
eventType: "lifecycle",
|
eventType: "lifecycle",
|
||||||
stream: "system",
|
stream: "system",
|
||||||
|
|
@ -2509,27 +2508,30 @@ export function heartbeatService(db: Db) {
|
||||||
|
|
||||||
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
|
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
|
||||||
let lastOutputAtFlushPending = false;
|
let lastOutputAtFlushPending = false;
|
||||||
|
let lastOutputAtLatest: Date | null = null;
|
||||||
const onLog = async (stream: "stdout" | "stderr", chunk: string) => {
|
const onLog = async (stream: "stdout" | "stderr", chunk: string) => {
|
||||||
const sanitizedChunk = redactCurrentUserText(chunk, currentUserRedactionOptions);
|
const sanitizedChunk = redactCurrentUserText(chunk, currentUserRedactionOptions);
|
||||||
if (stream === "stdout") stdoutExcerpt = appendExcerpt(stdoutExcerpt, sanitizedChunk);
|
if (stream === "stdout") stdoutExcerpt = appendExcerpt(stdoutExcerpt, sanitizedChunk);
|
||||||
if (stream === "stderr") stderrExcerpt = appendExcerpt(stderrExcerpt, sanitizedChunk);
|
if (stream === "stderr") stderrExcerpt = appendExcerpt(stderrExcerpt, sanitizedChunk);
|
||||||
const ts = new Date().toISOString();
|
const ts = new Date().toISOString();
|
||||||
|
lastOutputAtLatest = new Date(ts);
|
||||||
|
|
||||||
// Batch lastOutputAt writes — flush at most once per 30 seconds to avoid DB churn
|
// Batch lastOutputAt writes — flush at most once per 30 seconds to avoid DB churn
|
||||||
if (!lastOutputAtFlushPending) {
|
if (!lastOutputAtFlushPending) {
|
||||||
lastOutputAtFlushPending = true;
|
lastOutputAtFlushPending = true;
|
||||||
|
// Flush immediately on first output
|
||||||
|
await db.update(heartbeatRuns)
|
||||||
|
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
||||||
|
.where(eq(heartbeatRuns.id, runId));
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
lastOutputAtFlushPending = false;
|
lastOutputAtFlushPending = false;
|
||||||
|
if (!lastOutputAtLatest) return;
|
||||||
db.update(heartbeatRuns)
|
db.update(heartbeatRuns)
|
||||||
.set({ lastOutputAt: new Date(), updatedAt: new Date() })
|
.set({ lastOutputAt: lastOutputAtLatest, updatedAt: new Date() })
|
||||||
.where(eq(heartbeatRuns.id, runId))
|
.where(eq(heartbeatRuns.id, runId))
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => logger.warn({ err, runId }, "failed to flush lastOutputAt"));
|
.catch((err) => logger.warn({ err, runId }, "failed to flush lastOutputAt"));
|
||||||
}, 30_000);
|
}, 30_000);
|
||||||
// Also flush immediately on first output
|
|
||||||
await db.update(heartbeatRuns)
|
|
||||||
.set({ lastOutputAt: new Date(ts), updatedAt: new Date() })
|
|
||||||
.where(eq(heartbeatRuns.id, runId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle) {
|
if (handle) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue