SQLite has gotten so good, that companies like 37signals are now using it in real apps in production.
I want to do the same thing, but with C++ and the Drogon framework. In theory, it’s simple. But, there’s a nasty interaction between how SQLite is configured, and Drogon’s connection pools that makes it annoyingly hard.
Here’s what’s going on. And, how to work around it.
Using SQLite in Produciton
Two things are needed to set up SQLite for production web app use:
- Use the right configuration, because SQLite’s defaults are optimized for maximum backward compatibility, not performance
- Set up a live database backup system such as Litestream, to protect your data from hardware failure
Sounds easy, right? Here’s where it gets complicated…
The Problem: SQLite’s Configuration Method & Drogon’s DB Pools
Other databases store their configuration in config files. SQLite, doesn’t. Instead, you execute a series of pragma queries, such as:
PRAGMA journal_mode=WAL;
PRAGMA foreign_keys=ON;
PRAGMA busy_timeout=5000;
… a bunch of others …
This still looks easy. But, only WAL mode gets saved to the database. All the other settings need to be reapplied every time you open the database.
Here’s where we clash with drogon’s database connection pool…
Drogon uses a database connection pool, so that you can run multiple queries simultaneously. When you submit a database query, it sends it via one of the connections in the pool. You can’t control which one.
So, if you simply ran the pragma queries one by one, you don’t know which connections they’ll be sent on. And you need them to be run on EVERY SINGLE DATABASE CONNECTION.
Solution(s)
There are four possible solutions:
- Create a connection pool of one. One connection, means that all the initialization queries will be executed on one connection. Problem solved. Alas, now all database queries get serialized, and you no longer get the benefit of multiple read queries. It doesn’t affect database writes, because SQLite only allows one write at a time anyway. Still, serializing database reads sucks
- Modify drogon itself to execute the pragma queries when each sqlite database connection is opened. Very effective, but I try not to modify third-party dependencies because it tends to make updating them hard
- Compile my own SQLite with the defaults baked in
- Do it the hard way: Find a way to force Drogon to send the queries on all connections in a pool
I really wanted to choose option 3, and build my own SQLite with the preferred defaults. But, there was one per-connection setting that I need to be able to toggle at runtime
So, I still needed option 4. Here’s how it works:
Lets say that we have 5 database connections in the pool
- I start 5 transactions to the pool. Each transaction locks up a connection in the pool. Now there are no free connections left
- Next, I submit one of the pragma queries to the pool directly, 5 times. There are no free connections, so they will be queued
- Now, cancel all 5 transactions. This frees up all connections in the pool. Each connection takes one query off the queue. Voila! The pragma query is executed on all connections
- Wait for the queries to be done. And repeat for all other pragma queries
- This should almost guarantee that the pragma queries are run on all connections. I say almost, because it depends how Drogon handles the pool internally. It may also break in future, if Drogon changes things later. For example, if transactions were to no longer reserve a connection, then we’d be screwed…
At this stage I’m sure someone will comment, hey Hans you idiot, why don’t you just run the queries inside the transaction?
I tried that, and SQLite didn’t like it. Some of the queries can be run inside a transaction, but not all of them. So, the procedure above is still needed.
Final Comments
What do you think of this solution? It works well, although I do have feel a little uneasy that future changes to Drogon might break this. I’ve submitted an enhancement request to the drogon project, which may one day turn into a complete solution. More thought is needed about the exact shape of that solution, because this problem is specific to the sqlite database backend. It’s irrelevant to all the others.
I recently discovered your site. Rest assured you are not the only one who is using Drogon for a SaaS site. As for Drogon and connection pools, I’ve found that in only the most basic of instances do they meet my needs.
I’ve learned the hard way that it’s easier to roll your own. I have a separate connection pool that I initialize and keep in a singleton that is available to the data layer in my applications. I found an implementation that works well for MySQL, but I would imagine that it could be adapted for use with SQLite. Each connection uses the parameters that you pass in, so all 5 connections should have the same attributes as you specify.
Assuming you handle interrupts and shutdowns of Drogon, adding a “shutdown” process to close all of the connections should be pretty trivial. Good luck!
@Michael
Thanks. Interesting to hear that you completely replaced the connection pool with your own implementation. SQLite initialization is the only situation where they’re not working for me right now. Maybe I’ll hit more issues as I use it more…
I’ve found that Drogon is great for use as a C++ web server, particularly for API calls that return JSON or XML. The same goes for incorporating their CSP files into HTML for use by humans in browsers. I have not had the same success with using their database tools. While they do work as described, they don’t fit my use case well. So I’ve worked around them. It’s still a good, stable platform. If you choose not to implement your own pool (it’s debatable whether it’s the best choice for your situation) you’d be left wrapping database queries around an interface that required all your pragma queries to run first before executing your SQL. It’s still doable, just not within the confines of their setup. Please be sure to comment or blog about the approach you decide on.