Gourav Goyat

Django with Sqlite in production

05 Jul 2024


Since its introduction, Sqlite has not been considered as a viable alternative to other RDBMS like MySQL and PotgreSQL. Main reason behind this is its bad performance under concurrency.

But with the introduction of WAL mode and various other improvements in the recent times, things are changing for Sqlite. For some use cases, it is now considered on par or even better than the alternatives because of its simple setup, low maintenance cost and low latency.

How to configure

Django comes with Sqlite as the default database. But there used to be a warning in the official docs against using it in production. It was meant to be only for development. That warning is not there anymore. It means Django now considers it good enough.

But Sqlite is not tuned for performance by default. It requires some configurations for optimal performance. And Django also, by default, does not do it for us. We need to do those configurations ourselves.

Sqlite provides something called PRAGMA statements which can be used to finetune the database according to our needs. We need to enable following 4 PRAGMAS for a better performance:

PRAGMA journal_mode = WAL

This PRAGMA significantly increases its ability to handle high concurrency, hence removing one of its major drawback.

PRAGMA synchronous = NORMAL

With journal_mode = WAL it makes more sense to use the less expensive synchronous = NORMAL instead of the default synchronous = FULL.

PRAGMA foreign_keys = ON

To enforce foreign key constraints, we need this PRAGMA as Sqlite does not enforce those constraints by default.

PRAGMA busy_timeout = 20000

By default Sqlite does not wait before throwing the database lock errors. This PRAGMA makes it wait for the given milliseconds duration before throwing the error.

It is not possible to set these PRAGMAS in settings.py file where the database configuration resides. One good place to do it is the apps.py file of any app.

This is how the apps.py file should look like.

from Django.apps import AppConfig
from Django.db.backends.signals import connection_created


class Mainapp(AppConfig):
    default_auto_field = "Django.db.models.BigAutoField"
    name = "mainapp"

    def ready(self):
        def enable_Sqlite_pragmas(sender, connection, **kwargs):
            """Enable the necessary Sqlite pragmas."""
            cursor = connection.cursor()
            cursor.execute("PRAGMA journal_mode = WAL;")
            cursor.execute("PRAGMA synchronous = NORMAL;")
            cursor.execute("PRAGMA foreign_keys = ON;")
            cursor.execute("PRAGMA busy_timeout = 20000;")

        connection_created.connect(enable_Sqlite_pragmas)

This ensures that these PRAGMAs are set whenever a database connection is created.

This is all we need to finetune Sqlite for production.

References

The following videos do a good job of explaining why Sqlite should be considered for production

DjangoCon Europe 2023 | Use Sqlite in production

GopherCon 2021: Ben Johnson - Building Production Applications Using Go & Sqlite

David Crawshaw Sqlite and Go

You can read through the following resources for a better understading of PRAGMAs

Sqlite Pragma Cheatsheet for Performance and Consistency

Sqlite official documentation