Author: mreutegg
Date: Mon Feb 1 21:15:08 2016
New Revision: 1728008
URL: http://svn.apache.org/viewvc?rev=1728008&view=rev
Log:
OAK-3724: Use the bulk createOrUpdate in Commit
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java?rev=1728008&r1=1728007&r2=1728008&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
Mon Feb 1 21:15:08 2016
@@ -30,6 +30,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.jackrabbit.oak.api.PropertyState;
@@ -279,7 +280,6 @@ public class Commit {
// branch commits always use root node as commit root
commitRootPath = "/";
}
- ArrayList<UpdateOp> newNodes = new ArrayList<UpdateOp>();
ArrayList<UpdateOp> changedNodes = new ArrayList<UpdateOp>();
// operations are added to this list before they are executed,
// so that all operations can be rolled back if there is a conflict
@@ -312,121 +312,77 @@ public class Commit {
int commitRootDepth = PathUtils.getDepth(commitRootPath);
// check if there are real changes on the commit root
boolean commitRootHasChanges = operations.containsKey(commitRootPath);
+ for (UpdateOp op : operations.values()) {
+ NodeDocument.setCommitRoot(op, revision, commitRootDepth);
+ changedNodes.add(op);
+ }
// create a "root of the commit" if there is none
UpdateOp commitRoot = getUpdateOperationForNode(commitRootPath);
- for (String p : operations.keySet()) {
- UpdateOp op = operations.get(p);
- if (op.isNew()) {
- NodeDocument.setDeleted(op, revision, false);
- }
- if (op == commitRoot) {
- if (!op.isNew() && commitRootHasChanges) {
- // commit root already exists and this is an update
- changedNodes.add(op);
- }
- } else {
- NodeDocument.setCommitRoot(op, revision, commitRootDepth);
- if (op.isNew()) {
- newNodes.add(op);
- } else {
- changedNodes.add(op);
- }
- }
- }
- if (changedNodes.size() == 0 && commitRoot.isNew()) {
- // no updates and root of commit is also new. that is,
- // it is the root of a subtree added in a commit.
- // so we try to add the root like all other nodes
- NodeDocument.setRevision(commitRoot, revision, commitValue);
- newNodes.add(commitRoot);
- }
+
boolean success = false;
try {
- if (newNodes.size() > 0) {
- // set commit root on new nodes
- if (!store.create(NODES, newNodes)) {
- // some of the documents already exist:
- // try to apply all changes one by one
- for (UpdateOp op : newNodes) {
- if (op == commitRoot) {
- // don't write the commit root just yet
- // (because there might be a conflict)
- NodeDocument.unsetRevision(commitRoot, revision);
- }
- changedNodes.add(op);
- }
- newNodes.clear();
- }
- }
- for (UpdateOp op : changedNodes) {
- // set commit root on changed nodes. this may even apply
- // to the commit root. the _commitRoot entry is removed
- // again when the _revisions entry is set at the end
- NodeDocument.setCommitRoot(op, revision, commitRootDepth);
- opLog.add(op);
- createOrUpdateNode(store, op);
- }
- // finally write the commit root, unless it was already written
- // with added nodes (the commit root might be written twice,
- // first to check if there was a conflict, and only then to commit
- // the revision, with the revision property set)
- if (changedNodes.size() > 0 || !commitRoot.isNew()) {
- // set revision to committed
- NodeDocument.setRevision(commitRoot, revision, commitValue);
- if (commitRootHasChanges) {
- // remove previously added commit root
- NodeDocument.removeCommitRoot(commitRoot, revision);
- }
- opLog.add(commitRoot);
- if (baseBranchRevision == null) {
- // create a clone of the commitRoot in order
- // to set isNew to false. If we get here the
- // commitRoot document already exists and
- // only needs an update
- UpdateOp commit = commitRoot.copy();
- commit.setNew(false);
- // only set revision on commit root when there is
- // no collision for this commit revision
- commit.containsMapEntry(COLLISIONS, revision, false);
- NodeDocument before = nodeStore.updateCommitRoot(commit,
revision);
- if (before == null) {
- String msg = "Conflicting concurrent change. " +
- "Update operation failed: " + commitRoot;
- NodeDocument commitRootDoc = store.find(NODES,
commitRoot.getId());
- DocumentStoreException dse;
- if (commitRootDoc == null) {
- dse = new DocumentStoreException(msg);
- } else {
- dse = new ConflictException(msg,
- commitRootDoc.getConflictsFor(
- Collections.singleton(revision)));
- }
- throw dse;
+ opLog.addAll(changedNodes);
+ List<NodeDocument> oldDocs = store.createOrUpdate(NODES,
changedNodes);
+ checkConflicts(oldDocs, changedNodes);
+ checkSplitCandidate(oldDocs);
+
+ // finally write the commit root (the commit root might be written
+ // twice, first to check if there was a conflict, and only then to
+ // commit the revision, with the revision property set)
+ NodeDocument.setRevision(commitRoot, revision, commitValue);
+ if (commitRootHasChanges) {
+ // remove previously added commit root
+ NodeDocument.removeCommitRoot(commitRoot, revision);
+ }
+ opLog.add(commitRoot);
+ if (baseBranchRevision == null) {
+ // create a clone of the commitRoot in order
+ // to set isNew to false. If we get here the
+ // commitRoot document already exists and
+ // only needs an update
+ UpdateOp commit = commitRoot.copy();
+ commit.setNew(false);
+ // only set revision on commit root when there is
+ // no collision for this commit revision
+ commit.containsMapEntry(COLLISIONS, revision, false);
+ NodeDocument before = nodeStore.updateCommitRoot(commit,
revision);
+ if (before == null) {
+ String msg = "Conflicting concurrent change. " +
+ "Update operation failed: " + commitRoot;
+ NodeDocument commitRootDoc = store.find(NODES,
commitRoot.getId());
+ DocumentStoreException dse;
+ if (commitRootDoc == null) {
+ dse = new DocumentStoreException(msg);
} else {
- success = true;
- // if we get here the commit was successful and
- // the commit revision is set on the commitRoot
- // document for this commit.
- // now check for conflicts/collisions by other commits.
- // use original commitRoot operation with
- // correct isNew flag.
- checkConflicts(commitRoot, before);
- checkSplitCandidate(before);
+ dse = new ConflictException(msg,
+ commitRootDoc.getConflictsFor(
+ Collections.singleton(revision)));
}
+ throw dse;
} else {
- // this is a branch commit, do not fail on collisions now
- // trying to merge the branch will fail later
- createOrUpdateNode(store, commitRoot);
+ success = true;
+ // if we get here the commit was successful and
+ // the commit revision is set on the commitRoot
+ // document for this commit.
+ // now check for conflicts/collisions by other commits.
+ // use original commitRoot operation with
+ // correct isNew flag.
+ checkConflicts(commitRoot, before);
+ checkSplitCandidate(before);
}
- operations.put(commitRootPath, commitRoot);
+ } else {
+ // this is a branch commit, do not fail on collisions now
+ // trying to merge the branch will fail later
+ createOrUpdateNode(store, commitRoot);
}
+ operations.put(commitRootPath, commitRoot);
} catch (DocumentStoreException e) {
// OAK-3084 do not roll back if already committed
if (success) {
LOG.error("Exception occurred after commit. Rollback will be
suppressed.", e);
} else {
try {
- rollback(newNodes, opLog, commitRoot);
+ rollback(opLog, commitRoot);
} catch (Exception ex) {
// catch any exception caused by the rollback, log it
// and throw the original exception
@@ -456,8 +412,7 @@ public class Commit {
}
}
- private void rollback(List<UpdateOp> newDocuments,
- List<UpdateOp> changed,
+ private void rollback(List<UpdateOp> changed,
UpdateOp commitRoot) {
DocumentStore store = nodeStore.getDocumentStore();
for (UpdateOp op : changed) {
@@ -467,11 +422,6 @@ public class Commit {
}
store.findAndUpdate(NODES, reverse);
}
- for (UpdateOp op : newDocuments) {
- UpdateOp reverse = op.getReverseOperation();
- NodeDocument.setDeletedOnce(reverse);
- store.findAndUpdate(NODES, reverse);
- }
UpdateOp removeCollision = new UpdateOp(commitRoot.getId(), false);
NodeDocument.removeCollision(removeCollision, revision);
store.findAndUpdate(NODES, removeCollision);
@@ -490,6 +440,12 @@ public class Commit {
checkSplitCandidate(doc);
}
+ private void checkSplitCandidate(Iterable<NodeDocument> docs) {
+ for (NodeDocument doc : docs) {
+ checkSplitCandidate(doc);
+ }
+ }
+
private void checkSplitCandidate(@Nullable NodeDocument doc) {
if (doc != null && doc.getMemory() > SPLIT_CANDIDATE_THRESHOLD) {
nodeStore.addSplitCandidate(doc.getId());
@@ -587,6 +543,25 @@ public class Commit {
}
}
+ private void checkConflicts(List<NodeDocument> oldDocs,
+ List<UpdateOp> updates) {
+ int i = 0;
+ List<ConflictException> exceptions = new
ArrayList<ConflictException>();
+ Set<Revision> revisions = new HashSet<Revision>();
+ for (NodeDocument doc : oldDocs) {
+ UpdateOp op = updates.get(i++);
+ try {
+ checkConflicts(op, doc);
+ } catch (ConflictException e) {
+ exceptions.add(e);
+ Iterables.addAll(revisions, e.getConflictRevisions());
+ }
+ }
+ if (!exceptions.isEmpty()) {
+ throw new ConflictException("Following exceptions occurred during
the bulk update operations: " + exceptions, revisions);
+ }
+ }
+
private String formatConflictRevision(Revision r) {
if (nodeStore.getHeadRevision().isRevisionNewer(r)) {
return r + " (not yet visible)";
Modified:
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java?rev=1728008&r1=1728007&r2=1728008&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreTest.java
Mon Feb 1 21:15:08 2016
@@ -239,10 +239,10 @@ public class DocumentNodeStoreTest {
final Semaphore created = new Semaphore(0);
DocumentStore docStore = new MemoryDocumentStore() {
@Override
- public <T extends Document> boolean create(Collection<T>
collection,
- List<UpdateOp>
updateOps) {
+ public <T extends Document> List<T> createOrUpdate(Collection<T>
collection,
+ List<UpdateOp>
updateOps) {
Semaphore semaphore = locks.get(Thread.currentThread());
- boolean result = super.create(collection, updateOps);
+ List<T> result = super.createOrUpdate(collection, updateOps);
if (semaphore != null) {
created.release();
semaphore.acquireUninterruptibly();