Optimizing Postgres's autovacuum for high-churn tables
tembo.io
tembo.io
I would add to this that if you truly have a high-churn table, perhaps a traditional RDBM is not the best choice. Ask me how I learned that lesson the hard way. There are many things that, in hindsight, really should have gone into ELK or Splunk or Redis or something more suited to ephemeral or short-lived data sets that also turn over constantly.
But, sometimes there is no choice, or the churn is high but not quite high enough to rule out the use of an RDBM altogether. For those scenarios, the tips in this article really shine.
At the time it was created, the company was in its infancy, there wasn't much throughput, the service emitting the logs was kind of obscure, and the solution was a quick, dirty stopgap meant to be easily digestible and consumable to the customer, compatible with the rest of their in-house knowledge and the rest of their backend stack. However, as happens too often, the company grew meteorically, the volume got to be insane, many more default-on log entries were added to the standard event loop, and the system was put straight into production without any interest in redesign; after all, it worked fine so far.
As you might guess, it got to the point where we were seeing insanely degraded performance and cascade failures after only a few days' log activity, and increasing time and resources were devoted to the endless care and feeding of this beast. It took a surprisingly long time for the rubber band to snap and for the customer to consider ELK, despite the fact that I advocated for this almost from the very beginning.
I gave a talk titled "UPDATE Considered Harmful" featuring exactly this idea a few months back. The etymology still makes no sense to me...they should have named it REPLACE instead of UPDATE: https://www.youtube.com/watch?v=JxMz-tyicgo
After all these years, I wonder why vacuum is still such a PITA with Postgres. Vacuuming should be at least partially concurrent with CREATE.
If memory serves the GC implementation was freeing/defragging 10 chunks of memory for each one it allocated. In a real time system if you're allocating a bunch of memory, you're already on notice, so having it cost a few thousand instructions instead of a few hundred isn't that big of an inconvenience.
And with SQL the network time is going to dominate.
I'll definitely be looking at further tweaking vacuuming based on this article.
https://postgresqlco.nf/doc/en/param/fsync/#:~:text=Setting%....
How is memory consumption during the tests? If you run out of memory on a tmpfs, you'll hit swap and that'll slow you down considerably.
Do you see other disk access during the test?
If you're using docker/podman or docker-compose and your db size is small, a major speedup on linux is to just mount the entire data dir into memory with --tmpfs /var/lib/postgresql/data (or tmpfs: - /var/lib/postgresql/data in docker-compose)
Additionally, if you constantly reset your db in the tests, consider making a template db at the start and later just doing CREATE DATABASE ... TEMPLATE foo; to copy the pages from that template instead of running migrations that produce WAL log. In fact, consider making a db for every test suite from that template at the start - then you can run each suite in parallel (if your app's only state is the db and a single backend).
A few more application-level optimisations: if you need to clear your database between tests, the best way is to wrap each test in a transaction and roll it back (Postgres supports nested transactions with checkpoints which might help make that transparent to the application under test). The second best way is usually to use DELETE rather than TRUNCATE, the latter is much slower on small tables (but much faster on big ones). The third fastest way is to create a new DB from template, although it plays nice with parallelisation.
Just made me curious.
For situations like these, creating the database in a tmpfs volume and having enough memory on the db host would do wonders.
I asked Karen Jex this question at her Djangocon 2023 talk on obscure postgres switches.
I used this among other things to build some model integration tests which exhibit best practices: https://github.com/hitchdev/hitchstory/tree/master/examples/...
I considered using tmpfs but since I wanted to cache the entire database volume after it had been built according to the fixtures and couldnt figure out how to do that with podman and tmpfs.
I just disable autovacuum completely for test runs with autovacuum=off. Remember that it kicks in according to the percentage of data updated so with a table which starts empty you are guaranteed to see autovacuum run once you insert a few rows.
If you have a lot of updates you can also try change the table fillfactor. This makes HOT updates a bit more efficient.