News for hackers. Stuff that matters.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by simonw on Wednesday August 09, 2023 @10:54AM (#37063653)
Following the PostgreSQL logical replication stream to update a local SQLite database copy is definitely a neat trick, and feels very safe to me (especially since you track the Log Sequence Number in a postgres_pos table).

The bit that surprised me was that this thing supports writes as well!

It does it by acting as a PostgreSQL proxy. You connect to that proxy with a regular PostgreSQL client, then any read queries you issue run against the local SQLite copy and any writes are forwarded on to "real" PostgreSQL.

The downside is that now your SELECT statements all need to be in the subset of SQL that is supported by both SQLite and PostgreSQL. This can be pretty limiting, mainly because PostgreSQL SQL is a much, much richer dialect than SQLite.

Should work fine for basic SELECT queries though.

I'd find this project useful even without the PostgreSQL connection/write support though.

I worked with a very high-scale feature flag system a while ago - thousands of flag checks a second. This scaled using a local memcached cache of checks on each machine, despite the check logic itself consulting a MySQL database.

I had an idea to improve that system by running a local SQLite cache of the full flag logic on every frontend machine instead. That way flag checks could use full SQL logic, but would still run incredibly fast.

The challenge would be keeping that local SQLite database copy synced with the centralized source-of-truth database. A system like SQLedge could make short work of that problem.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by DaiPlusPlus on Wednesday August 09, 2023 @11:36AM (#37064298)
> I worked with a very high-scale feature flag system a while ago - thousands of flag checks a second.

May I ask why the flags are checked that frequently? Couldn't they be cached for at least a minute?

> It does it by acting as a PostgreSQL proxy. [...] and any writes are forwarded on to "real" PostgreSQL.

What happens if there's a multi-statement transaction with a bunch of writes sent-off to the mothership - which then get returned to the client via logical replication, but then there's a ROLLBACK - how would that situation be handled such that both the SQLite edge DBs and the mothership DB are able to rollback okay - would this impact other clients?

Re:SQLedge: Replicate Postgres to SQLite on the Edge by vasco on Wednesday August 09, 2023 @11:42AM (#37064382)
> May I ask why the flags are checked that frequently? Couldn't they be cached for at least a minute?

Not in that project but feature flags don't have to be all or nothing. You can apply flags to specific cohorts of your users for example, so if you have a large user base, even if you cache them per-user, it still translates into many checks a second for large systems.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by mikepurvis on Wednesday August 09, 2023 @01:26PM (#37065935)
I guess the cost of doing it precisely isn't terribly high, but if the majority of the flags were "off" (eg, subsets of users being opted into a beta or something), I wonder if you cut a bunch of the queries by sending down a bloom filter.

So basically you check the filter first, and if that says the feature is enabled, only then do you actually ask the real DB.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by kiitos on Thursday August 10, 2023 @07:11PM (#37082923)
Checking a feature flag for a given cohort/user/demographic/etc. should always be a memory read, not a syscall. (You can't have syscalls in a hot loop!) That means feature flags will always be cached in each application instance from their source of truth. And that means you would never query the source of truth directly from the application, regardless of any property of the flag or its users.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by runeks on Wednesday August 09, 2023 @11:45AM (#37064421)
My limited understanding of logical replication is that writes only happen at COMMIT. Ie. nothing is replicated until it's committed.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by zknill on Wednesday August 09, 2023 @11:45AM (#37064424)
The logical replication protocol sends a series of messages that essentially follow the flow that a database transaction would.

i.e. a stream of messages like: "BEGIN", "[the data]", ["COMMIT" or "ROLLBACK"].

So any application that listens to the Postgres replication protocol can handle the transaction in the same way that Postgres does. Concretely you might choose to open a SQLite transaction on BEGIN, apply the statements, and then COMMIT or ROLLBACK based on the next messages received on the stream replication protocol.

The data sent on the replication protocol includes the state of the row after the write query has completed. This means you don't need to worry about getting out of sync on queries like "UPDATE field = field + 1" because you have access to the exact resulting value as stored by Postgres.

TL;DR - you can follow the same begin/change/commit flow that the original transaction did on the upstream Postgres server, and you have access to the exact underlying data after the write was committed.

It's also true (as other commenters have pointed out) that for not-huge transactions (i.e. not streaming transactions, new feature in Postgres 15) the BEGIN message will only be sent if the transaction was committed. It's pretty unlikely that you will ever process a ROLLBACK message from the protocol (although possible).

Re:SQLedge: Replicate Postgres to SQLite on the Edge by paulddraper on Wednesday August 09, 2023 @01:01PM (#37065577)
Logical replication never includes uncommitted data, unless you have written an custom output plugin.

EDIT: https://stackoverflow.com/questions/52202534/postgresql-does...

Re:SQLedge: Replicate Postgres to SQLite on the Edge by zknill on Wednesday August 09, 2023 @01:29PM (#37065970)
It's true for protocol version 1 that only the committed data is sent on the replication connection.

In protocol version 2 (introduced in postgres 14) large in-progress transactions can appear in the new "Stream" messages sent on the replication connection: StreamStart, StreamStop, StreamAbort, StreamCommit, etc. In the case of large in-progress transactions uncommitted data might end up in the replication connection after a StreamStart message. But you would also receive a StreamCommit or StreamAbort message to tell you what happened to that transaction.

I've not worked out what qualifies as a "large" transaction though. But it is _possible_ to get uncommitted data in the replication connection, although unlikely.

https://www.postgresql.org/docs/15/protocol-logicalrep-messa...

Re:SQLedge: Replicate Postgres to SQLite on the Edge by jmull on Wednesday August 09, 2023 @11:46AM (#37064436)
> May I ask why the flags are checked that frequently? Couldn't they be cached for at least a minute?

Not the previous poster, but it appears in the scenario, the SQLite database is the cache.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by paulddraper on Wednesday August 09, 2023 @12:58PM (#37065528)
> Couldn't they be cached for at least a minute?

Only per feature+per user. (Though 1000s per second does seem high unless your scale is gigantic.)

> What happens if there's a multi-statement transaction with a bunch of writes sent-off to the mothership - which then get returned to the client via logical replication, but then there's a ROLLBACK

Nothing makes it into the replication stream until it is committed.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by rockostrich on Wednesday August 09, 2023 @01:34PM (#37066036)
Feature flag systems are usually based on a set of rules that could be serialized and evaluated locally (this is how pretty much every open source feature flag system and feature flag SaaS works). Usually it's based on some kind of UUID being hashed with a per-flag seed and bucketed after some set of targeting rules are applied to other properties passed in for that user. There are added features where you can stores large cohorts to do specific targeting and usually there's some kind of local cache added to make that look-up faster for recent users.

I'm not sure what the original commenter was doing but it sounds like they had some kind of targeting that was almost entirely based on cohorts or maybe they needed to have stability over time which would require a database. We did something similar recently except we just store a "session ID" with a blob for look-up and the evaluation only happens on the first request for a given session ID.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by simonw on Wednesday August 09, 2023 @01:41PM (#37066138)
They were being cached for at least a minute (maybe even more than that, I can't remember the details) - that's what the local memcached instance was for.

This was problematic though because changing a feature flag and then waiting for a minute plus to see if the change actually worked can be frustrating, especially if it relates to an outage of some sort.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by kiitos on Thursday August 10, 2023 @04:42PM (#37081510)
If the cache is out-of-process, access is going to be subject to faults and latencies that are categorically different than anything that would be in-process.

You'd never want to include a request to an out-of-process service in any kind of hot loop, even if that service is on the same machine.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by ericraio on Wednesday August 09, 2023 @11:30AM (#37064214)
One use case I can see this being valuable for is for a client based application and Postgres being a centralized database. The client would just query SQLite and not need to write Postgres SQL.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by aeyes on Wednesday August 09, 2023 @01:40PM (#37066124)
How many flags are we talking here? I implemted a similar system and we just replace the whole sqlite DB file by downloading it from the centralized storage whenever it changes.

Even with 1M flags it's still only a few 100 kB compressed.

I wouldn't replicate per user flags to the edge to keep size under control.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by hinkley on Wednesday August 09, 2023 @02:51PM (#37066924)
Does it though? If it’s a proxy it can support the SQLite read and the Postgres write syntax. If reads only ever go to SQLite they don’t need to work on Postgres.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by Omnipresent on Wednesday August 09, 2023 @06:39PM (#37069343)
Honest question: why is SQLLite needed for local? Why would you not have PG at edge that replicates data with central PG? That way the SQL dialect problem you mentioned wouldn't exist.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by joshuahaglund on Wednesday August 09, 2023 @07:08PM (#37069615)
SQLite is much smaller and self-contained than postgres. It's written in ANSI-C and by including one file you have access to a database (which is stored in another single file). It's popular in embedded systems like, I imagine, edge devices
Re:SQLedge: Replicate Postgres to SQLite on the Edge by linsomniac on Wednesday August 09, 2023 @08:49PM (#37070431)
You know what's faster than a local connection to Postgres? Having the database engine directly embedded in your application. No context switch.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by hot_gril on Thursday August 10, 2023 @12:48PM (#37078544)
The overhead of IPC isn't significant here, unless there's some special use case I'm not thinking of. SQLite might still be faster for small queries for other reasons.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by zbentley on Thursday August 10, 2023 @04:32PM (#37081379)
There are still context switches with sqlite, even in-memory sqlite (it has to mess with files, which means syscalls). Just not network ops.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by kiitos on Thursday August 10, 2023 @05:40PM (#37082154)
In-memory SQLite doesn't read or write files, and therefore doesn't generate syscalls, by definition.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by zbentley on Thursday August 10, 2023 @08:54PM (#37083777)
Fair enough; I didn't realize that ":memory:" SQLite accesses did zero syscalls overall, I had assumed that they required shared memory writes that entailed syscalls for barriers and the like.

I'm happy you helped me learn that's not the case! That'll make several things I regularly need to do much, much easier.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by kiitos on Thursday August 10, 2023 @09:47PM (#37084136)
Sure! It's a huge and important benefit of that access mode.
Re:SQLedge: Replicate Postgres to SQLite on the Edge by yashap on Wednesday August 09, 2023 @09:19PM (#37070666)
That is a much safer way to go for most use cases. Well actually, most use cases don't need edge compute at all, but for those that do, this setup is indeed common, and fine for most apps:

- Say we do edge compute in San Francisco, Montreal, London and Singapore

- Set up a PG master in one place (like San Francisco), and read replicas in every place (San Francisco, Montreal, London and Singapore)

- Have your app query the read replica when possible, only going to the master for writes

In rare cases, maybe any network latency is not OK, you really need an embedded DB for ultimate read performance - then this is pretty interesting. But a backend server truly needing an embedded DB is certainly a rare case. I would imagine this approach would come with some very major downsides, like having to replicate the entire DB to each app instance, as well as the inherent complexity/sketchiness of this setup, when you generally want your DB layer to be rock solid.

This is probably upvoted so high on HN because it's pretty cool/wild, and HN loves SQLite, vs. it being something many ppl should use.

Re:SQLedge: Replicate Postgres to SQLite on the Edge by btown on Wednesday August 09, 2023 @08:26PM (#37070255)
A simple version of this is to do a very cheap SELECT * [where tenant = ...] of your feature flag table(s) to a dictionary structure in memory on every single edge/application server, and do this atomically every few seconds or minutes.

Statsig [0] and Transifex [1] both use this pattern to great effect, transmitting not only data but logic on permissions and liveness, and you can roll your own versions of all this for your own domain models.

I'm of the opinion that every agile project should start with a system like this; it opens up entirely new avenues of real-time configuration deployment to satisfy in-the-moment business/editorial needs, while providing breathing room to the development team to ensure codebase stability.

(As long as all you need is eventual consistency, of course, and are fine with these structures changing in the midst of a request or long-running operation, and are fine with not being able to read your writes if you ever change these values! If any of that sounds necessary, you'll need some notion of distributed consensus.)

[0] https://docs.statsig.com/server/introduction

[1] https://developers.transifex.com/docs/native

Re:SQLedge: Replicate Postgres to SQLite on the Edge by seedless-sensat on Wednesday August 09, 2023 @09:30PM (#37070764)
It's nice to see another pg proxy using the pgx parser (their src[1]) - I built one using this lib too. However, this implementation is missing a lot of low level features to be considered close to compatible, including: multi-query transactions, auth, TLS, extended query mode, query cancellation.

[1]: https://github.com/zknill/sqledge/blob/main/pkg/pgwire/postg...

Re:SQLedge: Replicate Postgres to SQLite on the Edge by yashap on Wednesday August 09, 2023 @09:33PM (#37070794)
I wonder, how does this handle a single transaction that contains both reads and writes? Maybe it just says "within a transaction, all reads and writes go through the Postgres proxy, SQLite is ignored"?
Re:SQLedge: Replicate Postgres to SQLite on the Edge by NicoJuicy on Thursday August 10, 2023 @09:02PM (#37083865)
Ff are mostly written centrally and cached to memory.