> 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.
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."
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!
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.
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.
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.
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.
-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).
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?
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.
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
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.
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
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.
> 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.
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.
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...
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.
31 comments
[ 1.1 ms ] story [ 11.5 ms ] threadI 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.
Query plans aren't that hard to read! [0]
0 - https://xkcd.com/2501/
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!
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.
-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).
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.
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.
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...
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.