[HN Gopher] Edge-compatible Serverless Driver for Postgres
       ___________________________________________________________________
        
       Edge-compatible Serverless Driver for Postgres
        
       Author : carlsverre
       Score  : 53 points
       Date   : 2022-12-08 16:18 UTC (6 hours ago)
        
 (HTM) web link (neon.tech)
 (TXT) w3m dump (neon.tech)
        
       | nikita wrote:
       | I'm Neon CEO. Happy to answer questions. We also have an
       | interesting roadmap for the driver where we hope to keep driving
       | latency and number of round trips down
        
         | krashidov wrote:
         | Hey Nikita, what are the key differences between neon and
         | Supabase?
        
           | nikita wrote:
           | I think we innovate on different parts of the stack. Neon is
           | offering separation of storage and compute and serverless.
           | Theoretically you can run Supabase on top of Neon
        
             | nikita wrote:
             | To elaborate Neon is designed deliberately to plug into the
             | Postgres ecosystem. So all of the ecosystem works out of
             | the box. Supabase seems to be vertically integrated. But
             | Paul should comment on that
        
               | kiwicopple wrote:
               | I believe most of the tools that Supabase use would work
               | with Neon, with the exception of Realtime. As I
               | understand it, Neon doesn't support Logical Decoding
               | yet[0], which is required so that the Realtime engine can
               | receive updates from the database.
               | 
               | [0] https://community.neon.tech/t/plans-for-logical-
               | replication/...
        
               | nikita wrote:
               | You are right. We will make it work soon
        
         | sakras wrote:
         | I'm curious why not implement websockets into Postgres natively
         | instead of relying on a proxy server? Wouldn't that be the way
         | to minimize latency?
        
           | neuronexmachina wrote:
           | Being able to connect to arbitrary Postgres servers (or
           | potentially even the postgres compatibility-layers for AWS's
           | Aurora or Google's AlloyDB) seems like a good reason to go
           | for the proxy approach.
        
             | nikita wrote:
             | Yes. Also it was an easier thing to do and ship quickly. We
             | are going to get very tight on latencies and if that would
             | require building it into Postgres we will do so
        
         | pbiggar wrote:
         | Does Neon scale down storage? I'm paying GCP a lot for storage
         | I don't use, and it's incredibly frustrating - they scaled me
         | up no problem but won't let me scale down.
        
           | nikita wrote:
           | Storage is multi tenant and bottomless. Unused storage spills
           | to S3 and is cheaper. We pass savings to our users
        
             | pbiggar wrote:
             | I think you're telling me the answer but I amn't sure.
             | 
             | Suppose I use 10TB of storage, and then later I delete all
             | but 100GB of storage. Will I be paying for 10TB or 100GB?
        
               | nikita wrote:
               | 100GB
        
         | jtmarmon wrote:
         | Would be really cool to see you guys become a managed service
         | on Fly.io - see what they did with Upstash & Redis:
         | 
         | https://community.fly.io/t/preview-managed-upstash-redis-wit...
         | 
         | They don't have a managed postgres offering yet ("this is not a
         | managed postgres" - https://fly.io/docs/postgres/getting-
         | started/what-you-should...)
        
         | bilalq wrote:
         | I don't see pricing listed anywhere. Can you expand on what
         | that might look like? Also, can Neon scale down to zero like
         | serverless aurora v1, or is there a minimum capacity unit?
        
           | nikita wrote:
           | Pricing is coming very soon. Dotting the is and crossing the
           | ts
        
             | nikita wrote:
             | And yes. Scales to 0
        
       | canadiantim wrote:
       | Would it ever be possible to run neon on a mobile phone, offline?
        
         | nikita wrote:
         | We have some ideas of compiling Postgres to wasm and sync with
         | the mothership in the cloud. But we haven't funded the work.
         | Check out electricsql they are looking at this problem.
        
           | thruflo wrote:
           | Hey, link for the curious: https://electric-sql.com
           | 
           | Hybrid serverless <> local-first is the future. Neon in the
           | client would be so cool.
        
       | dboreham wrote:
       | Hmm. Wouldn't this also be useful for browser-hosted application
       | code that wants to talk to PG directly?
        
         | kelvich wrote:
         | Right, that is one of the use cases. Auth is tricky part in
         | that setup -- in the browser user may change the query in
         | arbitrary way. But there are some workarounds like obtaining
         | JWT from 3rd party service and checking it via Row-Level
         | Security policy in Postgres.
        
       | Sujan wrote:
       | The "How it works" section surprised me - I did not expect to see
       | Websockets there.
       | 
       | Other serverless drivers or database APIs (PlanetScale Serverless
       | Driver, AWS RDS Proxy or Prisma Data Proxy) usually use HTTP to
       | replace the stateful TCP connection. The post explains a bit why
       | this might be beneficial (can use existing tools, and use
       | connection state) - but what about the other benefits and use
       | cases of going with HTTP?
       | 
       | For example in that architecture the connection pool also moves
       | from the application to the component in between and all that
       | complexity can completely be removed from the app. Especially on
       | serverless platforms, where in theory each request gets its own
       | application with its own pool, that is huge. How does that work
       | here? Or does the pool also move to the "WebSocket-to-TCP proxy"
       | and you just did not highlight it in the post? (The graphic shows
       | a "Connection Pool" but I can not really make sense of what that
       | means.)
        
         | mattashii wrote:
         | HTTP does not (easily) allow for using answers of earlier
         | queries in the transaction. E.g.                   BEGIN;
         | INSERT INTO my_table (...) RETURNING (id);         SELECT
         | count(\*) AS my_count FROM my_table;         INSERT INTO
         | historical (new_id, value_derived_from_count, now());
         | COMMIT;
         | 
         | is difficult (or potentially impossible) to do transactionally
         | using single HTTP queries. Sure, you can rewrite your queries
         | to use single-statement queries if you're lucky, but for others
         | that may take a lot of work. Keeping a transaction alive over
         | WebSocket allows you to easily have transaction states that
         | last longer than the lifetime of the first request, which
         | allows for transactions of which state is processed in more
         | than one place.
        
           | rhyselsmore wrote:
           | Just turn it into a single query using CTEs.
        
           | Sujan wrote:
           | That I understand.
           | 
           | But of course for the user of the driver it might be fine if
           | that is 2 or more HTTP requests. I expect that is how
           | PlanetScale does it in their transaction implementation,
           | https://github.com/planetscale/database-js#transactions, and
           | I know that is how Prisma Data Proxy handles it - the
           | transaction is identified with an ID which is returned to the
           | Client and then included in further requests for the same
           | transaction.
           | 
           | It's valid tradeoff to make to prefer a persistent connection
           | to keep the overhead for multiple queries in a transaction as
           | low as possible - which seems what Neon has done here.
        
             | throwdown220 wrote:
             | It's not a trade off. They've not been able to do something
             | that is doable and are trying to tell you it is a trade
             | off.
        
             | mattashii wrote:
             | > the transaction is identified with an ID which is
             | returned to the Client and then included in further
             | requests for the same transaction.
             | 
             | Yes, and there's a catch there that people might not
             | notice: HTTP-based queries utilize this ID to identify
             | which active transaction to use, but this is vulnerable to
             | concurrent HTTP requests on the same transaction ID, thus
             | allowing query injection (early COMMIT, SELECT
             | sleep(1000000), etc. by attackers) due to requests being
             | re-routed every time you send the query.
             | 
             | Keeping a direct connection (albeit proxied) to the
             | PostgreSQL instance prevents this kind of attack, while
             | also allowing for better state keeping control in systems
             | that don't have access to raw TCP sockets, and reducing
             | per-query overheads.
        
               | mattrobenolt wrote:
               | In our case, it's a bit more state than just a
               | transaction ID, but the same concept applies.
               | 
               | And yeah, to mirror the other comment, this isn't
               | particularly something I'd consider a security issue. If
               | an attacker has access to your transaction (session)
               | state, this is a similar risk vector on any web service
               | that utilizes browser cookies or sessions. If someone
               | hijacks your browser cookies, they can do anything they
               | want into your account typically, ignoring 2FA and extra
               | layers.
               | 
               | Same risk here. If someone were to intercept your
               | request, which is strictly over HTTPS, and hijack your
               | session, or you erroneously were logging the session and
               | authentication credentials publicly, then you're exposing
               | yourself to a lot bigger risk than someone adding a
               | sleep() into your session. They could just dump
               | everything from the database, because they are you.
               | 
               | A direct connection only changes this because it's a
               | stateful protocol, and if the connection is severed,
               | everything is lost/rolled back and nobody can intercept
               | that.
               | 
               | But anyways, I'd hardly call a well established security
               | model of HTTP sessions flawed in this regard.
               | 
               | If anything, it's possible for a client to do some dumb
               | things, like, parallel queries within a single
               | transaction, which you cannot do with a direct
               | connection, which ultimately is just... going to be
               | undefined behavior since they fundamentally get
               | serialized into the database, just in an undeterministic
               | order relative to the client.
        
               | [deleted]
        
               | Sujan wrote:
               | I am not sure I am following.
               | 
               | We can have either parallel queries in a transaction
               | where the order does not matter, or we can have them
               | serialized - but then the next query will only be sent
               | onto its journey after the previous query returned its
               | data to the app.
               | 
               | What are the attack vectors here? And where would the
               | attacker sit and try to achieve what exactly?
        
         | kelvich wrote:
         | Another angle here is compatibility. With our current driver
         | one can use ordinary node-postgres package, as we can
         | substitute TCP-related calls with WebSocket calls during the
         | build time. With that it possible to use all the packages that
         | do require node-postgres like Prisma, Zapatos, etc.
        
           | Sujan wrote:
           | Prisma does not actually use node-postgres, but a Rust
           | PostgreSQL driver. Prisma will not be able to use the Neon
           | serverless driver.
        
             | kelvich wrote:
             | https://github.com/prisma/prisma/blob/main/packages/client/
             | p...
        
               | Sujan wrote:
               | That is only used in tests :) The query engine uses this:
               | https://github.com/prisma/quaint/blob/6532d69b5aec007ad06
               | ac6...
               | 
               | (I work at Prisma, could have mentioned that earlier)
        
               | kelvich wrote:
               | Gotcha. I drew my conclusion based on the mentioned
               | package.json. Now wonder why did you decide to go with
               | rust for query engine? Do you compile it into wasm?
        
               | Sujan wrote:
               | It made sense at the time. We do not only support Node,
               | but also have community Clients in Go, Python or Rust.
               | Right now we are moving more and more parts from a Node-
               | API library or binary engine (the two variants we support
               | until now) over to Wasm modules where it is possible for
               | our Node/TS/JS Client. Socket/TCP connections itself are
               | unfortunately not supported yet, so this will only be
               | partial. And maybe there is also a future where we
               | support Node based databases drives. As this blog post we
               | are commenting on shows, sometimes we have to combine the
               | weirdest things together to achieve our goal.
        
         | gmac wrote:
         | Right: at a basic level, using WebSockets lets us change as
         | little as possible from the user perspective. You get an
         | ordinary Postgres session with an ordinary Postgres driver,
         | full control over transactions, and so on.
         | 
         | At this point, your serverless function establishes a new
         | Postgres connection on each call. We do pooling on the server
         | side with pgBouncer, which means we can handle lots of
         | simultaneous connections (which is what this approach
         | generates). It's true that this approach doesn't fully optimise
         | for low latencies. But, as Nikita has mentioned elsewhere in
         | this thread, we have a roadmap for bringing latency down in a
         | number of different ways over time.
        
           | Sujan wrote:
           | Ok, so to make that explicit: If I want to do 5 parallel
           | queries on my serverless function I should still have a
           | connection pool size of 5 in my application, which will be
           | fine as PgBouncer ensures there are plenty connections to
           | open and use from the database server side. Correct?
        
             | gmac wrote:
             | In principle, yes, but as things stand you'll be starting 5
             | separate TLS connections to the server that way. My feeling
             | is that this would be an unusual way to use serverless
             | functions. Is it something you think you'd do?
        
               | Sujan wrote:
               | Yes. You only have to open these connections once on the
               | first execution of that function (cold start), any future
               | request that hit this warm function will have 5 open
               | connections and can instantly execute these queries in
               | parallel. No overhead at all to open the connections.
               | 
               | What would be the alternative? Only execute the queries
               | in sequence, one by one?
        
       | vojtad wrote:
       | Just for the recerence, it is possible to use Cloudflare Tunnel
       | to connect to Postgres from Cloudflare Workers [1]. IIRC they
       | also use WebSockets for communication between the Worker and the
       | Tunnel.
       | 
       | [1] https://developers.cloudflare.com/workers/tutorials/query-
       | po...
        
       ___________________________________________________________________
       (page generated 2022-12-08 23:01 UTC)