-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using pg-gateway, pglite & prisma #4
Comments
Hey @BracketJohn thanks for the detailed issue. This is good to know - I haven't tried connecting prisma to pg-gateway yet so don't know exactly what might be going wrong. Regardless I am certainly open to adding this as another example. Give me a bit to debug this one and I'll get back to you 😃 |
Hey @gregnr 👋 Big thanks for the reply & looking into it - glad, that the issue is helpful! Looking forward to anything you may find, have a good one! (: |
Hey @gregnr - quick q: Is there anything I can do to support resolving this / is there any insights you've already gathered? E.g., if it seems likely to you that it is unrelated to Thanks and have a good one! |
Hey @BracketJohn, sorry for the slowness on this. We're in the middle of a pretty major refactor which I was hoping to get done before moving forward with more examples/tests, but it's taking longer than I expected. I just took a stab at Prisma + pg-gateway + PGlite and am also receiving the same error as you:
Right now I'm trying to determine if this is a pg-gateway issue or a PGlite issue. In case you're interested in the details - I'm logging the protocol messages back and forth between Prisma and PGlite and noticing that the trip-up seems to happen during extended query protocol messages. For some reason PGlite responds with nothing (empty I'm going to continue digging and will keep you updated as I learn more. This is almost certainly a bigger issue than Prisma so will be important to get to the bottom of it. |
Just an update - I've narrowed the problem down to how PGlite handles the extended query protocol. A similar issue exists on PGlite that tracks this: electric-sql/pglite#223 Once that is solved Prisma (and all other PG clients using extended queries) should JustWork™ 😃 |
Thanks for the updates & in-depth info - very helpful and interesting 🙏 Will follow the fix of electric-sql/pglite#223 closely then! |
So the pglite folks have fixed electric-sql/pglite#223 🎊 There also was a release Maybe just ambiguity, definitely worth for me to try out whether it works now! Will report back here, once I get to that. |
@gregnr I just tried out ❯ pnpm i -D [email protected]
ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@jsr%2Fstd__bytes: Not Found - 404
This error happened while installing the dependencies of [email protected]
@jsr/std__bytes is not in the npm registry, or you have no permission to fetch it. I guess this is related to: pg-gateway/packages/pg-gateway/package.json Lines 43 to 47 in 4809cfc
Just sharing it in case you did not notice. I know that by using an unofficial |
@BracketJohn you're too fast 😆 this bug was caused by those deps living under JSR and losing the |
With regard to PGlite - 0.2.6 does indeed fix the extended query issues, but we noticed one more bug where error messages do not follow up with a |
Good to hear! I saw that they just released 0.2.7 which includes the fix for the latest bug 🐛 |
I further test everything out with I learned the following:
[1] diff for readme example to work - const server = net.createServer((socket) => {
+ const server = net.createServer(async (socket) => { [2] error thrown on > vite-node prisma/pglite.ts
Server listening on port 5432
Error: write EPIPE
at afterWriteDispatched (node:internal/stream_base_commons:161:15)
at writeGeneric (node:internal/stream_base_commons:152:3)
at Socket._writeGeneric (node:net:952:11)
at Socket._write (node:net:964:8)
at writeOrBuffer (node:internal/streams/writable:570:12)
at _write (node:internal/streams/writable:499:10)
at Socket.Writable.write (node:internal/streams/writable:508:10)
at Object.write (node:internal/webstreams/adapters:215:63)
at invokePromiseCallback (node:internal/webstreams/util:180:10)
at node:internal/webstreams/util:185:23 {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}
Client disconnected [3] prepared statement already exists error [nitro] [unhandledRejection] PrismaClientUnknownRequestError:
Invalid `prisma.positionTemplate.findFirst()` invocation:
Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind: QueryError(PostgresError { code: "42P05", message: "prepared statement \"s19\" already exists", severity: "ERROR", detail: None, column: None, hint: None }), transient: false })
at _n.handleRequestError (/home/runner/work/the-project/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:7117)
at _n.handleAndLogRequestError (/home/runner/work/the-project/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:6235)
at _n.request (/home/runner/work/the-project/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:122:5919)
at async l (/home/runner/work/the-project/node_modules/.pnpm/@[email protected][email protected]/node_modules/@prisma/client/runtime/library.js:131:9116)
at async seed (file:///home/runner/work/the-project/.nuxt/dev/index.mjs:9144:37)
at async Object.run (file:///home/runner/work/the-project/.nuxt/dev/index.mjs:9279:24)
at async runTask (file:///home/runner/work/the-project/.nuxt/dev/index.mjs:1732:17)
at async file:///home/runner/work/the-project/.nuxt/dev/index.mjs:6377:3 {
clientVersion: '5.16.2'
} [4] Script used to import net from 'node:net'
import { PGlite } from '@electric-sql/pglite'
import { fromNodeSocket } from 'pg-gateway/node'
import { join } from 'pathe'
// create a single instance of the db, so that subsequent requests use the same db
const db = new PGlite({ dataDir: join(import.meta.dirname, 'pglite-data') })
const server = net.createServer(async (socket) => {
await fromNodeSocket(socket, {
serverVersion: '16.3',
auth: {
// No password required
method: 'trust',
},
async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady
},
// Hook into each client message
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return
}
// Forward raw message to PGlite and send response to client
return await db.execProtocolRaw(data)
},
})
socket.on('end', () => {
console.info('Client disconnected')
})
})
server.listen(5432, () => {
console.info('Server listening on port 5432')
}) |
I did further testing. While the Here's a minimal reproduction: pg-gateway-migrate-stuck-reproduction.zip. The |
Thanks for testing @BracketJohn. We did a bit of a deep dive into prisma and learned that prisma creates a shadow database during CREATE DATABASE "prisma_migrate_shadow_db_<uuid>" Then opens a new connection to it concurrently with the regular DB. There are 2 issues going on:
You could experiment with the queuing strategy, but I have a hunch prisma expects both connection to run concurrently. Another approach is to create a second temporary PGlite instance dedicated to act as the shadow DB, then manually configure prisma to use this as the shadow DB. It would roughly look like:
|
My temporal solution for https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer |
update: Just tried it: The current 0.3.0-beta version already resolves the Hey @gregnr! Thanks for taking the time & doing a deep dive into the reported topics, very helpful! 💯 I think the shadow DB strategy could work and will try that one out to resolve our migration-woes. One thing I do not understand yet: Why does
and the error that is shown documented here:
I'm pretty sure that |
@BracketJohn I am using version 0.3.0-beta.3. But in my case I am not managing only one prisma connection. When I connect to only one of my two services it works correctly. But when the other service connects and starts sending requests I have the error of Implementing the This is my implementation.
|
So, I've done some extensive testing and playing around with different
Here are the prisma settings that worked best for us: // This `env(..)` value will be read from the `.env` file or from the environment.
url = "postgres://postgres@localhost:5432/postgres?pgbouncer=true&connection_limit=1"
// These two are required for development only. NOTE: If we ever use connection poolers such as `pgbouncer` or prisma accelerate, we'll need to make the `directUrl` dynamic and set it in production, see `directUrl` description here: https://www.prisma.io/docs/orm/reference/prisma-schema-reference
shadowDatabaseUrl = "postgres://postgres@localhost/prisma-shadow?pgbouncer=true&connection_limit=1"
directUrl = "postgres://postgres@localhost:5432/postgres?connection_limit=1" The exact combinations of Here is the final script we used to run the database, supporting the shadow database approach: /**
* Script that starts a postgres database using pg-gateway (https://github.com/supabase-community/pg-gateway) and pglite (https://github.com/electric-sql/pglite).
*
* We use this database for local development with prisma ORM. The script also supports creating a `shadow-database`, which is a second, separate database
* that prisma uses for certain commands, such as `pnpm prisma migrate dev`: https://www.prisma.io/docs/orm/prisma-migrate/understanding-prisma-migrate/shadow-database.
*
* To make use of the shadow-database add `/prisma-shadow` to the DSN you provide. This script will then spin up a second, in-memory-only database and connect you to it.
*
* This whole script approach is novel to us (before we used sqlite locally). Here is the PR that brought it all together: https://github.com/sidestream-tech/REDACTED
*/
import net from 'node:net'
import { PGlite } from '@electric-sql/pglite'
import { fromNodeSocket } from 'pg-gateway/node'
const db = new PGlite({ dataDir: 'pglite-data' })
let activeDb = db
const server = net.createServer(async (socket) => {
activeDb = db
console.info(`Client connected: ${socket.remoteAddress}:${socket.remotePort}`)
await fromNodeSocket(socket, {
serverVersion: '16.3',
auth: {
// No password required
method: 'trust',
},
async onStartup({ clientParams }) {
// create a temp in-memory instance if connecting to the prisma shadow DB
if (clientParams?.database === 'prisma-shadow') {
console.info(`Connecting client to shadow database`)
activeDb = new PGlite()
}
// Wait for PGlite to be ready before further processing
await activeDb.waitReady
},
// Hook into each client message
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return
}
// Forward raw message to PGlite and send response to client
return await activeDb.execProtocolRaw(data)
},
})
socket.on('end', () => {
console.info('Client disconnected')
})
})
server.listen(5432, () => {
console.info('Server listening on port 5432')
}) With this setup every command we tested worked flawlessly. You can see that prisma nicely respects the connection limit. The only "off" thing that still occurs is this error on the first
This error seems to be inconsequential and does not lead to any problems we've experienced. With this, we are happy to use Thanks @gregnr for all the help & the nice debugging-tandem, I certainly enjoyed it 🙌 |
Amazing work @BracketJohn. Thanks for sharing your final implementation 🙌 I'm in the middle of implementing more robust connection handling logic (closing each side of the stream at the correct time, exposing close events you can hook into via promise, etc) which hopefully solves the above issue since it sounds related. This is likely the last piece before 0.3.0 is released. Will keep you updated. |
@AndresGnu if I understand your use case correctly, it's worth reiterating that PGlite only operates on a single connection (single-user mode), so sending messages from multiple connections to the same PGlite instance will have undefined behaviour. For example:
At a minimum I would suggest tracking extended queries and queue them between connections so that they're atomic, and also tracking and queuing transactions between connections so that they don't overlap. Though there are no guarantees these 2 things alone will make this work. Future versions of pg-gateway will support |
@gregnr happy to share & thanks again to you for being along on the ride plus working the Getting rid of |
Improve documentation
I followed the pglite guide to get a local development-setup using
pg-gateway
,pglite
andprisma
running. I could not get it to work following the docs. I then tried to use theexample/pglite
of this repository to create a minimal reproduction. The minimal reproduction also did not work (see below).Describe the problem
Minimum reproduction using the
example/pglite
fromm this repository.First, clone the repo and install the required dependencies:
Add the following content into the
schema.prisma
:Start the database:
Attempt to push the schema in a parallel terminal:
Running it a second time still yields an error, albeit a different one:
Running these commands with
DEBUG="*"
set reveals that:select version();
,select version();
- so that is probably left over from the first runDescribe the improvement
Add a
prisma
-subsection that specifies any extra steps to get prisma to work.Additional context
Exact package versions this is running on:
As I'm unsure whether this is related to this package at all, desired behavior or a bug I opened this as a documentation issue - I hope that's alright! Thanks for this great package, it looks super promising and I'd love to use it to create a local postgres-development setup without having to spin up a full docker container every time!
The text was updated successfully, but these errors were encountered: