fix(sessions): preserve legacy data when exclusive projection fails (#11469)

Validated in a combined sub-batch worktree off release/v3.8.51 tip.
- Focused test: exclusive-session-observability.test.ts — part of sub-batch's 165/165 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for the narrow failure boundary and the privacy-conscious warning (proven not to leak the caught error's sensitive fields) — a projection failure discarding valid legacy data was a real regression from #11389.
This commit is contained in:
KaspaPulse
2026-08-26 00:08:29 +00:00
committed by GitHub
parent 721ee2a038
commit be6cbe7de5
3 changed files with 114 additions and 20 deletions

View File

@@ -0,0 +1 @@
- **fix(sessions):** preserve legacy Sessions data when the additive exclusive-session projection is unavailable, returning an empty projection and warning only once per contiguous outage instead of failing the endpoint; the Sessions badge intentionally reflects the merged legacy and exclusive row count introduced by [#11389](https://github.com/diegosouzapw/OmniRoute/pull/11389)

View File

@@ -10,32 +10,47 @@ import { getExclusiveLeaseOccupancy } from "@/lib/db/exclusiveConnectionLeases";
import { getProviderConnectionDisplayMetadata } from "@/lib/db/providers";
import { getAccountDisplayName } from "@/lib/display/names";
import { getPendingRequests } from "@/lib/usage/usageHistory";
import { buildExclusiveDashboardSessions } from "@/lib/sessionObservability";
import {
buildExclusiveDashboardSessions,
type ExclusiveDashboardSession,
} from "@/lib/sessionObservability";
const EXCLUSIVE_PROJECTION_WARNING = "[SESSIONS] Exclusive session projection unavailable";
let exclusiveProjectionWarningEmitted = false;
export async function GET() {
try {
const sessions = getActiveSessions();
const count = getActiveSessionCount();
const byApiKey = getAllActiveSessionCountsByKey();
let exclusiveSessions: ExclusiveDashboardSession[] = [];
// Reuse the hard-lease authority added by #10362. The API-key policy derives
// the managed candidate set; SQLite occupancy is the source of truth for
// which of those connections are actually leased right now.
const managedConnectionIds = Array.from(await getExclusiveLeaseConnectionIds());
const occupancy = getExclusiveLeaseOccupancy(managedConnectionIds);
const leasedConnectionIds = new Set(occupancy.keys());
const connectionNames = new Map(
getProviderConnectionDisplayMetadata([...leasedConnectionIds]).map((connection) => [
connection.id,
getAccountDisplayName(connection),
])
);
const exclusiveSessions = buildExclusiveDashboardSessions(
leasedConnectionIds,
getPendingRequests().byAccount,
sessions,
connectionNames
);
try {
// Reuse the hard-lease authority added by #10362. The API-key policy derives
// the managed candidate set; SQLite occupancy is the source of truth for
// which of those connections are actually leased right now.
const managedConnectionIds = Array.from(await getExclusiveLeaseConnectionIds());
const occupancy = getExclusiveLeaseOccupancy(managedConnectionIds);
const leasedConnectionIds = new Set(occupancy.keys());
const connectionNames = new Map(
getProviderConnectionDisplayMetadata([...leasedConnectionIds]).map((connection) => [
connection.id,
getAccountDisplayName(connection),
])
);
exclusiveSessions = buildExclusiveDashboardSessions(
leasedConnectionIds,
getPendingRequests().byAccount,
sessions,
connectionNames
);
exclusiveProjectionWarningEmitted = false;
} catch {
if (!exclusiveProjectionWarningEmitted) {
exclusiveProjectionWarningEmitted = true;
console.warn(EXCLUSIVE_PROJECTION_WARNING);
}
}
return NextResponse.json({
count,

View File

@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import test, { mock } from "node:test";
import {
buildExclusiveDashboardSessions,
@@ -319,6 +319,84 @@ test("sessions API keeps legacy fields additive and decorates only in-flight lea
assert.deepEqual(releasedBody.exclusiveSessions, []);
});
test("sessions API deduplicates projection warnings per contiguous outage", async () => {
const secretFailure = {
ownerHash: "d".repeat(64),
generation: 91,
apiKeyId: "private-api-key-id",
credential: "private-credential",
token: "private-token",
connectionIdentity: "private-connection-identity",
leaseOwnership: "private-lease-ownership",
fencingMaterial: "private-fencing-material",
};
const nowMock = mock.method(Date, "now", () => BASE_TIME);
sessionManager.touchSession("legacy-fallback", "legacy-connection");
sessionManager.registerKeySession("legacy-key", "legacy-fallback");
const db = core.getDbInstance();
const originalPrepare = db.prepare.bind(db);
let projectionFails = true;
const prepareMock = mock.method(db, "prepare", (sql: string) => {
if (projectionFails) throw new Error(JSON.stringify(secretFailure));
return originalPrepare(sql);
});
const warnings: unknown[][] = [];
const warnMock = mock.method(console, "warn", (...args: unknown[]) => {
warnings.push(args);
});
try {
const firstResponse = await sessionsRoute.GET();
const firstBody = (await firstResponse.json()) as {
count: number;
sessions: Array<{ sessionId: string; connectionId: string | null }>;
byApiKey: Record<string, number>;
exclusiveSessions: unknown[];
};
const secondResponse = await sessionsRoute.GET();
const secondBody = (await secondResponse.json()) as typeof firstBody;
assert.equal(firstResponse.status, 200);
assert.equal(secondResponse.status, 200);
assert.deepEqual(secondBody, firstBody);
assert.equal(firstBody.count, 1);
assert.equal(firstBody.sessions[0].sessionId, "legacy-fallback");
assert.equal(firstBody.sessions[0].connectionId, "legacy-connection");
assert.deepEqual(firstBody.byApiKey, { "legacy-key": 1 });
assert.deepEqual(firstBody.exclusiveSessions, []);
assert.deepEqual(warnings, [["[SESSIONS] Exclusive session projection unavailable"]]);
projectionFails = false;
const recoveredResponse = await sessionsRoute.GET();
assert.equal(recoveredResponse.status, 200);
projectionFails = true;
const laterOutageResponse = await sessionsRoute.GET();
const laterOutageBody = (await laterOutageResponse.json()) as typeof firstBody;
assert.equal(laterOutageResponse.status, 200);
assert.deepEqual(laterOutageBody, firstBody);
assert.deepEqual(warnings, [
["[SESSIONS] Exclusive session projection unavailable"],
["[SESSIONS] Exclusive session projection unavailable"],
]);
const observableOutput = JSON.stringify({
firstBody,
secondBody,
laterOutageBody,
warnings,
});
for (const forbidden of Object.values(secretFailure)) {
assert.equal(observableOutput.includes(String(forbidden)), false);
}
} finally {
warnMock.mock.restore();
prepareMock.mock.restore();
nowMock.mock.restore();
}
});
test("sessions route keeps raw lease SQL out of the API and sanitizes failures", () => {
const route = fs.readFileSync(
new URL("../../src/app/api/sessions/route.ts", import.meta.url),