[fix] Bound the session-records fetch and consume the persist-drop signal - #5501
[fix] Bound the session-records fetch and consume the persist-drop signal#5501ardaerzin wants to merge 2 commits into
Conversation
Two small robustness gaps in the reconstruction pipeline: - fetchSessionRecords sits on the turn's critical path (the prompt waits on it), but had no timeout, so a stalled query would wait on Undici's long request-level timeout instead of failing fast to the inbound-history fallback. Add an env-tunable AbortSignal.timeout (5s default). - takePersistFailures had no caller: in durable mode a dropped record was counted into a per-session map that nothing read, so the signal was lost and the map grew unbounded. Consume it at the turn-end drain (flush) — warn when the durable log is incomplete and clear the counter.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
Railway Preview Environment
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 452202da-8419-4099-beb1-53772089ca5d
📒 Files selected for processing (3)
services/runner/src/sessions/persist.tsservices/runner/src/sessions/records-query.tsservices/runner/tests/unit/session-persist.test.ts
| function queryTimeoutMs(): number { | ||
| const n = Number(process.env.AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS); | ||
| return Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate target file:"
fd -a 'records-query\.ts$' . || true
echo
echo "Relevant source:"
file="$(fd 'records-query\.ts$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
nl -ba "$file" | sed -n '1,120p'
fi
echo
echo "Search usages of queryTimeoutMs:"
rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS" . || true
echo
echo "Runtime/version facts if Node is available:"
if command -v node >/dev/null 2>&1; then
node -e "console.log(process.version)"
node - <<'JS'
const tests = [0.001, 0.5, 0.999, 1, 1.999, 2, 5000, -0.001, 0, 1e-9, 1e-5];
console.log("AbortSignal.timeout(0):", AbortSignal.timeout(0).reason);
for (const n of tests) {
const computed = Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000;
let reason = "<no signal>";
if (computed > 0 && computed !== Infinity && Number.isFinite(computed)) {
try {
const sig = AbortSignal.timeout(computed);
reason = sig.reason?.name ?? String(sig.reason);
sig.addEventListener("abort", () => {
// no-op
});
} catch (e) {
reason = `${e.name}: ${e.message}`;
}
}
console.log(`${n} -> computed=${computed} signal reason=${reason}`);
}
JS
fiRepository: Agenta-AI/agenta
Length of output: 298
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="$(fd 'records-query\\.ts$' . | head -n 1 || true)"
echo "target=$file"
echo
echo "Relevant source:"
awk '{printf "%6d %s\n", NR, $0}' "$file" | sed -n '1,120p'
echo
echo "Query timeout usages:"
rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS|AbortSignal\\.timeout" . || true
echo
echo "Runtime/version facts if Node is available:"
if command -v node >/dev/null 2>&1; then
node -e "console.log(process.version)"
node - <<'JS'
const tests = [0.001, 0.5, 0.999, 1, 1.999, 2, 5000, -0.001, 0, 1e-9, 1e-5];
console.log("AbortSignal.timeout(0):", AbortSignal.timeout(0).reason);
for (const n of tests) {
const computed = Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000;
let reason = "<no signal>";
if (computed > 0 && computed !== Infinity && Number.isFinite(computed)) {
try {
const sig = AbortSignal.timeout(computed);
sig.addEventListener("abort", () => {});
reason = sig.reason?.name ?? String(sig.reason);
console.log(`${n} -> computed=${computed} signal reason=${reason}`);
} catch (e) {
console.log(`${n} -> computed=${computed} signal throws ${e.name}: ${e.message}`);
}
}
}
JS
fiRepository: Agenta-AI/agenta
Length of output: 1401
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate records-query.ts:"
fd 'records-query\\.ts$' . || true
echo
echo "target candidate:"
file="$(fd 'records-query\\.ts$' . | head -n 1 || true)"
echo "$file"
if [ -n "$file" ] && [ -f "$file" ]; then
echo
echo "Relevant source:"
awk '{printf "%6d %s\n", NR, $0}' "$file" | sed -n '1,120p'
echo
echo "Query timeout usages:"
rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS|AbortSignal\\.timeout" "$file" . || true
fiRepository: Agenta-AI/agenta
Length of output: 199
Keep the computed timeout strictly positive.
Values below 1ms pass validation, but Math.floor(n) produces 0, so an AbortSignal.timeout(0) signal can abort the query immediately. Clamp or reject sub-millisecond values before constructing the signal.
Context
Two small robustness gaps in the last-message-only reconstruction pipeline (#5486), separate from the QA-finding PRs (#5488-#5491, #5494).
Changes
fetchSessionRecordssits on the turn's critical path (the prompt waits on it), so a stalled query would hang on Undici's long request-level timeout instead of falling back to the inbound history. Added an env-tunableAbortSignal.timeout(AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS, 5s default).takePersistFailureshad no caller: in durable mode a dropped record was counted into a per-session map that nothing read, so the signal was lost and the map grew unbounded. Now consumed at the turn-end drain (flush) — it warns when the durable log is incomplete and clears the counter.Tests
flushnow consumes the count.Note
Neither is a QA finding; both surfaced while reviewing the reconstruction path. Independent of the other stacked PRs — merges into #5486 in any order.