On Feb 7, 2009, at 6:59 PM, Patrick Antivackis wrote:
Thank you for the preview.
I will just think loud in order to comment your explaination.
As for background how it's working today:
version of a document : a version of a document is identified by an
id and a
revision. The version of the document contains all the fileds/values
as they
were at this specific revision
revision history : the list of all the revisions of the document
beginning
by the most recent
Compaction of a database removes all previous versions of a document
but
keep the revision history, so a revs=true for a document will return
the
revision history of this document but of course I will not be able
to see
what contains this document revision
Replication only replicate the last version of a document. if I
replicate
baseA to an empty baseB, baseB will contains the last version of all
non
deleted documents, and for each documents the full revision history.
So for
each document i'm able to do a revs=true, but I am unable to see the
content
of a previous version.
Spurious conflicts : As a document contains the full list of its
revision
history, the replication process is sure to be able to find if a
document
with the same id on source and destination bases represent the same
document
(by comparing both revision histories)
What you describe is :
1) to have the possibility, through a flag, to specify the number of
revisions that the revision history of each document will keep in
order to
not make the revision history grow and grow. The drawback is the
introduction of spurious conflicts during replication as overlapping
revision for a same document id can be missing.
2) to change the revision format from a generated number to a tuple
containing the sequential number of the document revision and a
generated id
(string)
On your first point, I see more inconvenients than advantages, as
spurious
conflicts can appear. Except if you make "intelligent" trimming, by
keeping
the first revision in the revision history. So in your example, Doc
on DbA -
["1-foo" "2-bar" "3-baz" "4-biz"] would be Doc on DbA - ["1-foo" "3-
baz"
"4-biz"] after trimming.
You got everything right except this. It doesn't solve the problem,
because on another node, I could have a document that looked like ["1-
foo" "2-bif"]. That is a real edit conflict that wouldn't be caught by
what I think you are proposing.
On the second point, it's interesting as you have a sequential
number fo the
revision so a count of the number of revision made (thing that you
have
already today if you count the number of element in the revision
history
list), but to be honest i don't really catch the interest to know
that's
it's the 10000th revision, without being able to access individually
each
version.
This is used during conflict detection to figure out if 2 tree
fragments overlap. We don't actually store a sequential number for
each revision, we store a revision tree of numbers, with the root of
the tree being the offset from 0 where it was trimmed (technically
it's stemmed).
Why not going further, because what is the aim to have revision
history for
version than you cannot access. So why not just keeping in the
revision
history, the first revision (to prevent spurious conflicts) and the
revision
corresponding to the version of the document you can access on the
node.
Like this the sequential revision number brings interest as we know
what
versions are missing on one node and could may be found on another
node.
Sometimes people can deal with spurious conflicts. This gives you the
option. If you don't want spurious conflicts, don't use this feature.
And if you want the same document to be editted over and over, 100s of
thousands of times, this is really the only option. The revision
history will get too big and slow down updates tremendously.
Aside your explainaition, I think it would be interesting, to have a
possibility to also replicate all version of the documents, so you
could
see whatever version on whatever node of your bases. In this case, a
revision history trim based on period would be interesting.
Just some loud thinkings,
Regards,
2009/2/7 Damien Katz <[email protected]>
Part of the larger replication security work (branches/
rep_security) is to
allow rev histories to be trimmed back to an arbitrary length.
Without this,
revision histories must grow and grow, each update to a doc adds a
new
revision to the history. So if a document is edited 1 million
times, there
is a 1 million rev history that must be tracked.
But with it, it allows for unlimited to edits to documents with
only a
fixed size history. The catch is it's possible to have spurious
conflicts if
the trimmed revision history for a later edit is replicated to a
database
without overlapping revs.
The new revs look like this: "4-3693042815". The format is pretty
much
arbitrary, it just needs to be a parseable representation of an
integer and
second string value. The first number is the sequential revseq
(shown is the
4th revision), the second is a randomly generated id (which
eventually
should be deterministically generated based on doc content, making
idempotent updates possible and completely transparent to clients).
However, when representing a rev in Erlang it is a tuple like this
{4,
<<"3693042815">>}, we need to convert back and forth between string
format
for json. Representing it as string in json instead of a complex
structure
has the least impact on couchdb clients.
This will also simplify partial replication support in the future,
as we
can track the rev seq a field or attachment when changed, and during
replication only send those parts that have changed since a previous
revision that available in the target db. The main benefit being
saving
network IO by not sending fields and attachments that haven't
changed.
-Spurious Conflicts-
The issue with spurious conflicts is if you have non-overlapping
revision
histories you don't know if you have a conflict or not. CouchDB
will always
report there is a conflict in the case.
Example id database a with have document with this revision history
(I'm
using string for rev ids where it would normally be number):
Doc on DbA - ["1-foo" "2-bar" "3-baz" "4-biz"]
Doc on DbB - ["1-foo"]
Lets say the revision history on A is trimmed and it now looks like
this:
Doc on DbA - ["2-bar" "3-baz" "4-biz"]
When we replicate DbA with DbB, we get a spurious conflict, because
it
can't tell if "4-biz" is actually a later revision of "1-foo":
Doc on DbB - winner: ["2-bar" "3-baz" "4-biz"] conflict: ["1-foo"]
But if on DbC we still have the full history of that doc:
Doc on DbC - ["1-foo" "2-bar" "3-baz" "4-biz"]
When it replicates back with DbB, the missing part of the revision
history
is sent and the spurious conflict automatically eliminated:
Doc on DbB - ["1-foo" "2-bar" "3-baz" "4-biz"]
-What Breaks-
This change won't break application code, so long as they treat the
_rev
field as an opaque string and aren't converting it to integers or
something.
This change *does* break replication with previous versions of
CouchDB, and
changes the file format. So a dump and import will be required for
existing
database files.
As of yet, I've not actually coded the parts that trim back the old
revs.
That will likely be a "max rev history" setting in the DB, but other
suggestions welcome.
-Damien