Created
January 21, 2015 20:38
-
-
Save tleyden/4b0f84e3edc41c2d0f78 to your computer and use it in GitHub Desktop.
possible approach - move validation out of transaction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void forceInsert(RevisionInternal rev, List<String> revHistory, URL source) throws CouchbaseLiteException { | |
// TODO: in the iOS version, it is passed an immutable RevisionInternal and then | |
// TODO: creates a mutable copy. We should do the same here. | |
// TODO: see github.com/couchbase/couchbase-lite-java-core/issues/206#issuecomment-44364624 | |
RevisionInternal winningRev = null; | |
boolean inConflict = false; | |
String docId = rev.getDocId(); | |
String revId = rev.getRevId(); | |
if(!isValidDocumentId(docId) || (revId == null)) { | |
throw new CouchbaseLiteException(Status.BAD_REQUEST); | |
} | |
int historyCount = 0; | |
if (revHistory != null) { | |
historyCount = revHistory.size(); | |
} | |
if(historyCount == 0) { | |
revHistory = new ArrayList<String>(); | |
revHistory.add(revId); | |
historyCount = 1; | |
} else if(!revHistory.get(0).equals(rev.getRevId())) { | |
throw new CouchbaseLiteException(Status.BAD_REQUEST); | |
} | |
// First look up all locally-known revisions of this document: | |
long docNumericID = getOrInsertDocNumericID(docId); | |
RevisionList localRevs = getAllRevisionsOfDocumentID(docId, docNumericID, false); | |
if(localRevs == null) { | |
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR); | |
} | |
// Validate against the latest common ancestor: | |
if(validations != null && validations.size() > 0) { | |
RevisionInternal oldRev = null; | |
for (int i = 1; i < historyCount; i++) { | |
oldRev = localRevs.revWithDocIdAndRevId(docId, revHistory.get(i)); | |
if (oldRev != null) { | |
break; | |
} | |
} | |
String parentRevId = (historyCount > 1) ? revHistory.get(1) : null; | |
validateRevision(rev, oldRev, parentRevId); | |
} | |
boolean success = false; | |
beginTransaction(); | |
try { | |
AtomicBoolean outIsDeleted = new AtomicBoolean(false); | |
AtomicBoolean outIsConflict = new AtomicBoolean(false); | |
boolean oldWinnerWasDeletion = false; | |
String oldWinningRevID = winningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict); | |
if (outIsDeleted.get()) { | |
oldWinnerWasDeletion = true; | |
} | |
if (outIsConflict.get()) { | |
inConflict = true; | |
} | |
// Walk through the remote history in chronological order, matching each revision ID to | |
// a local revision. When the list diverges, start creating blank local revisions to fill | |
// in the local history: | |
long sequence = 0; | |
long localParentSequence = 0; | |
String localParentRevID = null; | |
for(int i = revHistory.size() - 1; i >= 0; --i) { | |
revId = revHistory.get(i); | |
RevisionInternal localRev = localRevs.revWithDocIdAndRevId(docId, revId); | |
if(localRev != null) { | |
// This revision is known locally. Remember its sequence as the parent of the next one: | |
sequence = localRev.getSequence(); | |
assert(sequence > 0); | |
localParentSequence = sequence; | |
localParentRevID = revId; | |
} | |
else { | |
// This revision isn't known, so add it: | |
RevisionInternal newRev; | |
byte[] data = null; | |
boolean current = false; | |
if(i == 0) { | |
// Hey, this is the leaf revision we're inserting: | |
newRev = rev; | |
if(!rev.isDeleted()) { | |
data = encodeDocumentJSON(rev); | |
if(data == null) { | |
throw new CouchbaseLiteException(Status.BAD_REQUEST); | |
} | |
} | |
current = true; | |
} | |
else { | |
// It's an intermediate parent, so insert a stub: | |
newRev = new RevisionInternal(docId, revId, false, this); | |
} | |
// Insert it: | |
sequence = insertRevision(newRev, docNumericID, sequence, current, (newRev.getAttachments().size() > 0), data); | |
if(sequence <= 0) { | |
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR); | |
} | |
if(i == 0) { | |
// Write any changed attachments for the new revision. As the parent sequence use | |
// the latest local revision (this is to copy attachments from): | |
Map<String, AttachmentInternal> attachments = getAttachmentsFromRevision(rev); | |
if (attachments != null) { | |
processAttachmentsForRevision(attachments, rev, localParentSequence); | |
stubOutAttachmentsInRevision(attachments, rev); | |
} | |
} | |
} | |
} | |
// Mark the latest local rev as no longer current: | |
if(localParentSequence > 0 && localParentSequence != sequence) { | |
ContentValues args = new ContentValues(); | |
args.put("current", 0); | |
String[] whereArgs = { Long.toString(localParentSequence) }; | |
int numRowsChanged = 0; | |
try { | |
numRowsChanged = database.update("revs", args, "sequence=? AND current!=0", whereArgs); | |
if (numRowsChanged == 0) { | |
inConflict = true; // local parent wasn't a leaf, ergo we just created a branch | |
} | |
} catch (SQLException e) { | |
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR); | |
} | |
} | |
winningRev = winner(docNumericID, oldWinningRevID, oldWinnerWasDeletion, rev); | |
success = true; | |
// Notify and return: | |
notifyChange(rev, winningRev, source, inConflict); | |
} catch(SQLException e) { | |
throw new CouchbaseLiteException(Status.INTERNAL_SERVER_ERROR); | |
} finally { | |
endTransaction(success); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment