neondatabase / serverless

Connect to Neon PostgreSQL from serverless/worker/edge functions
https://www.npmjs.com/package/@neondatabase/serverless
MIT License
321 stars 13 forks source link

Can't call WebSocket send() after close(). #10

Closed jeremyjacob closed 1 year ago

jeremyjacob commented 1 year ago

Thank you to the NeonDB team for this library. After facing issues with Kysely and PlanetScale we took a look at migrating to Zapatos with NeonDB. Currently we are just testing the integration but are considering a full migration if everything goes smoothly.

Steps to reproduce

Unknown as the issue has appeared in a large-ish codebase after a migration to @neondatabase/serverless

Expected result

Query result is returned

Actual result

POST /api/v1/internal.authentication.begin 500 Internal Server Error (279.52ms, waitUntil: 279.54ms)
[mf:err] Unhandled Promise Rejection: TypeError: Can't call WebSocket send() after close().
    at _WebSocket.[kSend] (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@miniflare+web-sockets@2.12.1/node_modules/@miniflare/web-sockets/src/websocket.ts:262:13)
    at _WebSocket.send (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@miniflare+web-sockets@2.12.1/node_modules/@miniflare/web-sockets/src/websocket.ts:254:10)
    at /Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:43:4406
    at Timeout._onTimeout (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@miniflare+core@2.12.1/node_modules/@miniflare/core/src/standards/timers.ts:20:13)

Environment

@neondatabase/serverless v0.2.8 with pg shim for Zapatos. Using Cloudflare workers. Error appears when running locally, although I have not tried deploying this code. My main request handler looks like this:

app.all("/api/v1/*", async (c) => {
    const pool = new Pool({ connectionString: c.env.DATABASE_URL });
    const res = await fetchRequestHandler({
        endpoint: "/api/v1",
        req: c.req as unknown as Request,
        router: appRouter,
        createContext: createCreateContext({ c, pool }),
    });

    c.executionCtx.waitUntil(pool.end());
    return res;
});
jeremyjacob commented 1 year ago

Upon further inspection, it looks like this error might be cause by an exception raised downstream:

TypeError: Converting circular structure to JSON
  --> starting at object with constructor 'ServiceWorkerGlobalScope'
  --- property 'global' closes the circle
  at JSON.stringify (<anonymous>)
  at ru (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:1:50489)
  at St (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:1:50249)
  at prepareValue (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:1:51516)
  at Ec (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:43:10678)
  at Object.bc [as bind] (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:43:11000)
  at hn.bind (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:43:21467)
  at Pr.prepare (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:2:10925)
  at Pr.submit (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:2:10572)
  at jt._pulseQueryQueue (/Users/jeremyjacob/Documents/GitHub/Project/node_modules/.pnpm/@neondatabase+serverless@0.2.8/node_modules/@neondatabase/serverless/index.js:43:30257)
jeremyjacob commented 1 year ago

Turns out I've been using self as in the global instead of Zapatos's db.self. This, besides not working, creates an object that references itself and cannot be stringified, raising the above error.

jawj commented 1 year ago

Good to hear you got to the bottom of this.

I have actually now added to Zapatos a run-time check for self (i.e. globalThis) embedded in queries, because that seems like a rather unfortunate footgun. It should be in the next Zapatos release.

jeremyjacob commented 1 year ago

That's great to hear! Thank you for that.