lipanski
Joined 13 years ago
Joined 13 years ago
* BusyBox httpd [1]
* thttpd [2]
* Lwan Web Server [3]
* gatling [4]
I built and maintain a super tiny Docker image (~158KB) for serving static files [5]. My current implementation is based on Busybox httpd and scratch but I also tried out thttpd in the past, which worked well but came with a high memory footprint in some cases. thttpd was used to serve big projects back in the day but it doesn't seem maintained any more. The other two projects were on my list of things to try out. All these projects should compile to binaries <500KB.
By comparison nano-web builds a 18.5MB image on my machine. In my experience you can't go below 5-6MB with Alpine (scratch is the way to go if you can compile statically) and you can't go below ~500KB with Golang or Rust, at least not for serving http requests (C or ASM is probably the way to go).
Note that I'm not saying that small is always better, it probably depends on your use case and there are reasons for using nginx for a static website as well.
[1] https://www.busybox.net/ [2] https://www.acme.com/software/thttpd/ [3] https://lwan.ws/ [4] https://www.fefe.de/gatling/ [5] https://github.com/lipanski/docker-static-website
Serving a static folder `/static` on port `3000` as user `static-user` and with cache headers set to 60 seconds would go like this:
thttpd -D -h 0.0.0.0 -p 3000 -d /static -u static-user -l - -M 60
Even if you've got Python lying around on every Ubuntu server, I still don't get why you wouldn't use something leaner to serve your static files, as long as it's easy to install/configure. Same goes for most of the runtimes in that list.What people don't always expect is the immense portability that POSIX-compliant Shell offers you. This thing runs on pretty much everything.
It felt as good as building Ikea furniture without checking the manual and it's what user/developer experience should be about.
Why not just use a dedicated uptime service with a free tier? I've been using UptimeRobot [1] in the past - they give you 50 free checks at the same refresh rate of 5 minutes.
These tools don't require explicit `includes`or `preload` calls. They can infer the tables that need to be eager loaded through usage (e.g. if you iterate through users and every iteration requests the user's posts, the first iteration will eager load all posts for all those users). This fits really well with GraphQL because different clients might trigger requests for different associations. Automatic eager loading allows you to optimize for all those different cases without writing a single line of code.
The other solutions are either way too explicit or might be eager loading too much. Things like graphql-batch don't even feel like ActiveRecord any more (and for a good reason - it's a different kind of abstraction, meant to batch all sorts of things, including HTTP requests).
There are more caveats when it comes to eager loading, mainly there are many things that can break eager loading. I recently wrote a blog post [3] about dealing with those issues.
[1] https://github.com/salsify/goldiloader
[2] https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/P...
There's no magic there, it's left up to you whether you expose such functionality and you are in full control of all fields that make up your API. Most of the time your APIs will reflect your database associations `{ users { posts { comments } } }` which should be indexed anyway. Custom queries on top of that, like a search filter, can be indexed/optimized individually. Resources can be paginated quite easily and you can also enforce a maximum depth when requesting associations, so that you don't end up with requests too large to deliver.
The main problem with GraphQL comes from the many different ways you can use it, which makes caching or eager loading difficult.