Author: mreutegg
Date: Wed Mar 19 20:09:59 2014
New Revision: 1579378
URL: http://svn.apache.org/r1579378
Log:
OAK-1555: Inefficient node state diff with old revisions
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
(with props)
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.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=1579378&r1=1579377&r2=1579378&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
Wed Mar 19 20:09:59 2014
@@ -569,6 +569,7 @@ public class Commit {
}
list.add(p);
}
+ DiffCache.Entry cacheEntry = nodeStore.getDiffCache().newEntry(before,
revision);
List<String> added = new ArrayList<String>();
List<String> removed = new ArrayList<String>();
List<String> changed = new ArrayList<String>();
@@ -592,10 +593,10 @@ public class Commit {
boolean isNew = op != null && op.isNew();
boolean pendingLastRev = op == null
|| !NodeDocument.hasLastRev(op, revision.getClusterId());
- boolean isDelete = op != null && op.isDelete();
- nodeStore.applyChanges(revision, before, path, isNew, isDelete,
- pendingLastRev, isBranchCommit, added, removed, changed);
+ nodeStore.applyChanges(revision, path, isNew, pendingLastRev,
+ isBranchCommit, added, removed, changed, cacheEntry);
}
+ cacheEntry.done();
}
public void moveNode(String sourcePath, String targetPath) {
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java?rev=1579378&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java
Wed Mar 19 20:09:59 2014
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * A cache for child node diffs.
+ */
+public interface DiffCache {
+
+ /**
+ * Returns a jsop diff for the child nodes at the given path. The returned
+ * String may contain the following changes on child nodes:
+ * <ul>
+ * <li>Changed child nodes: e.g. {@code ^"foo":{}}</li>
+ * <li>Added child nodes: e.g. {@code +"bar":{}}</li>
+ * <li>Removed child nodes: e.g. {@code -"baz"}</li>
+ * </ul>
+ * A {@code null} value indicates that this cache does not have an entry
+ * for the given revision range at the path.
+ *
+ * @param from the from revision.
+ * @param to the to revision.
+ * @param path the path of the parent node.
+ * @return the diff or {@code null} if unknown.
+ */
+ @CheckForNull
+ public String getChanges(@Nonnull Revision from,
+ @Nonnull Revision to,
+ @Nonnull String path);
+
+ /**
+ * Starts a new cache entry for the diff cache. Actual changes are added
+ * to the entry with the {@link Entry#append(String, String)} method.
+ *
+ * @param from the from revision.
+ * @param to the to revision.
+ * @return the cache entry.
+ */
+ @Nonnull
+ public Entry newEntry(@Nonnull Revision from,
+ @Nonnull Revision to);
+
+ public interface Entry {
+
+ /**
+ * Appends changes about children of the node at the given path.
+ *
+ * @param path the path of the parent node.
+ * @param changes the child node changes.
+ */
+ public void append(@Nonnull String path,
+ @Nonnull String changes);
+
+ /**
+ * Called when all changes have been appended and the entry is ready
+ * to be used by the cache.
+ */
+ public void done();
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DiffCache.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java?rev=1579378&r1=1579377&r2=1579378&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java
Wed Mar 19 20:09:59 2014
@@ -445,6 +445,7 @@ public class DocumentMK implements Micro
private static final long DEFAULT_MEMORY_CACHE_SIZE = 256 * 1024 *
1024;
private DocumentNodeStore nodeStore;
private DocumentStore documentStore;
+ private DiffCache diffCache;
private BlobStore blobStore;
private int clusterId =
Integer.getInteger("oak.documentMK.clusterId", 0);
private int asyncDelay = 1000;
@@ -479,6 +480,10 @@ public class DocumentMK implements Micro
if (this.blobStore == null) {
this.blobStore = new MongoBlobStore(db);
}
+
+ if (this.diffCache == null) {
+ this.diffCache = new MongoDiffCache(db, this);
+ }
}
return this;
}
@@ -581,6 +586,18 @@ public class DocumentMK implements Micro
return nodeStore;
}
+ public DiffCache getDiffCache() {
+ if (diffCache == null) {
+ diffCache = new MemoryDiffCache(this);
+ }
+ return diffCache;
+ }
+
+ public Builder setDiffCache(DiffCache diffCache) {
+ this.diffCache = diffCache;
+ return this;
+ }
+
/**
* Set the blob store to use. By default an in-memory store is used.
*
@@ -643,7 +660,7 @@ public class DocumentMK implements Micro
public Builder memoryCacheSize(long memoryCacheSize) {
this.nodeCacheSize = memoryCacheSize * 25 / 100;
this.childrenCacheSize = memoryCacheSize * 10 / 100;
- this.diffCacheSize = memoryCacheSize * 2 / 100;
+ this.diffCacheSize = memoryCacheSize * 5 / 100;
this.docChildrenCacheSize = memoryCacheSize * 3 / 100;
this.documentCacheSize = memoryCacheSize - nodeCacheSize -
childrenCacheSize - diffCacheSize - docChildrenCacheSize;
return this;
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java?rev=1579378&r1=1579377&r2=1579378&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeState.java
Wed Mar 19 20:09:59 2014
@@ -52,7 +52,6 @@ import com.google.common.collect.Iterato
import com.google.common.collect.Maps;
import static com.google.common.base.Preconditions.checkNotNull;
-import static
org.apache.jackrabbit.oak.plugins.document.DocumentMK.MANY_CHILDREN_THRESHOLD;
import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
/**
@@ -241,8 +240,8 @@ class DocumentNodeState extends Abstract
if (lastRevision.equals(mBase.lastRevision)) {
// no differences
return true;
- } else if (getChildNodeCount(MANY_CHILDREN_THRESHOLD) >
MANY_CHILDREN_THRESHOLD) {
- // use DocumentNodeStore compare when there are many
children
+ } else {
+ // use DocumentNodeStore compare
return dispatch(store.diffChildren(this, mBase),
mBase, diff);
}
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1579378&r1=1579377&r2=1579378&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
Wed Mar 19 20:09:59 2014
@@ -249,12 +249,9 @@ public final class DocumentNodeStore
private final CacheStats docChildrenCacheStats;
/**
- * Diff cache.
- *
- * Key: PathRev, value: StringValue
+ * The change log to keep track of commits for diff operations.
*/
- private final Cache<CacheValue, StringValue> diffCache;
- private final CacheStats diffCacheStats;
+ private final DiffCache diffCache;
/**
* The blob store.
@@ -329,9 +326,7 @@ public final class DocumentNodeStore
docChildrenCacheStats = new CacheStats(docChildrenCache,
"Document-DocChildren",
builder.getWeigher(), builder.getDocChildrenCacheSize());
- diffCache = builder.buildCache(builder.getDiffCacheSize());
- diffCacheStats = new CacheStats(diffCache, "Document-Diff",
- builder.getWeigher(), builder.getDiffCacheSize());
+ diffCache = builder.getDiffCache();
// check if root node exists
if (store.find(Collection.NODES, Utils.getIdFromPath("/")) == null) {
@@ -526,10 +521,6 @@ public final class DocumentNodeStore
return docChildrenCacheStats;
}
- public CacheStats getDiffCacheStats() {
- return diffCacheStats;
- }
-
public int getPendingWriteCount() {
return unsavedLastRevisions.getPaths().size();
}
@@ -808,10 +799,8 @@ public final class DocumentNodeStore
* Apply the changes of a node to the cache.
*
* @param rev the commit revision
- * @param before the revision right before the commit.
* @param path the path
* @param isNew whether this is a new node
- * @param isDelete whether the node is deleted
* @param pendingLastRev whether the node has a pending _lastRev to write
* @param isBranchCommit whether this is from a branch commit
* @param added the list of added child nodes
@@ -819,10 +808,11 @@ public final class DocumentNodeStore
* @param changed the list of changed child nodes.
*
*/
- public void applyChanges(Revision rev, Revision before, String path,
- boolean isNew, boolean isDelete, boolean
pendingLastRev,
+ public void applyChanges(Revision rev, String path,
+ boolean isNew, boolean pendingLastRev,
boolean isBranchCommit, List<String> added,
- List<String> removed, List<String> changed) {
+ List<String> removed, List<String> changed,
+ DiffCache.Entry cacheEntry) {
UnsavedModifications unsaved = unsavedLastRevisions;
if (isBranchCommit) {
Revision branchRev = rev.asBranchRevision();
@@ -841,21 +831,21 @@ public final class DocumentNodeStore
}
c.children.addAll(set);
nodeChildrenCache.put(key, c);
- } else if (!isDelete) {
- // update diff cache for modified nodes
- PathRev key = diffCacheKey(path, before, rev);
- JsopWriter w = new JsopStream();
- for (String p : added) {
-
w.tag('+').key(PathUtils.getName(p)).object().endObject().newline();
- }
- for (String p : removed) {
- w.tag('-').value(PathUtils.getName(p)).newline();
- }
- for (String p : changed) {
-
w.tag('^').key(PathUtils.getName(p)).object().endObject().newline();
- }
- diffCache.put(key, new StringValue(w.toString()));
}
+
+ // update diff cache
+ JsopWriter w = new JsopStream();
+ for (String p : added) {
+
w.tag('+').key(PathUtils.getName(p)).object().endObject().newline();
+ }
+ for (String p : removed) {
+ w.tag('-').value(PathUtils.getName(p)).newline();
+ }
+ for (String p : changed) {
+
w.tag('^').key(PathUtils.getName(p)).object().endObject().newline();
+ }
+ cacheEntry.append(path, w.toString());
+
// update docChildrenCache
if (!added.isEmpty()) {
CacheValue docChildrenKey = new StringValue(path);
@@ -1110,23 +1100,16 @@ public final class DocumentNodeStore
* @return the json diff.
*/
String diffChildren(@Nonnull final DocumentNodeState node,
- @Nonnull final DocumentNodeState base) {
- PathRev key = diffCacheKey(node.getPath(),
- base.getLastRevision(), node.getLastRevision());
- try {
- return diffCache.get(key, new Callable<StringValue>() {
- @Override
- public StringValue call() throws Exception {
- return new StringValue(diffImpl(base, node));
- }
- }).toString();
- } catch (ExecutionException e) {
- if (e.getCause() instanceof MicroKernelException) {
- throw (MicroKernelException) e.getCause();
- } else {
- throw new MicroKernelException(e.getCause());
- }
+ @Nonnull final DocumentNodeState base) {
+ if (node.hasNoChildren() && base.hasNoChildren()) {
+ return "";
+ }
+ String diff = diffCache.getChanges(base.getLastRevision(),
+ node.getLastRevision(), node.getPath());
+ if (diff == null) {
+ diff = diffImpl(base, node);
}
+ return diff;
}
String diff(@Nonnull final String fromRevisionId,
@@ -1146,46 +1129,36 @@ public final class DocumentNodeStore
path, fromRev, from != null, toRev, to != null);
throw new MicroKernelException(msg);
}
- PathRev key = diffCacheKey(path, fromRev, toRev);
- try {
- JsopWriter writer = new JsopStream();
- diffProperties(from, to, writer);
- String compactDiff = diffCache.get(key, new
Callable<StringValue>() {
- @Override
- public StringValue call() throws Exception {
- return new StringValue(diffImpl(from, to));
+ String compactDiff = diffCache.getChanges(fromRev, toRev, path);
+ if (compactDiff == null) {
+ // calculate the diff
+ compactDiff = diffImpl(from, to);
+ }
+ JsopWriter writer = new JsopStream();
+ diffProperties(from, to, writer);
+ JsopTokenizer t = new JsopTokenizer(compactDiff);
+ int r;
+ do {
+ r = t.read();
+ switch (r) {
+ case '+':
+ case '^': {
+ String name = t.readString();
+ t.read(':');
+ t.read('{');
+ t.read('}');
+ writer.tag((char) r).key(PathUtils.concat(path, name));
+ writer.object().endObject().newline();
+ break;
}
- }).toString();
- JsopTokenizer t = new JsopTokenizer(compactDiff);
- int r;
- do {
- r = t.read();
- switch (r) {
- case '+':
- case '^': {
- String name = t.readString();
- t.read(':');
- t.read('{');
- t.read('}');
- writer.tag((char) r).key(PathUtils.concat(path, name));
- writer.object().endObject().newline();
- break;
- }
- case '-': {
- String name = t.readString();
- writer.tag('-').value(PathUtils.concat(path, name));
- writer.newline();
- }
+ case '-': {
+ String name = t.readString();
+ writer.tag('-').value(PathUtils.concat(path, name));
+ writer.newline();
}
- } while (r != JsopReader.END);
- return writer.toString();
- } catch (ExecutionException e) {
- if (e.getCause() instanceof MicroKernelException) {
- throw (MicroKernelException) e.getCause();
- } else {
- throw new MicroKernelException(e.getCause());
}
- }
+ } while (r != JsopReader.END);
+ return writer.toString();
}
//------------------------< Observable
>------------------------------------
@@ -1562,12 +1535,6 @@ public final class DocumentNodeStore
return new PathRev((name == null ? "" : name) + path, readRevision);
}
- private static PathRev diffCacheKey(@Nonnull String path,
- @Nonnull Revision from,
- @Nonnull Revision to) {
- return new PathRev(from + path, to);
- }
-
private static DocumentRootBuilder asDocumentRootBuilder(NodeBuilder
builder)
throws IllegalArgumentException {
if (!(builder instanceof DocumentRootBuilder)) {
@@ -1661,4 +1628,7 @@ public final class DocumentNodeStore
return new BlobReferenceIterator(this);
}
+ public DiffCache getDiffCache() {
+ return diffCache;
+ }
}
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1579378&r1=1579377&r2=1579378&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
Wed Mar 19 20:09:59 2014
@@ -261,16 +261,21 @@ public class DocumentNodeStoreService {
registrations.add(
registerMBean(wb,
CacheStatsMBean.class,
- store.getDiffCacheStats(),
- CacheStatsMBean.TYPE,
- store.getDiffCacheStats().getName()));
- registrations.add(
- registerMBean(wb,
- CacheStatsMBean.class,
store.getDocChildrenCacheStats(),
CacheStatsMBean.TYPE,
store.getDocChildrenCacheStats().getName())
);
+ DiffCache cl = store.getDiffCache();
+ if (cl instanceof MemoryDiffCache) {
+ MemoryDiffCache mcl = (MemoryDiffCache) cl;
+ registrations.add(
+ registerMBean(wb,
+ CacheStatsMBean.class,
+ mcl.getDiffCacheStats(),
+ CacheStatsMBean.TYPE,
+ mcl.getDiffCacheStats().getName()));
+
+ }
DocumentStore ds = store.getDocumentStore();
if (ds instanceof CachingDocumentStore) {
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java?rev=1579378&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
Wed Mar 19 20:09:59 2014
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.cache.CacheValue;
+import org.apache.jackrabbit.oak.plugins.document.util.StringValue;
+
+import com.google.common.cache.Cache;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An in-memory diff cache implementation.
+ */
+class MemoryDiffCache implements DiffCache {
+
+ /**
+ * Diff cache.
+ *
+ * Key: PathRev, value: StringValue
+ */
+ protected final Cache<CacheValue, StringValue> diffCache;
+ protected final CacheStats diffCacheStats;
+
+
+ MemoryDiffCache(DocumentMK.Builder builder) {
+ diffCache = builder.buildCache(builder.getDiffCacheSize());
+ diffCacheStats = new CacheStats(diffCache, "Document-Diff",
+ builder.getWeigher(), builder.getDiffCacheSize());
+ }
+
+ @CheckForNull
+ @Override
+ public String getChanges(@Nonnull Revision from,
+ @Nonnull Revision to,
+ @Nonnull String path) {
+ PathRev key = diffCacheKey(path, from, to);
+ StringValue diff = diffCache.getIfPresent(key);
+ return diff != null ? diff.toString() : null;
+ }
+
+ @Nonnull
+ @Override
+ public Entry newEntry(@Nonnull Revision from,
+ @Nonnull Revision to) {
+ return new MemoryEntry(from, to);
+ }
+
+ public CacheStats getDiffCacheStats() {
+ return diffCacheStats;
+ }
+
+ protected class MemoryEntry implements Entry {
+
+ private final Revision from;
+ private final Revision to;
+
+ protected MemoryEntry(Revision from, Revision to) {
+ this.from = checkNotNull(from);
+ this.to = checkNotNull(to);
+ }
+
+ @Override
+ public void append(@Nonnull String path, @Nonnull String changes) {
+ PathRev key = diffCacheKey(path, from, to);
+ diffCache.put(key, new StringValue(changes));
+ }
+
+ @Override
+ public void done() {
+ }
+ }
+
+ private static PathRev diffCacheKey(@Nonnull String path,
+ @Nonnull Revision from,
+ @Nonnull Revision to) {
+ return new PathRev(from + path, to);
+ }
+
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java?rev=1579378&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java
(added)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java
Wed Mar 19 20:09:59 2014
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.document;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.BasicDBObjectBuilder;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.MongoException;
+import com.mongodb.WriteConcern;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A diff cache implementation using a capped collection as a secondary cache.
+ */
+public class MongoDiffCache extends MemoryDiffCache {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(MongoDiffCache.class);
+
+ private static final long MB = 1024 * 1024;
+
+ private static final String COLLECTION_NAME = "changeLog";
+
+ // TODO: make configurable
+ private static final DBObject COLLECTION_OPTIONS =
BasicDBObjectBuilder.start()
+ .add("capped", true).add("size", 256 * MB).get();
+
+ private final DBCollection changes;
+
+ public MongoDiffCache(DB db, DocumentMK.Builder builder) {
+ super(builder);
+ if (db.collectionExists(COLLECTION_NAME)) {
+ changes = db.getCollection(COLLECTION_NAME);
+ } else {
+ changes = db.createCollection(COLLECTION_NAME, COLLECTION_OPTIONS);
+ }
+ }
+
+ @CheckForNull
+ @Override
+ public String getChanges(@Nonnull Revision from,
+ @Nonnull Revision to,
+ @Nonnull String path) {
+ // first try to serve from cache
+ String diff = super.getChanges(from, to, path);
+ if (diff != null) {
+ return diff;
+ }
+ // grab from mongo
+ DBObject obj = changes.findOne(new BasicDBObject("_id",
to.toString()));
+ if (obj == null) {
+ return null;
+ }
+ if (obj.get("_b").equals(from.toString())) {
+ // apply to diff cache and serve later requests from cache
+ Entry entry = super.newEntry(from, to);
+ applyToDiffCache(obj, "/", entry);
+ entry.done();
+
+ DBObject current = obj;
+ for (String name : PathUtils.elements(path)) {
+ String n = Utils.unescapePropertyName(name);
+ current = (DBObject) obj.get(n);
+ if (current == null) {
+ break;
+ }
+ }
+ if (current == null || !current.containsField("_c")) {
+ // no changes here
+ return "";
+ } else {
+ return current.get("_c").toString();
+ }
+ }
+ // diff request goes across multiple commits
+ // TODO: implement
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public Entry newEntry(@Nonnull final Revision from,
+ @Nonnull final Revision to) {
+ return new MemoryEntry(from, to) {
+
+ private BasicDBObject commit = new BasicDBObject();
+
+ {
+ commit.put("_id", to.toString());
+ commit.put("_b", from.toString());
+ }
+
+ @Override
+ public void append(@Nonnull String path, @Nonnull String changes) {
+ // super.append() will apply to diff cache in base class
+ super.append(path, changes);
+ BasicDBObject current = commit;
+ for (String name : PathUtils.elements(path)) {
+ String escName = Utils.escapePropertyName(name);
+ if (current.containsField(escName)) {
+ current = (BasicDBObject) current.get(escName);
+ } else {
+ BasicDBObject child = new BasicDBObject();
+ current.append(escName, child);
+ current = child;
+ }
+ }
+ current.append("_c", checkNotNull(changes));
+ }
+
+ @Override
+ public void done() {
+ try {
+ changes.insert(commit, WriteConcern.UNACKNOWLEDGED);
+ } catch (MongoException e) {
+ LOG.warn("Write back of diff cache entry failed", e);
+ }
+ }
+ };
+ }
+
+ private void applyToDiffCache(DBObject obj,
+ String path,
+ Entry entry) {
+ String diff = (String) obj.get("_c");
+ if (diff != null) {
+ entry.append(path, diff);
+ }
+ for (String k : obj.keySet()) {
+ if (Utils.isPropertyName(k)) {
+ String name = Utils.unescapePropertyName(k);
+ applyToDiffCache((DBObject) obj.get(k),
+ PathUtils.concat(path, name), entry);
+ }
+ }
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/MongoDiffCache.java
------------------------------------------------------------------------------
svn:eol-style = native