I've recently been considering replication models, and looking at
relevant prior art. I'd like to start a discussion about the best
replication model for CouchDB, in the hope of garnering both support
and help in implementing a replication model that provides a stronger
form of weak consistency under replication that CouchDB currently
provides. This can be done without sacrificing any of the pre-
determined goals of CouchDB.
There are two research streams that I've been following. The first is
Bayou, for which this: http://www2.parc.com/csl/projects/bayou/ is a
good entry point. Bayou is somewhat more powerful than CouchDB because
it provides consistency guarantees while reading from groups of
replicas.
The second is PRACTI, which starts here: http://www.usenix.org/event/nsdi06/tech/full_papers/belaramani/belaramani_html
. The interesting thing about PRACTI from my point of view is how it
extends weak-consistency to partial replication.
There's also an interesting set of papers here: http://highscalability.com/links/weblink/83
, although most of them aren't directly applicable.
Firstly though, it's worth considering the characteristics of
CouchDB's current replication system.
The background to this issue is the CAP dilemma, described and
analysed here: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.20.1495
The PRACTI paper summarizes this as "the CAP dilemma states that a
replication system that provides sequential Consistency cannot
simultaneously provide 100% Availability in an environment that can be
Partitioned".
CouchDB is a virtually-always-partitioned system that provides 100%
availability (at any given node). Nodes themselves are not partition
tolerant, and hence can provide arbitrary consistency guarantees,
including sequential Consistency as represented by serializable
transactions. It is intended however that CouchDB provide a cluster
architecture. Although the only extant suggestion for this presumes
partition tolerant clustering (http://horicky.blogspot.com/2008/10/couchdb-cluster.html
), this is but one model of a cluster architecture. I would argue that
this is little more than a load-balancing proxy, and that there are
alternative cluster architectures that provide significant benefits,
although this may be begging the question.
For the purposes of initial discussion, the cluster issue isn't
relevant, although it is an issue when considering isolated write
sequences, which are roughly analgous to Bayou's sessions, and are a
very useful replacement for traditional ACID transactions.
The key issue is that there are forms of consistency that, while less
than 'sequential consistency' i.e. distributed transactions, are still
useful. Specifically, Bayou provides the following:
1. Read Your Writes - read operations reflect previous writes.
2. Monotonic Reads - successive reads reflect a non-decreasing set of
writes.
3. Writes Follow Reads - writes are propagated after reads on which
they depend.
4. Monotonic Writes - writes are propagated after writes that
logically precede them.
Monotonic Writes, sometimes called write-ordering, is the specific
form of weak-consistency that interests me in the context of CouchDB.
Consider two documents, A and B, with write versions indicated by
numeric suffixes e.g. A0, A1, A2 etc. A local application makes a
series of writes:
[ A0, B0, A1 ]
Couch currently replicates this as
[ A0-, B0, A1 ]
where A0- indicates that the document is replication without it's
data. The replicator chooses not to provide the data for A0-, only
noting that the revision exists. If the database is compacted however,
then the replicator no longer has any choice - the data for A0 no
longer exists.
It might seem that this doesn't matter, but because replication isn't
atomic, the replication target can, at any time and for any length of
time (possibly permanently) see an arbitrary prefix of the replication
stream, such as this:
[ B0 ]
As far as I can tell, it won't see A0- until it sees A1, although this
doesn't affect this discussion. The point is that the target doesn't
see writes in the order that they occur in the source, and state-
consistency is only reached when the replication reaches the source
write-point, which, ignoring the read/write ratio, is by no means
guaranteed in an unreliable environment.
To make this more concrete - imagine that A is a blog post and B is a
comment. It's possible for running code to see a comment without a
blog post. This isn't the end of the world in this example, but it
does complicate applications which use this data, and unnecessarily,
as Bayou and PRACTI show. In the face of failure, either temporary
(comms) or permanent node failure, the target will see a view of the
source that possibly cannot be made write-order consistent. Write-
order consistency is a very useful and greatly simplifying feature for
applications.
This situation is exacerbated by per-document revision stemming.
<TENTATIVE>
One the surface, the simplest solution to this is to retain and
replicate each revision of a document, in MVCC commit order. The
result of this is that every intermediate state that the target sees
during replication is consistent with the write ordering in the
source. Incremental replication this maintains write-order
consistency, even in the face of failure.
An obvious optimisation is that this: [ ... A0, A1, A2 ... ] can be
replicated as this [ ... A2 ... ] because the intermediate writes
aren't relevant, although see my caveat.
If you allow for *local* multi-document ACID commits then you can
significantly optimise replication, with the added advantage of being
able to provide a weak-consistency equivalent to transactions. The
idea is that you can group writes into an isolation group e.g.
[ ... A1, B3, C2 .... ]
Concurrent access on the local node cannot see any intermediate state
e.g. the three writes are ACID. Note that the 'C' in ACID doesn't mean
that the write will fail if there are conflicts - you can choose for
that to be the case on a per-group-write basis on the local node, but
when it's replicated you don't have that choice - it will commit
regardless. The key property here is really Isolation, rather than
Consistency.
It's not difficult to replication such isolation groups - you simply
wrap the writes in a start/end group in the replication stream, and
replication uses local ACID with commit-on-conflict semantics. If the
replication stream doesn't see the end group marker because of comms
failure, then the group isn't written.
This allows the replication process itself to be adaptively optimised
even if such isolation groups aren't exposed to the user. Consider a
replication stream:
[ ... A1, B3, C2, A2, B4, A3 ... ]
This can be replicated as:
[ ... { C2, B3-, B4, A1-, A2-, A3 } ... ]
or
[ ... { C2, B4, A3 } ... ]
where { } delimit isolation groups. Once again though, see the caveat.
Finally, the existing compact and proposed revision stemming are
incompatible with write-ordering consistency. Bayou uses a simple
point-in-time truncation of the history e.g. linear in the db, and
when it gets a replication request that requires more history that it
has, it synchronizes the entire database. This is an issue for
availability because the target needs to be locked while the missing
history prefix is synchronised to ensure that the target doesn't see
an inconsistent write-ordering.
</TENTATIVE>
<CAVEAT>
The reason the above is tentative, is that it only considers two
peers. Multiple peers can have write dependencies caused by multiple
replications between arbitrary peers. I haven't thought through that
yet. This paper has some information of this issue in a slightly more
challenging context: http://www2.parc.com/csl/projects/bayou/pubs/sg-pdis-94/SessionGuaranteesPDIS.ps
</CAVEAT>
And that's as far as my thinking has progressed. Write-order
consistency in the face of partial replication introduces some new
requirements.
Antony Blakey
-------------
CTO, Linkuistics Pty Ltd
Ph: 0438 840 787
The fact that an opinion has been widely held is no evidence whatever
that it is not utterly absurd.
-- Bertrand Russell