Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 49ACAE949 for ; Mon, 25 Feb 2013 12:50:46 +0000 (UTC) Received: (qmail 39950 invoked by uid 500); 25 Feb 2013 12:50:46 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 39901 invoked by uid 500); 25 Feb 2013 12:50:45 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-dev@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 39864 invoked by uid 99); 25 Feb 2013 12:50:43 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 25 Feb 2013 12:50:43 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 25 Feb 2013 12:50:41 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id D51632388900; Mon, 25 Feb 2013 12:50:20 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1449687 - /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Date: Mon, 25 Feb 2013 12:50:20 -0000 To: oak-commits@jackrabbit.apache.org From: mreutegg@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20130225125020.D51632388900@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: mreutegg Date: Mon Feb 25 12:50:20 2013 New Revision: 1449687 URL: http://svn.apache.org/r1449687 Log: OAK-638: Avoid branch/merge for small commits Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java?rev=1449687&r1=1449686&r2=1449687&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStoreBranch.java Mon Feb 25 12:50:20 2013 @@ -19,9 +19,12 @@ package org.apache.jackrabbit.oak.kernel import org.apache.jackrabbit.mk.api.MicroKernel; import org.apache.jackrabbit.mk.api.MicroKernelException; import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder; import org.apache.jackrabbit.oak.spi.commit.CommitHook; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch; +import org.apache.jackrabbit.oak.spi.state.RebaseDiff; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -46,12 +49,15 @@ class KernelNodeStoreBranch implements N /** Revision of the base state of this branch*/ private String baseRevision; - /** Root state of the head revision of this branch*/ + /** Root state of the transient head revision on top of persisted branch, null if merged. */ private NodeState head; - /** Head revision of this branch, null if not yet branched*/ + /** Head revision of persisted branch, null if not yet branched*/ private String headRevision; + /** Number of updates to this branch via {@link #setRoot(NodeState)} */ + private int updates = 0; + KernelNodeStoreBranch(KernelNodeStore store, KernelNodeState root) { this.store = store; this.base = root; @@ -74,9 +80,20 @@ class KernelNodeStoreBranch implements N public void setRoot(NodeState newRoot) { checkNotMerged(); if (!head.equals(newRoot)) { - JsopDiff diff = new JsopDiff(store.getKernel()); - newRoot.compareAgainstBaseState(head, diff); - commit(diff.toString()); + NodeState oldRoot = head; + head = newRoot; + if (++updates > 1) { + // persist unless this is the first update + boolean success = false; + try { + persistTransientHead(); + success = true; + } finally { + if (!success) { + head = oldRoot; + } + } + } } } @@ -126,23 +143,36 @@ class KernelNodeStoreBranch implements N public NodeState merge(CommitHook hook) throws CommitFailedException { checkNotMerged(); NodeState toCommit = checkNotNull(hook).processCommit(base, head); - NodeState oldRoot = head; - setRoot(toCommit); + NodeState oldRoot = head; + head = toCommit; try { - if (headRevision == null) { + if (head.equals(base)) { // Nothing was written to this branch: return base state head = null; // Mark as merged return base; } else { MicroKernel kernel = store.getKernel(); - String mergedRevision = kernel.merge(headRevision, null); - headRevision = null; + String newRevision; + JsopDiff diff = new JsopDiff(kernel); + if (headRevision == null) { + // no branch created yet, commit directly + head.compareAgainstBaseState(base, diff); + newRevision = kernel.commit("", diff.toString(), baseRevision, null); + } else { + // commit into branch and merge + head.compareAgainstBaseState(store.getRootState(headRevision), diff); + if (diff.toString().length() > 0) { + headRevision = kernel.commit("", diff.toString(), headRevision, null); + } + newRevision = kernel.merge(headRevision, null); + headRevision = null; + } head = null; // Mark as merged - return store.getRootState(mergedRevision); + return store.getRootState(newRevision); } } catch (MicroKernelException e) { - setRoot(oldRoot); + head = oldRoot; throw new CommitFailedException(e); } } @@ -150,12 +180,22 @@ class KernelNodeStoreBranch implements N @Override public void rebase() { KernelNodeState root = store.getRoot(); - if (headRevision == null) { + if (head.equals(root)) { // Nothing was written to this branch: set new base revision head = root; base = root; baseRevision = root.getRevision(); + } else if (headRevision == null) { + // Nothing written to persistent branch yet + // perform rebase in memory + NodeBuilder builder = new MemoryNodeBuilder(root); + getRoot().compareAgainstBaseState(getBase(), new RebaseDiff(builder)); + head = builder.getNodeState(); + base = root; + baseRevision = root.getRevision(); } else { + // perform rebase in kernel + persistTransientHead(); headRevision = store.getKernel().rebase(headRevision, root.getRevision()); head = store.getRootState(headRevision); base = root; @@ -189,7 +229,56 @@ class KernelNodeStoreBranch implements N headRevision = kernel.branch(baseRevision); } + // persist transient changes first + persistTransientHead(); + headRevision = kernel.commit("", jsop, headRevision, null); head = store.getRootState(headRevision); } + + private void persistTransientHead() { + NodeState oldBase = base; + String oldBaseRevision = baseRevision; + NodeState oldHead = head; + String oldHeadRevision = headRevision; + boolean success = false; + try { + MicroKernel kernel = store.getKernel(); + JsopDiff diff = new JsopDiff(store.getKernel()); + if (headRevision == null) { + // no persistent branch yet + if (head.equals(base)) { + // nothing to persist + success = true; + return; + } else { + // create branch + headRevision = kernel.branch(baseRevision); + head.compareAgainstBaseState(base, diff); + } + } else { + // compare against head of branch + NodeState branchHead = store.getRootState(headRevision); + if (head.equals(branchHead)) { + // nothing to persist + return; + } else { + head.compareAgainstBaseState(branchHead, diff); + } + } + // if we get here we have something to persist + // and a branch exists + headRevision = kernel.commit("", diff.toString(), headRevision, null); + head = store.getRootState(headRevision); + success = true; + } finally { + // revert to old state if unsuccessful + if (!success) { + base = oldBase; + baseRevision = oldBaseRevision; + head = oldHead; + headRevision = oldHeadRevision; + } + } + } }