Dqlite ("distributed SQLite") is a portable C library that provides a shared-nothing DBMS. It uses SQLite as its internal storage manager. It allows applications to be highly available without any dependence on external databases but SQLite. Devices such as IoT or Edge nodes are appropriate use-cases for it. There is currently a Go library `go-dqlite` available to demonstrate binding with the Dqlite's C code and serve as a fully functional client written in Go.
Dqlite supports SQL-like commands as SQLite does. Queries will be sent to other nodes following the Raft's protocol, where individual Dqlite server will execute the query on its SQLite connection.
Dqlite uses Raft protocol to keep all the replicas in sync. Since only the leader node in Raft will be able to add WAL entry, while other followers will replicate the WAL entries from the leader, there will not be conflicting WALs proposed by multiple nodes. In cases when a client tries to perform a write transaction on a non-leader node, the transition will fail. Besides, each Dqlite node runs in a single thread that runs in a loop to execute a query on the underlying SQLite engine. When not enough nodes are available in light of network partition, writes to the database might hang until a consensus is reached or a timeout if triggered so that the write fails.
Dqlite requires WAL with its patched version of SQLite. Logs will be propagated from the leader node (as designated by the Raft protocol) to the follower nodes, where individual Dqlite servers will apply the actions. The Raft protocol ensures that WAL logs and actions at different nodes will be identical, thus maintaining the consistency of data across multiple database instances.
Dqlite stores tuples in files backed up by disk as supported by the underlying SQLite engine. Besides, for any applications or clients that wish to connect with Dqlite engines, the tuples format has to consist of a header and a body: The header of size 64-bit is a collection of 4-bit code which specifies the type of the corresponding value of the tuple. The body consists of values of the tuple that follow immediately after the header.
By default, Dqlite nodes initialize the underlying SQLite connection with the SERIALIZABLE isolation level. Since Dqlite processes usually run on independent machines, there is only one database connection per Dqlite server thus other isolation levels are not relevant without other concurrent database connections.
Upon executing a statement, the request will be dispatched to the leader node's execution loop first, which will invoke the underlying SQLite engine to step over the statement. If the execution of the statement requires actions across replicas, the control will be switched back to the main loop. After the main loop finishes replicating the Raft logs across replicas, the control will be switched back to the execution loop, and thus the underlying SQLite engine will continue stepping over.
Similar to SQLite, Dqlite performs checkpointing automatically when the length of the Write-Ahead-Logging (WAL) file reaches a threshold size. (The default value in the Dqlite setting is 1000 pages). The leader will delay the checkpoint operation if the WAL logs have not reached the threshold or the underlying SQLite database is "locked" by another connection and not available for flushing Upon performing the checkpoint, the leader will issue a checkpoint command and sends that command to the followers. Once the checkpointing command is committed by all the followers, they will perform checkpointing respectively in blocking mode.
Dqlite processes running on different nodes only communicate through network protocols and are not aware of other Dqlite instances on the same machine (if there is any). Each Dqlite process has its view of the database connection and does not share files with other instances.