Skip to content

Commit

Permalink
fix: ensure CSR works with a capped collection
Browse files Browse the repository at this point in the history
The findOneAndDelete() operation is not authorized on a capped
collection, so the connection state recovery would always fail when
fetching the session.

> MongoServerError: cannot remove from a capped collection

Related: #20
  • Loading branch information
darrachequesne committed Jan 23, 2024
1 parent 0c80f7f commit d3fa038
Show file tree
Hide file tree
Showing 2 changed files with 422 additions and 124 deletions.
64 changes: 54 additions & 10 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -806,28 +806,22 @@ export class MongoAdapter extends Adapter {
try {
results = await Promise.all([
// could use a sparse index on [data.pid] (only index the documents whose type is EventType.SESSION)
this.mongoCollection.findOneAndDelete({
type: EventType.SESSION,
"data.pid": pid,
}),
this.findSession(pid),
this.mongoCollection.findOne({
type: EventType.BROADCAST,
_id: eventOffset,
}),
]);
} catch (e) {
debug("error while fetching session: %s", (e as Error).message);
return Promise.reject("error while fetching session");
}

const result = (results[0]?.ok
? results[0].value // mongodb@5
: results[0]) as unknown as WithId<Document>; // mongodb@6

if (!result || !results[1]) {
if (!results[0] || !results[1]) {
return Promise.reject("session or offset not found");
}

const session = result.data;
const session = results[0].data;

// could use a sparse index on [_id, nsp, data.opts.rooms, data.opts.except] (only index the documents whose type is EventType.BROADCAST)
const cursor = this.mongoCollection.find({
Expand Down Expand Up @@ -889,4 +883,54 @@ export class MongoAdapter extends Adapter {

return session;
}

private findSession(
pid: PrivateSessionId
): Promise<WithId<Document> | undefined> {
const isCollectionCapped = !this.addCreatedAtField;
if (isCollectionCapped) {
return this.mongoCollection
.findOne(
{
type: EventType.SESSION,
"data.pid": pid,
},
{
sort: {
_id: -1,
},
}
)
.then((result) => {
if (!result) {
debug("session not found");
return;
}

if (result.data.sid) {
debug("session found, adding tombstone");

// since the collection is capped, we cannot remove documents from it, so we add a tombstone to prevent recovering the same session twice
// note: we could also have used two distinct collections, one for the events (capped) and the other for the sessions (not capped, with a TTL)
const TOMBSTONE_SESSION = { pid, tombstone: true };
this.persistSession(TOMBSTONE_SESSION);

return result;
} else {
debug("tombstone session found");
}
});
} else {
return this.mongoCollection
.findOneAndDelete({
type: EventType.SESSION,
"data.pid": pid,
})
.then((result) => {
return result?.ok && result.value
? result.value // mongodb@5
: (result as unknown as WithId<Document>); // mongodb@6
});
}
}
}
Loading

0 comments on commit d3fa038

Please sign in to comment.