kleydon / prisma-session-store

Express session store for Prisma
MIT License
116 stars 18 forks source link

WriteConflict under MongoDB #97

Open SunburntRock89 opened 1 year ago

SunburntRock89 commented 1 year ago

Simply navigating to any page gives me

set(): Error: 
Invalid `this.prisma[this.sessionModelName].update()` invocation in
D:\Documents\Projects\Lunus\Rewrite\node_modules\@quixo3\prisma-session-store\dist\lib\prisma-session-store.js:492:81

  489 case 3:
  490     _a.trys.push([3, 8, , 9]);
  491     if (!(existingSession !== null)) return [3 /*break*/, 5];
→ 492     return [4 /*yield*/, this.prisma[this.sessionModelName].update(
  Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: RawDatabaseError { code: "unknown", message: "Command failed (WriteConflict): WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.)" } })
Error: 
Invalid `this.prisma[this.sessionModelName].update()` invocation in
D:\Documents\Projects\Lunus\Rewrite\node_modules\@quixo3\prisma-session-store\dist\lib\prisma-session-store.js:492:81

  489 case 3:
  490     _a.trys.push([3, 8, , 9]);
  491     if (!(existingSession !== null)) return [3 /*break*/, 5];
→ 492     return [4 /*yield*/, this.prisma[this.sessionModelName].update(
  Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: RawDatabaseError { code: "unknown", message: "Command failed (WriteConflict): WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.)" } })
    at RequestHandler.request (D:\Documents\Projects\Lunus\Rewrite\node_modules\@prisma\client\runtime\index.js:49026:15)
    at async PrismaClient._request (D:\Documents\Projects\Lunus\Rewrite\node_modules\@prisma\client\runtime\index.js:49919:18

Presumably there's multiple update commands being ran at the same time?

kleydon commented 1 year ago

Perhaps this is the result of using multiple prisma clients? E.g, one passed to the PrismaSessionStore constructor, other(s) for other backend functionality?

The one passed to the PrismaSessionStore constructor has a "background process" checking to see if sessions have expired; perhaps the conflict is related to this check?

(Just a paranoid theory; I haven't looked at the code.)

Also - not using a separate prisma client for the PrismaSessionStore constructor (i.e. passing in a pre-existing prisma client, used elsewhere in the backend) has historically proven problematic for some users; though its not clear to me why that might be the case, or if it is still the case. Might be worth a try.

SunburntRock89 commented 1 year ago

Double checked and I'm sure that only 1 prisma client is present.

...passing in a pre-existing prisma client, used elsewhere in the backend) has historically proven problematic for some users

That's frowned upon by Prisma themselves so probably not a great thing to have to put up with. I don't think that this would be symptomatic of that anyway, it's presumable that 2 different queries within the lib are trying to update the same thing at the same time. I see a few .updateMany queries in log mode, maybe it's one of them? (what do they actually do?)

SunburntRock89 commented 1 year ago

Just had a brainwave, could this be caused by the fact that this library manually calls prisma.$connect?

Edit: seemingly not

kleydon commented 1 year ago

Guessing not... But maybe? Possibly relevant, from the docs:

It is not necessary to call $connect() thanks to the lazy connect behavior: The PrismaClient instance connects lazily when the first request is made to the API ($connect() is called for you under the hood).

If you need the first request to respond instantly and cannot wait for a lazy connection to be established, you can explicitly call prisma.$connect() to establish a connection to the data source

To avoid too many connections in a long-running application, we recommend that you use a single instance of PrismaClient across your application.

@SunburntRock89 - Earlier, you wrote:

Double checked and I'm sure that only 1 prisma client is present. ...passing in a pre-existing prisma client, used elsewhere in the backend) has historically proven problematic for some users That's frowned upon by Prisma themselves so probably not a great thing to have to put up with.

Curious: If Prisma is recommending using a single instance of PrismaClient, why is it frowned upon to pass an existing prisma client from back-end code to the prisma-session-store library code?

SunburntRock89 commented 1 year ago

Curious: If Prisma is recommending using a single instance of PrismaClient, why is it frowned upon to pass an existing prisma client from back-end code to the prisma-session-store library code?

Sorry, can see why that statement might be confusing. I meant that creating multiple clients was frowned upon because you implied doing that. You're only meant to use one client.

kleydon commented 1 year ago

Ah - got it.

I suppose one wrinkle is that some people (not all) report issues (I believe Typescript typing issues) when they try to pass a pre-existing prisma client to the PrismaSessionStore constructor; in this case, people are likely using at least two clients (one for prisma-session-store, another for back-end code). But Prisma does support multiple clients, so I'm not yet convinced that two clients should be a problem.

SunburntRock89 commented 1 year ago

Yeah, multiple clients shouldn't be an issue regardless, it's just a connection pooling thing iirc. Making multiple pools means far more connections, some of which won't be touched at all. It's probably more a thing of trying to prevent people from making a new connection pool for every single part of their app.

kleydon commented 1 year ago

Thanks @SunburntRock89; good to get your sense of this...

SunburntRock89 commented 1 year ago

@kleydon Whilst we (were) here, do you happen to know why, when there's no updateMany queries in the session store code, logging queries in prisma returns several updateMany queries to the Session table?

Another thing: It may be beneficial to edit the library so that, where possible, JSON types are used instead of a JSON string.

kleydon commented 1 year ago

Whilst we (were) here, do you happen to know why, when there's no updateMany queries in the session store code, logging queries in prisma returns several updateMany queries to the Session table?

Good question. Guessing prisma is doing something "under the hood", but I'm not 100% certain.

kleydon commented 1 year ago

It may be beneficial to edit the library so that, where possible, JSON types are used instead of a JSON string.

I'll add an issue for considering this.

It's unclear to me whether there are circumstances where this might "lock things down" too much, or not.