sql

valerauko 2025-07-15T15:55:17.630999Z

@jarohen I decided not to derail that thread any further and start fresh... What would this "vulnerable" mean in practice?

jarohen 2025-07-15T15:56:02.921879Z

apologies, yes, that was quite a derail on my part 😅

jarohen 2025-07-15T15:56:27.883659Z

vulnerable in that we saw it exhibit that behaviour

jarohen 2025-07-15T15:58:03.438609Z

so: • tx 1 starts, increments the auto-inc serial counter • tx 2 starts, increments it again. tx2 sees the uncommitted version of the counter • tx 2 finishes writing its operations and commits • our indexer looked at the table, saw tx-id 2, indexes that, sets 'latest completed tx' • tx 1 commits • our indexer never sees tx 1, because we were then querying for tx > 2

valerauko 2025-07-15T15:58:58.276859Z

ok so it still stays uniquely incremented except it's not in the order you expected it to be right?

jarohen 2025-07-15T16:03:08.786049Z

not in the order the transactions commit, yeah

jarohen 2025-07-15T16:03:19.594379Z

and that's even if you set the txs to be 'serializable'

😢 1
jarohen 2025-07-15T16:03:34.921349Z

(it's documented as such, just that it caught us out)

adi 2025-07-16T10:45:11.638569Z

apologies, yes, that was quite a derail on my partnononooo... it's absolutely on the rails getting "time" right is everything in fact, I do not want to try this in PG (or any other client-server Current DB) because their transaction model is complex and nondeterministic (because of whatever concurrency control method they have) SQLite in WAL mode is dead-simple, which guarantees me monotonic auto-inc serial primary key in the lynchpin "world facts" table. • serial writes • in-process with the application • nonblocking concurrent reads

adi 2025-07-16T10:49:32.183669Z

basically, by having all the parts separate (write queue, transactor, and data itself) /but unified in process/, i can solve the problem by avoiding the problem for as long as one machine's compute allows for a bog-standard SaaS app, this might work surprisingly well with a tenant-per-db model ... any especially busy tenant can get its own box

adi 2025-07-16T10:54:01.000809Z

writer -> WAL -> DB -> .sqlite file
                 /|\
                readers
The SQLite engine gives readers what they need using a shared-memory representation over the ephemeral WAL file and the durable .sqlite file.

adi 2025-07-16T10:59:35.693859Z

nb. I do this with awareness of how SQLite's rowid increment behaves https://sqlite.org/autoinc.html ... I can even avoid saying AUTOINCREMENT because the world-facts table is append-only