Show HN: SQLite disk page explorer
github.com
github.com
I have some visualization tools too, but with way less features than the OP's software: https://www.nayuki.io/page/sqlite-database-file-visualizatio...
I always thought an explorer or just an base lib would be fun. Great to see yours, especially with a MIT license. Tbanks for sharing.
Maybe add a color-legend to the front page? I didn't know what the colors represented at first.
It's kind of choking on a larger db (3.6GB, 942719 pages) - maybe you can paginate the pages.
And yeah, large DBs get slow; pages are iterated "server side" which can take a while when there are hundreds of thousands of them. Ideal would probably be some live-scrolling page-range API call, although maybe a crude "show next 10K pages" or something would work too.
As someone who isn't disciplined enough to sit through a course or class, this was a really good way to visualize what's going on under the hood, and how to structure my data more efficiently.
Protip: Use WITHOUT ROWID with monotonic IDs if you don't want your rows sprayed randomly around the file! That one change was the difference between SQLite-on-S3 being unusably slow and being fast enough. WITHOUT ROWID tables let you manage the physical clustering.
https://www.sqlite.org/withoutrowid.html
In particular, they say:
> WITHOUT ROWID tables will work correctly (that is to say, they provide the correct answer) for tables with a single INTEGER PRIMARY KEY. However, ordinary rowid tables will run faster in that case. Hence, it is good design to avoid creating WITHOUT ROWID tables with single-column PRIMARY KEYs of type INTEGER.
This has not proven correct in my testing, but perhaps other applications are different. IMO, you _should_ use WITHOUT ROWID on your tables with single-column PRIMARY KEYs of type INTEGER. With a 100ms seek time on S3 requests it's obvious that the WITHOUT ROWID table with monotonic IDs is benefitting from spatial locality and rowid tables are not. I suspect when giving their advice, they are not considering queries of contiguous ranges of ids (which happens naturally more often than you'd think when JOINs are involved).
Presumably, it is faster on a saner filesystem.
Read-only or read-write? And if writeable, what's concurrency like?
There are some open source codebases that do similar things, but take it all the way with write support: https://github.com/uktrade/sqlite-s3vfs
Suggestion: since having multiple writers will corrupt the database, it's worth investigating if the recent (November 2024) S3 conditional writes features might allow you to prevent that from accidentally happening: https://simonwillison.net/2024/Nov/26/s3-conditional-writes/
I see some challenges, though. Can we implement a reader-writer lock using conditional writes? I think we need one so that readers don't have to take the writer lock to ensure they don't read some pages from before an update happening concurrently and some pages from after. If it's just a regular lock, oops--now we only allow a single reader at a time. I wonder if SQLite's file format makes it safe to swap the pages out like this and let the readers see a skewed list of pages and I'm just worrying too much.
A different superpower we could exploit is S3 object versions. If you require a versioned bucket, then readers can keep accessing the old version even in the face of a concurrent update. You just need a mechanism for readers to know what version to use.
Under concurrency, but also under a crash.
Similarly, the big issue with locks (exclusive or reader/writer) is not so much safety, but liveness.
It seems a bit silly to need to have (e.g.) rollback journals on top of object storage like S3.
Similarly, the problem with versioning is that for both S3/GCS you don't get to ask for the same version of different objects: there's no such thing.
So you'd need some kind of manifest, but then you don't need versioning, you can use object per version.
Ideally, I'd rather use WAL mode with some kind of (1) optimized append for the log, and (2) check pointing that doesn't need to read back the entire database. I can do (1) easily on GCS, but not (2); (2) is probably easier in S3, not sure about (1).
My Go driver supports it, through a combination of a VFS that can take any io.ReaderAt, and something that implements a io.ReaderAt with http Range requests (possibly with some caching).
https://github.com/ncruces/go-sqlite3/blob/main/vfs/readervf...
This is compatible with S3, but also GCS, and really any thing that supports http Range for GET.
I've wanted to implement something writeable with concurrency. Not that it'll be great, but because people tend to do worse anyway (like run SQLite on top of something like GCS FUSE, and we can definitely do better than that), so why not?
I've also worked with GCS concurrency features and locks, so I'd like to try and do better. But it's hard to be compatible with multiple cloud providers.
So thanks for that!
likely a bit outdated but:
https://github.com/shmup/awesome-cosmopolitan?tab=readme-ov-...
I always wanted to do something using Janet in the same way with images and compile-time programming. Fun possibilities.
I know it does work but my experience definitely turned me off to it. A polyglot binary seemed like a bad idea in retrospect. This was back when cosmopolitan was first announced so I assume it's better now.