What we offer
While the case study explains why managing data bitemporally is useful, it might leave you wondering: what does TwiceDB provide besides a few extra columns to track time in two dimensions? There’s quite a bit more, as we’ll see.
Referentially transparent reads
Referentially transparent reads are reads whose values never change and are a motivating use case for bitemporal databases. Think of them as permanent reads, or reads whose values never change. To provide permanent reads, the database guarantees that the transaction time of each write is monotonically increasing. Without this guarantee, it would be possible to change past data, which is forbidden. Unlike in a relational database where a user could specify, say, txTime = NOW()
, or worse txTime = $1
where $1 = time.Now()
, TwiceDB does not accept a txTime
parameter from the user at all; the txTime
is assigned by the database at the time of the write. However, TwiceDB clients must know the maximum committed txTime
so that they know the maximum tt
that they may query. Clients must not query beyond that, as that would be akin to reading data that will be committed in the future. The database must respond with an error if the client attempts to read a future tt
.
During times of few client writes, it might appear that the transaction time stops moving since the transaction time moves forward only on new writes. Therefore, TwiceDB uses a server-side heartbeat that performs a small write to keep the tt
advancing.
Single writer, many readers
Because we need a monotonically increasing transaction time, TwiceDB uses a single writer that has the responsibility of assigning the txTime
. Readers replicate data from the writer in tt
order, so reads may return one of two possible values: the permanent result of the query, or an error that the requested tt
is in the future. Clients randomly select a reader by default but may be configured to use a specific reader.
TwiceDB servers may act as a reader, a writer, or both. Within a single logical instance, which we call a shard, there may be many readers–this is how we scale horizontally. However, there may be only one node that acts as either a writer or both a reader and a writer.
Strict serializability
TwiceDB offers strict serializability, which means that we offer both linearizability and serializability.
Linearizability
Linearizability is a consistency guarantee that once a newly-written value is read by one client, it will be read by all clients until a new write. This is in contrast with eventually consistent datastores that allow different replicas to return different values during replication.
Since readers replicate data in tt
order, clients will receive either the permanent answer to their read or an error telling them to wait longer for the data to replicate. TwiceDB clients will never receive an intermediate or partial result.
Serializability
Serializability is an isolation guarantee that transactions appear to have been run in some serial order even if they were run concurrently. This prevents certain race conditions, such as two users who attempt to increment a counter at the same time by reading the old value, then writing the new value. If both users read the old value before the “first” user’s increment is written, then the value will increase by 1 rather than 2 since both users will write the same new value, oldValue + 1
.
TwiceDB tracks all in-progress writes to monitor for conflicting writes. If a write W_l
with a later tt
would write an object that would be written by an in-progress write W_e
with an earlier tt
, TwiceDB will reject W_l
. To do so, the database uses optimistic locking with a version number, where the version number is the tt
of the last committed write to the object. Clients must provide the version number in write requests, and writes with an incorrect version number are rejected. Since W_l
cannot possibly know the tt
assigned to W_e
, it’s easy to reject W_l
.
Robust client libraries
Writing and debugging SQL queries is hard, which is why ORMs (object-relational mappers) are so popular. To help developers work faster with fewer mistakes, TwiceDB client libraries provide an ORM that works with your existing classes. The libraries are easy to learn and ship with examples to get you up-and-running fast.
Next: learn more about the data storage model in concepts in detail.