sqlite> CREATE TABLE x1(a, b, c); -- Create table in database
sqlite> .expert
sqlite> SELECT * FROM x1 WHERE a=? AND b>?; -- Analyze this SELECT
CREATE INDEX x1_idx_000123a7 ON x1(a, b);
0|0|0|SEARCH TABLE x1 USING INDEX x1_idx_000123a7 (a=? AND b>?)
sqlite> CREATE INDEX x1ab ON x1(a, b); -- Create the recommended index
sqlite> .expert
sqlite> SELECT * FROM x1 WHERE a=? AND b>?; -- Re-analyze the same SELECT
(no new indexes)
0|0|0|SEARCH TABLE x1 USING INDEX x1ab (a=? AND b>?)
Also wrt
> My approach so far has been to just do these cleanup operations in small batches so that I don’t need to do database queries that take more than 5 seconds to run. This whole experience has given me more of an appreciation for why someone might want to use a “real” database like Postgres which can have more than one writer at the same time though.
The advice for those " “real” " databases is generally to also do cleanup operations in small batches, they just tend to make it less obvious you're doing something unperformant in the smaller case. You're more right than you thought!
bananamogul 1 days ago [-]
Another side effect of deleting 10 million rows in some databases (e.g., Oracle) is that the database writes out 10 million rows' worth of undo, which can swamp the disk space set aside for archive logs if you can't back it up and clear it off fast enough. Committing more frequently help, but if you have large databases and regularly need to purge, the best way in my experience is to use partitioning. Dropping the oldest (or whatever) partition is nearly instant and painless.
I wasn't clear exactly what the author was doing. "The worker crashes because it couldn’t write to the database and the VM shuts down" - why would the VM shut down? I assume VM here means the Virtual Machine (OS).
serhii777_123 8 hours ago [-]
[flagged]
simonw 1 days ago [-]
I've worked with large MySQL databases that used row-based replication and things like an UPDATE or DELETE that affected millions of rows had to be applied in batches there, because otherwise one SQL query might result in a million updated rows needing to be sent to all of the replicas at once.
cogman10 1 days ago [-]
Yeah, I think anyone that's done significant database work has come to the understanding that large updates need to be done in batches, otherwise you nuke performance.
Once you get to about 1M rows of data, batching is essential.
Groxx 1 days ago [-]
I've built custom batch-processors because percona-toolkit's automatic stuff was far too aggressive :|
Every DB needs it, eventually. Even NoSQL darlings like Cassandra - I've seen it go into a resource-constrained death-spiral on stuff that should be async / non-blocking and safe. If you need to stay up, it's always worth planning on, and making sure your logic works during long-running gradual migrations.
Raw EXPLAIN dumps bytecode, which is usually much more verbose than you want. EXPLAIN QUERY PLAN dumps a summary.
xetera 15 hours ago [-]
In my experience, SQLite explain plans are by far the most useless of any database out there. No concept of costing, no buffer information, no explain analyze. It's almost like they don't want you looking at it.
inigyou 14 hours ago [-]
sqlite competes with fopen. If you don't like it, use a real database.
simonw 10 hours ago [-]
What's your criteria for a "real database"?
9 hours ago [-]
antonvs 5 hours ago [-]
A couple of obvious ones would be running as a standalone service, and supporting multiple concurrent writes. Both of these help support transparent handling of multiple concurrent clients, which seems like table stakes for a "real database".
DANmode 1 days ago [-]
> they just tend to make it less obvious you're doing something unperformant
Is this being positioned as a strength, in your comment?
striking 1 days ago [-]
It just is what it is. Sometimes you want to write the obvious query without the DB getting in your way, and other times you want to know as soon as possible that you're doing something that won't scale under exponential load. At this point in my career I prefer the latter, but the former will always have a special place in my heart.
simonw 1 days ago [-]
> I’ve been backing up to AWS, which is always a pain because it’s annoying to navigate the AWS console to generate credentials.
I got so annoyed with that a few years ago that I ended up building a whole tool just to solve that one problem:
uvx s3-credentials create my-existing-s3-bucket
This spits out read-write credentials that are scoped JUST for that bucket. You can add --read-only or --write-only to have credentials that are further locked down, or even add --prefix foo/bar for credentials that can only read/write keys that start with that prefix within the bucket.
> Maybe one day I’ll move away to some other S3-compatible alternative.
I've used Restic with Cloudflare R2 and it worked great.
bspammer 14 hours ago [-]
A more general solution for dealing with complex AWS services is learning just enough terraform to let LLMs do the rest. It also makes it much easier to tear stuff down later as you won’t need to remember what you created.
this is pretty brilliant, aws cli should sherlock this.
when would you want write only?
simonw 19 hours ago [-]
For logging, in particular for an environment where you don't want a leaked credential to allow the deletion of any previously recorded files.
Natfan 17 hours ago [-]
does write only prevent overwrites?
ufmace 7 hours ago [-]
It does effectively, if you turn on bucket versioning.
I think write-only is worthwhile for database backups to guard against a ransomware compromise.
simonw 17 hours ago [-]
Good point, no it doesn't.
Natfan 14 hours ago [-]
but i suppose if you're prefixing your entries with enough entropy, it'd be quite difficult for adversaries to predict and overwrite files without an `ls` equivalent
stevoski 15 hours ago [-]
As a database person, this was hard to read. I wanted to find out what the problems are and solve them.
A db table with only 10k rows? Even a full table scan should be extremely fast.
And with SQLite - which I assumed runs in-process, but even if not, surely is running on the same physical server? Faster still.
Of course, the magic phrase in my head is “create index”.
I hope Julia posts an update!
Edit: I highly suspect the “slow deletes” problem is a classic “n+1” problem suffered by many ORM users, until they come to understand more about the underlying db interactions.
rollulus 16 hours ago [-]
I realized that in the age of LLMs I appreciate Julia’s writing even more. Authentic exploration as an antidote to overconfident know-it-all generated junk articles.
That doesn't block writers (when the writer uses WAL), and gives me a dump that's compressed well while also being easy to sync. My Home Assistant DB is 1.8GB, my dump is 286MB compressed, and I'd guess 90% of that is consistent from one day to the next.
arjie 23 hours ago [-]
What do you backup from your Home Assistant? The default backups are huge, but I finally settled for just the config and I leave the videos and caches off. I also leave off all the HACS downloaded repos. I'm wondering if I'm missing out by doing what I'm doing.
What's in the DB that makes the HA DB that big? You keep lots of historical time-series?
formerly_proven 1 days ago [-]
> That doesn't block writers (when the writer uses WAL)
Neither does VACUUM INTO or ".backup" (which uses the backup API) or sqlite3_rsync or litestream.
andrewaylett 24 hours ago [-]
I seem to recall having trouble with the read only flag and the backup API, but it's quite likely the problem was mine rather than with SQLite.
Anyway, the sync-friendly output is the really important part for me, because it means I can point borg at the zstd-compressed file and it'll only need to store what's changed.
second_route 18 hours ago [-]
Nice. I switched to .backup for live DBs because .dump locked me out once. The .part + mv trick is clean though.
noxer 1 days ago [-]
As for the DELETE issue the easy solutions are:
-Delete it batches
-Delay between batches
-Preload the rowids before deleteing with SELECT (Select does not block)
Additionally if data was added sequentially primary to the same table the data is likely stored this way in the file and deleting it in this or in reversed order can be faster (depends on storage medium and other factors).
zbentley 1 days ago [-]
Row ID preloading is an extremely effective technique—and not just for SQLite. I’ve also used it to great effect on massive Aurora MySQL or Postgres clusters since I could send the SELECT to a replica, and the whole point of deletions was that index memory pressure from the row filtering was putting tons of CPU and buffer cache pressure on the db.
If you’re in a situation where partition pruning or other strategies for getting useless data out of the hot path don’t make sense, this is a killer strategy.
rtpg 16 hours ago [-]
Diving a bit more into databases than your current comfort level/current job demands remains a great way to level up.
I've worked with many web developers who get mental blockage around DB tooling (granted, I have similar mental blockage when it comes to some operations stuff like K8s), and you can go far in life without really having to ask _that many questions_.
But going in and finding out how your SQL turns into data gotten from disk/written to disk is very helpful in just "knowing" what might be a decent idea. That and understanding your DB's locking system (or lack thereof...).
Figuring that stuff out can help reduce the surprise level when you can't seem to get a "simple COUNT" working quickly in Postgres or the like...
holgerschurig 16 hours ago [-]
> I didn’t care to investigate further
and
> my best guess
and
> and presumably other things?)
and
> maybe there’s a bunch of Python code running inside a transaction
Basically, this article has no substance. The author didn't bother to learn anything, didn't look things up. And is then wildly guessing, sometimes wrong.
This is BTW the reason why (as a Debian user) if I search something Linux related and a Ubuntu forum pops up, I don't even open that anymore. Sure, Ubuntu is similar to Debian, but the amount of wrong guessworks in these forums is hefty. I however usually open the Arch Wiki pages, despite Arch very != Debian. But the articles there are written by knowledgeable people.
simonw 10 hours ago [-]
Julia Evans is an extremely knowledgeable person.
She's also one of the best out there at demystifying technology and helping people understand what solving problems actually looks like.
This article doesn't pretend to be a world expert's take on using SQLite. The clue is right there in the title - "learning a few things about running SQLite" - which sets expectations right from the start.
The wider message is consistent throughout all of her writing. You can do this stuff. Here are simple practices to show how to figure out problems and build your knowledge. You don't have to know everything, and you certainly don't have to pretend to know everything. Sharing what you've figured out so far, in as clear a way as possible, is a virtue.
maccard 11 hours ago [-]
I actually think the article is great - it shows the experience of someone who is a good approximation of a smart user that uses the tech. The posters focus is clearly on running the website, and these are the sorts of things that trip up everyday users that don’t spend their day in these tools.
Off the top of my head, yesterday in work I used 2 programming languages, 2 build systems, a cloud provider, a secrets manager, a very intricate framework for client server communication in both languages, plus my VCS, editor and CI tool. That’s a fairly typical set of tools to use for one feature for me, before you go into the weeds of OS versions, specific runtime versions, databases, reverse proxies, caches, and the domain logic!
If I went deep on every single thread exposed to me, I’d never get anything done, so I have to choose my battles just like OP has done here
rmunn 16 hours ago [-]
The Arch wiki is one of the best Linux-related resources out there. I used to look things up there all the time back when I was running Mint (which is basically Ubuntu under the hood). Ironically, now that I'm actually running Arch I think I'm looking things up on the Arch wiki less often than when I was running Mint.
draw_down 11 hours ago [-]
[dead]
argamasa 12 hours ago [-]
Don’t tell me you you found a load bearing seam. Honest take, the blast radius of that footgun muestra have read impressive before it was posted
masklinn 1 days ago [-]
> and presumably other things?
Various statistical views over the value distributions of the indexes, so that the planner can estimate how useful (selective) the index should be.
sqlite_stat1 just gives an average (number of records in the index, and average number of records per value), and if enabled sqlite_stat4 stores histogram data.
src73 2 hours ago [-]
Great tip about `analyze`!
I just ran it on my self-hosted MediaWiki installation and it took the search from seconds to milliseconds.
jackhalford 19 hours ago [-]
Litestream is super interesting, I managed to get it to run with S3 as a backend. Making apps with sqlite backends (there are a _lot_ lf them) almost stateless, at least no filesystem stare. I feel like s3 state is much more manageable, backups and syncing is done by the provider.
luciana1u 21 hours ago [-]
the best thing about sqlite is that it's one of the few pieces of software where reading the documentation makes you a better engineer instead of just a more confused one
spikk 5 hours ago [-]
Maybe the backup job should periodically restore into a temporary DB and run PRAGMA integrity_check. Theoretically should help
pianopatrick 1 days ago [-]
If you are worried about the cleanup operation having python code running in it, maybe you could use the SQLite CLI to run that operation instead.
ryan42 1 days ago [-]
If you're not using them, adding in silk and/or debug toolbar to your django app will be able to get some good automatic reporting and guidance on performance issues.
arlattimore 1 days ago [-]
I wonder if the ORM deletes were slow due to looping through a list calling delete on each object vs having a bulk delete method which accepts a list of IDs?
dangantiban 18 hours ago [-]
IMHO for a small DB I’d encourage sending out an email on each successful backup to ensure it’s completed successfully as a safety check, and zipping it up and emailing it to a known account even. With inboxes being able to take gigabytes, it’s a no brainer. This can be done daily or weekly.
And yes, never allow the files to be deleted from outside. The transfer is a one way valve. If uploading, it’s a write-only operation, no delete unless the file has meta data for expiry.
maccard 11 hours ago [-]
Nah, write only upload to S3/R2 is the way to go. Why would you add all of the headaches of sending email to your tiny backup.
It’s as much work, and scales 1000x farther than the email approach.
1 days ago [-]
m0ose 1 days ago [-]
What does he mean by "I do usually try to monitor them with a dead man’s switch.", when talking about backups?
kevincox 1 days ago [-]
Dead-man's switch means triggering when something doesn't happen. (The name comes from a switch that an alive operator would need to hold in such a way that if they died they would stop holding.) So in this case she means that her monitoring will fire if there wasn't a successful backup within some configured period of time.
I assume this is opposed to alerting when the backup job fails, which is an issue if the job never runs, or hangs forever, or crashes in a way that doesn't trigger your monitoring.
However I don't see how any of this solves the issue of not testing your backup. Because you can definitely have a backup task succeed regularly but the thing it is backing up is still unusable.
sroussey 1 days ago [-]
Once hired a DBA that reworked backup scripts. He got real annoyed at the idea of testing the backups with real restores and clearly never did on the real databases (only on his smaller samples).
Turns out the backups took 30 hours. The daily backups. That then overwrote each other on the assumption that the backups would not take that long.
Of course we found out the hard way.
EvanAnderson 1 days ago [-]
I don't call it a "dead man's switch", but I absolutely monitor some directories for new newest file. If the backup monitoring script doesn't find a file less than 24-ish hours old in the backup destination directory at any time it should send me an alert.
In this context it means upon a successful backup, update a timestamp somewhere. Some other system monitors the timestamp and if it ever becomes more than for example 1 day ago, it fires an alert.
They're not hard to read until you see the queries that coworkers at large companies tend to generate. If the query is a novella that joins half of humankind's know data sources, the query plan tends to not be much better. Then there's always the dreaded question: "why is it slow?" "Because you're making the database yearn for the sweet release of death."
bambax 16 hours ago [-]
They're harder to completely understand than they are to read; it often happens for example that SQLite won't use existing indexes, for no obvious reason.
inigyou 23 hours ago [-]
They might mean the output from EXPLAIN which is definitely hard to read in sqlite as it shows you the transaction bytecode.
dksmart 19 hours ago [-]
It was a great read Julia. I have also been using SQLite for my Wingman AI bot but had not explore much not for a website or another project. Your notes will be helpful, for sure. Thanks
second_route 18 hours ago [-]
Database is locked got me too. Setting busy_timeout to something nonzero fixed most of it.
contentpulse 15 hours ago [-]
[flagged]
VaporJournalAPP 23 hours ago [-]
[flagged]
badmonster 23 hours ago [-]
[dead]
Kalanos 1 days ago [-]
It's great! However, it's only meant for local systems. Once you need to connect over a network or robustly handle simultaneous requests, you need something like postgres.
gabeio 1 days ago [-]
> However, it's only meant for local systems. Once you need to connect over a network or robustly handle simultaneous requests
That’s not really accurate any longer.
Mostly depends on how you layout your tables & files. If you shard the databases then multiple machines can act as writers for their shard. You can also split read requests from write requests and have read only machines scale up/down as much as you’d like. You can use multiple files in a query (there is a limit there).
So for example you can split the user table based on the first letter of the username and then depending on the rest of the database either a database file per user or per customer (organization). Of course more of everything is manual but it’s not as hard as you’d expect if you build for it.
At this point you're building your own networked database using sqlite just as a backing store. You should really reconsider if it's easier than using something designed for it.
If your entire system is sharded by username anyway for other reasons, then maybe what you've described works for you.
allknowingfrog 1 days ago [-]
Is this something that the authors of SQLite actually claim? I don't think anyone else can decide what something is meant for.
inigyou 24 hours ago [-]
The authors have explained that sqlite is meant to compete with fopen more than it's intended to compete with Postgres.
fragmede 1 days ago [-]
Once you release software to the world, the world can choose use it however it wants. Still, the author's of SQLite document their intention for it to be used locally on their "when to use" page.
> SQLite strives to provide local data storage for individual applications and devices.
> Generally speaking, any site that gets fewer than 100K hits/day should work fine with SQLite.
noxer 13 hours ago [-]
I have a 100GB SQLite DB in use that gets 8+ million rows added dally.
It's on an off-the-shelf nvme SSD in a "server" that I build from 5+ year old parts.
Those are writes not hits so not directly comparable.
maccard 11 hours ago [-]
So many problems in tech would be solved with $250 spent on an NVMe drive.
inigyou 23 hours ago [-]
So about one per second (up to ten, less conservatively). I concur. But if you think your site might ever scale beyond that, do yourself a favor and use Postgres from the get-go.
rafabulsing 19 hours ago [-]
The vast, vast majority of websites never see anything even close to that, so it's a safe bet unless you have some specific reason to expect it to reach that kind of traffic, or you are dealing with workloads that SQLite really does not handle well, e.g. many concurrent writes. And if your workload is mostly reads, then you probably can use a cache layer, which allows SQLite to go further still.
maccard 11 hours ago [-]
Adding a cache layer to keep using the wrong database is an architecture failure - if you use Postgres from the get go you will not need the cache until way way later on anyway. (Side note, I’m team MySql but the same point applies)
dukeyukey 16 hours ago [-]
They also say it seems to work well up to 500k a day, which is quite a bit.
noxer 13 hours ago [-]
I have 8+ million rows added daily to a 100+GB DB. There is a limit somewhere but I haven't found it yet.
simonw 19 hours ago [-]
SQLite can happily handle thousands of reads and writes per second even on modest hardware.
tomjen3 19 hours ago [-]
Use WAL (yes this should be the default, or at least explained much better) and you can have one writer, many readers.
Don't move to the network unless you have to - every single request gets massively slowed down because it has replaced local reads with network connections.
Of course if you are building a startup you must consider scaling.
ktzar 1 days ago [-]
Is it me or this is one of the worst and knowingly less informed articles that has hit HN in a while?
the_gastropod 22 hours ago [-]
Don't confuse Julia's humility / accessible writing style for "less informed". She's a deeply knowledgeable programmer who's been doing this for a long time. She works hard to make tech topics feel less intimidating to newcomers.
wwind123 24 hours ago [-]
Why not try a real database like Postgres? It's not as light-weight, but when operations get complicated, real databases are much easier to work with. I had a website that started with SQLLite, but when it got complicated enough, I spent two days to migrate the whole thing to Postgres. With current LLM coding agents, it's not that hard.
abrookewood 23 hours ago [-]
Honestly, I love PostgreSQL, but now I have another server or service to run. SQLite is just a file and often, that is enough.
9rx 21 hours ago [-]
PGlite offers the "real database" compiled to WASM, which can then be embedded similarly to SQLite. You don't have to choose between PostgreSQL and "just a file".
vatsachak 20 hours ago [-]
Yeah pglite is exquisite. Now I don't have to write separate code for client and server side queries.
For example, I have a compiler that compiles a DSL to a DB query which returns a list. Now that query can either be on data in the browser or can be on data on the server. Now I don't have to write the logic twice!
14 hours ago [-]
Rendered at 23:01:39 GMT+0000 (Coordinated Universal Time) with Vercel.
With SQLite's `.expert` mode you can delay that day a little longer: https://www.sqlite.org/cli.html#index_recommendations_sqlite...
Also wrt> My approach so far has been to just do these cleanup operations in small batches so that I don’t need to do database queries that take more than 5 seconds to run. This whole experience has given me more of an appreciation for why someone might want to use a “real” database like Postgres which can have more than one writer at the same time though.
The advice for those " “real” " databases is generally to also do cleanup operations in small batches, they just tend to make it less obvious you're doing something unperformant in the smaller case. You're more right than you thought!
I wasn't clear exactly what the author was doing. "The worker crashes because it couldn’t write to the database and the VM shuts down" - why would the VM shut down? I assume VM here means the Virtual Machine (OS).
Once you get to about 1M rows of data, batching is essential.
Every DB needs it, eventually. Even NoSQL darlings like Cassandra - I've seen it go into a resource-constrained death-spiral on stuff that should be async / non-blocking and safe. If you need to stay up, it's always worth planning on, and making sure your logic works during long-running gradual migrations.
Raw EXPLAIN dumps bytecode, which is usually much more verbose than you want. EXPLAIN QUERY PLAN dumps a summary.
Is this being positioned as a strength, in your comment?
I got so annoyed with that a few years ago that I ended up building a whole tool just to solve that one problem:
This spits out read-write credentials that are scoped JUST for that bucket. You can add --read-only or --write-only to have credentials that are further locked down, or even add --prefix foo/bar for credentials that can only read/write keys that start with that prefix within the bucket.> Maybe one day I’ll move away to some other S3-compatible alternative.
I've used Restic with Cloudflare R2 and it worked great.
when would you want write only?
I think write-only is worthwhile for database backups to guard against a ransomware compromise.
A db table with only 10k rows? Even a full table scan should be extremely fast.
And with SQLite - which I assumed runs in-process, but even if not, surely is running on the same physical server? Faster still.
Of course, the magic phrase in my head is “create index”.
I hope Julia posts an update!
Edit: I highly suspect the “slow deletes” problem is a classic “n+1” problem suffered by many ORM users, until they come to understand more about the underlying db interactions.
What's in the DB that makes the HA DB that big? You keep lots of historical time-series?
Neither does VACUUM INTO or ".backup" (which uses the backup API) or sqlite3_rsync or litestream.
Anyway, the sync-friendly output is the really important part for me, because it means I can point borg at the zstd-compressed file and it'll only need to store what's changed.
-Delete it batches
-Delay between batches
-Preload the rowids before deleteing with SELECT (Select does not block)
Additionally if data was added sequentially primary to the same table the data is likely stored this way in the file and deleting it in this or in reversed order can be faster (depends on storage medium and other factors).
If you’re in a situation where partition pruning or other strategies for getting useless data out of the hot path don’t make sense, this is a killer strategy.
I've worked with many web developers who get mental blockage around DB tooling (granted, I have similar mental blockage when it comes to some operations stuff like K8s), and you can go far in life without really having to ask _that many questions_.
But going in and finding out how your SQL turns into data gotten from disk/written to disk is very helpful in just "knowing" what might be a decent idea. That and understanding your DB's locking system (or lack thereof...).
Figuring that stuff out can help reduce the surprise level when you can't seem to get a "simple COUNT" working quickly in Postgres or the like...
and
> my best guess
and
> and presumably other things?)
and
> maybe there’s a bunch of Python code running inside a transaction
Basically, this article has no substance. The author didn't bother to learn anything, didn't look things up. And is then wildly guessing, sometimes wrong.
This is BTW the reason why (as a Debian user) if I search something Linux related and a Ubuntu forum pops up, I don't even open that anymore. Sure, Ubuntu is similar to Debian, but the amount of wrong guessworks in these forums is hefty. I however usually open the Arch Wiki pages, despite Arch very != Debian. But the articles there are written by knowledgeable people.
She's also one of the best out there at demystifying technology and helping people understand what solving problems actually looks like.
This article doesn't pretend to be a world expert's take on using SQLite. The clue is right there in the title - "learning a few things about running SQLite" - which sets expectations right from the start.
The wider message is consistent throughout all of her writing. You can do this stuff. Here are simple practices to show how to figure out problems and build your knowledge. You don't have to know everything, and you certainly don't have to pretend to know everything. Sharing what you've figured out so far, in as clear a way as possible, is a virtue.
Off the top of my head, yesterday in work I used 2 programming languages, 2 build systems, a cloud provider, a secrets manager, a very intricate framework for client server communication in both languages, plus my VCS, editor and CI tool. That’s a fairly typical set of tools to use for one feature for me, before you go into the weeds of OS versions, specific runtime versions, databases, reverse proxies, caches, and the domain logic!
If I went deep on every single thread exposed to me, I’d never get anything done, so I have to choose my battles just like OP has done here
Various statistical views over the value distributions of the indexes, so that the planner can estimate how useful (selective) the index should be.
sqlite_stat1 just gives an average (number of records in the index, and average number of records per value), and if enabled sqlite_stat4 stores histogram data.
I just ran it on my self-hosted MediaWiki installation and it took the search from seconds to milliseconds.
And yes, never allow the files to be deleted from outside. The transfer is a one way valve. If uploading, it’s a write-only operation, no delete unless the file has meta data for expiry.
It’s as much work, and scales 1000x farther than the email approach.
I assume this is opposed to alerting when the backup job fails, which is an issue if the job never runs, or hangs forever, or crashes in a way that doesn't trigger your monitoring.
However I don't see how any of this solves the issue of not testing your backup. Because you can definitely have a backup task succeed regularly but the thing it is backing up is still unusable.
Turns out the backups took 30 hours. The daily backups. That then overwrote each other on the assumption that the backups would not take that long.
Of course we found out the hard way.
In this context it means upon a successful backup, update a timestamp somewhere. Some other system monitors the timestamp and if it ever becomes more than for example 1 day ago, it fires an alert.
Query plans aren't that hard to read! [0]
0 - https://xkcd.com/2501/
That’s not really accurate any longer.
Mostly depends on how you layout your tables & files. If you shard the databases then multiple machines can act as writers for their shard. You can also split read requests from write requests and have read only machines scale up/down as much as you’d like. You can use multiple files in a query (there is a limit there).
So for example you can split the user table based on the first letter of the username and then depending on the rest of the database either a database file per user or per customer (organization). Of course more of everything is manual but it’s not as hard as you’d expect if you build for it.
https://rivet.dev/blog/2025-02-16-sqlite-on-the-server-is-mi...
If you need sqlite over the network you can look at https://turso.tech/ it’s a almost drop in replacement for sqlite (https://github.com/tursodatabase/turso/blob/main/COMPAT.md)
If your entire system is sharded by username anyway for other reasons, then maybe what you've described works for you.
> SQLite strives to provide local data storage for individual applications and devices.
https://www.sqlite.org/whentouse.html
> Generally speaking, any site that gets fewer than 100K hits/day should work fine with SQLite.
Those are writes not hits so not directly comparable.
Don't move to the network unless you have to - every single request gets massively slowed down because it has replaced local reads with network connections.
Of course if you are building a startup you must consider scaling.
For example, I have a compiler that compiles a DSL to a DB query which returns a list. Now that query can either be on data in the browser or can be on data on the server. Now I don't have to write the logic twice!