Skip to content

Commit 94773d5

Browse files
committed
AUT-5433: Return combined error when both session store reads fail
Adds DualStoreError with typed primary and secondary properties so callers can identify which stores failed easier.
1 parent ffcbb83 commit 94773d5

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

src/config/dual-session-store.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ import { type SessionData, Store } from "express-session";
22
import { logger } from "../utils/logger.js";
33
import { isDeepStrictEqual } from "node:util";
44

5+
export class DualStoreError extends Error {
6+
constructor(
7+
public readonly primary: Error,
8+
public readonly secondary: Error
9+
) {
10+
super("Both session stores failed");
11+
}
12+
}
13+
514
export class DualSessionStore extends Store {
615
constructor(
716
private readonly primary: Store,
@@ -32,7 +41,16 @@ export class DualSessionStore extends Store {
3241

3342
this.secondary.get(sid, (secondaryErr, secondarySession) => {
3443
if (primaryErr) {
35-
cb(secondaryErr, secondarySession);
44+
if (secondaryErr) {
45+
logger.warn(
46+
{ err: secondaryErr, sid, store: this.secondaryLabel },
47+
"Secondary session read failed"
48+
);
49+
}
50+
const error = secondaryErr
51+
? new DualStoreError(primaryErr, secondaryErr)
52+
: null;
53+
cb(error, secondarySession);
3654
return;
3755
}
3856

test/unit/dual-session-store.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { afterEach, beforeEach, describe, it } from "mocha";
22
import { expect, sinon } from "../utils/test-utils.js";
3-
import { DualSessionStore } from "../../src/config/dual-session-store.js";
3+
import {
4+
DualSessionStore,
5+
DualStoreError,
6+
} from "../../src/config/dual-session-store.js";
47
import { type SessionData, Store } from "express-session";
58

69
describe("DualSessionStore", () => {
@@ -47,14 +50,18 @@ describe("DualSessionStore", () => {
4750
});
4851
});
4952

50-
it("should return secondary error when both primary and secondary fail", (done) => {
53+
it("should return combined error when both primary and secondary fail", (done) => {
54+
const primaryErr = new Error("primary failure");
5155
const secondaryErr = new Error("secondary failure");
52-
primary.get = sinon.fake((_sid, cb) => cb(new Error("primary failure")));
56+
primary.get = sinon.fake((_sid, cb) => cb(primaryErr));
5357
secondary.get = sinon.fake((_sid, cb) => cb(secondaryErr));
5458
store = new DualSessionStore(primary, secondary, "Redis", "DynamoDB");
5559

5660
store.get(sid, (err) => {
57-
expect(err).to.equal(secondaryErr);
61+
expect(err).to.be.instanceOf(DualStoreError);
62+
expect(err.message).to.equal("Both session stores failed");
63+
expect(err.primary).to.equal(primaryErr);
64+
expect(err.secondary).to.equal(secondaryErr);
5865
done();
5966
});
6067
});

0 commit comments

Comments
 (0)