Architecture Notes: Datasette
architecturenotes.co
architecturenotes.co
I hope to create high quality posts from engineers that work on these systems and the challenges they are trying to solve. Really dig into the problems and the technologies and strategies they used to solve it.
What they have learned? Where they have failed?
I also plan to write up deep technical dives on technologies we all rely on and use everyday.
If there is any feedback to improve or you have something you want to write up with me please reach out.
There are so many things I love about this: the art style, the large font, the summary image, etc. Great work.
So you can support there though. Do appreciate it. Hoping to do more technical dives on technologies and use cases here soon.
(or maybe make intentional with e.g. a big blurry box-shadow)
Also: thanks for the resource! I felt incredibly lost doing system design interviews over the last few months, this seems like a fantastic site.
I am glad it can be of help and plan to do much more. :)
Mahdi interviewed me about the architecture of https://datasette.io/ - topics we covered include:
- Building a modern Python app using ASGI
- Benefits of SQLite
- Designing plugin hooks
- Safely allowing SQL injection
- Using SQLite from asyncio
- The Baked Data architectural pattern
- Bundling a Python web application in Electron
- Packaging a Python for WebAssembly
My breakthrough was the realization that any text data packaged as a SQLite database could be deployed to inexpensive serverless hosting
Not a Python developer myself, I was intrigued by the use of hooks to extend Datasette and ended up looking into pluggy and how Datasette plugins use hooks.
I noticed some Datasette hooks like 'extra_template_vars' override values set by other hooks, while 'extra_body_script' simply append stuff to a list.
How does Datasette avoid plugins clashing with each other? Say one plugin writes a header and another overrides it.
I'm not too worried about extra template vars overrides though - that will only be a problem if two plugins accidentally use the same variable name. Maybe I should encourage plugins to use a namespace based on their name in the documentation though.
I do think that SQLite is a really interesting format for publishing data, and I'd love to see more places publish raw SQLite files. It's much better at preserving things like column type information and relationships between tables than CSV is.
(CKAN is an open source, traditional web app style (flask/jinja/postgresql) open data portal that powers a bunch of open data portals, including data.gov.ie)
(Love Datasette!)
Datasette avoids offset/limit pagination because it performs poorly on huge queries - and I don't want random visitors (and crawlers) hurting performance of a public instance by crawling through offset/limit of thousands of pages.
That's why table pages implement keyset pagination instead - so you can do https://congress-legislators.datasettes.com/legislators/legi... and get back records following the one with A000106, which is a fast query because the ID column has an index on it.
Supporting this with arbitrary queries is harder. One idea I had is to allow the user to specify which column and sort order should be used for keyset pagination - so you could construct a URL like this:
/?sql=select+*+from+legislators+order+by+id&_pagination_column=id
If a pagination column has been specified, Datasette would use the same trick it uses on regular table pages and add next links that way.Would that work for you?
The other, probably easier option is a setting that enables offset/limit pagination of arbitrary SQL queries - turned off by default, but easy to turn on for users who are running Datasette on a private server. If that takes several seconds people can at least opt into it.
https://architecturenotes.co/content/images/size/w1600/2022/...
But AFAICT, it just doesn’t scale whatsoever. That SQLite db is both the dataset index and the dataset content combined, right? So you're limited by how big that SQLite db can realistically be. The docs say "share data of any shape or any size", but AFAICT it can't handle large datasets containing large unstructured data like images and video and multi-billion data point datasets are hard to store in a single machine/file.
Not really a criticism, but more wondering if there are scale optimizations in Datasette I'm not aware of since the docs do say any shape or size.
I think of Datasette as a tool for working with "small data" - where I define small data as data that will fit on a USB stick, or on my phone.
My iPhone has a TB of storage these days, so small data can get you a very long way!
Using it for unstructured image and video would work fine using the pattern where those binary files live somewhere like S3 and the Datasette instance exposes URLs to them. I should find somewhere in the documentation to talk about that.
But yes, I should probably take "of any size" off the homepage, it does give a misleading impression.
I decided to just drop "any size" but keep "any shape".
Images and videos can easily be yeeted in as binary blobs (same as with any other standard DB), and SQLite DBs scale into the hundreds of TB range as a single file. Are you comparing the single file strategy to something like a sharded cluster of DBs, or is your thought that a DB that stores objects as independent files is somehow superior?
I just have one small suggestion:
I think it would make it better if the subscribe button disappears after you scroll down for a whole page instead of having it sticky.
Other than that, great work in terms of the article and the website! looking forward to read more
In the meantime you can create data in an existing spreadsheet and then import it into Datasette using this plugin: https://datasette.io/plugins/datasette-upload-csvs
Unless one simply doesn't care about runtime quality.
Given the number of terrible, buggy sites I've seen built using Java or .NET I personally have trouble believing companies run those in production, but evidently they do!
If I'm going to put something in production, I want it to have:
- Comprehensive tests, protected by CI
- Thorough, up-to-date documentation
- Code that lives in version control, with good commit messages that help answer "why" questions about how it works
- Good development environments
- A robust deployment process
The language influences these in as much as different languages have different cultures and tooling around them, but conceptually they are pretty language agnostic.
I know how to do all of these things well in Python, which is why I tend to continue to spend my time in Python land.
(And I haven't seen any ORM to match Django's, in any language. Java and C# have horrible popular ORMs).