You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2012/07/26 09:25:30 UTC
svn commit: r1365900 -
/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
Author: jukka
Date: Thu Jul 26 07:25:30 2012
New Revision: 1365900
URL: http://svn.apache.org/viewvc?rev=1365900&view=rev
Log:
OAK-167: Caching NodeStore implementation
Better handling (and documentation) of the unmodified/connected state changes
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java?rev=1365900&r1=1365899&r2=1365900&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/MemoryNodeStateBuilder.java Thu Jul 26 07:25:30 2012
@@ -41,7 +41,31 @@ import com.google.common.collect.Iterabl
import com.google.common.collect.Maps;
/**
- * In-memory node state builder.
+ * In-memory node state builder. The following two builder states are used
+ * to properly track uncommitted chances without relying on weak references
+ * or requiring hard references on the entire accessed subtree:
+ * <dl>
+ * <dt>unmodified</dt>
+ * <dd>
+ * A child builder with no content changes starts in this state.
+ * It keeps a reference to the parent builder and knows it's name for
+ * use when connecting. Before each access the unconnected builder
+ * checks the parent for relevant changes to connect to. As long as
+ * there are no such changes, the builder remains unconnected and
+ * uses the immutable base state to respond to any content accesses.
+ * </dd>
+ * <dt>connected</dt>
+ * <dd>
+ * Once a child node is first modified, it switches it's internal
+ * state from the immutable base state to a mutable one and records
+ * a hard reference to that state in the mutable parent state. After
+ * that the parent reference is cleared and no more state checks are
+ * made. Any other builder instances that refer to the same child node
+ * will update their internal states to point to that same mutable
+ * state instance and thus become connected at next access.
+ * A root state builder is always connected.
+ * </dd>
+ * </dl>
*/
public class MemoryNodeStateBuilder implements NodeStateBuilder {
@@ -50,24 +74,29 @@ public class MemoryNodeStateBuilder impl
ImmutableMap.<String, NodeState>of());
/**
- * Parent state builder reference, or {@code null} for a connected
- * builder.
+ * Parent state builder reference, or {@code null} once this builder
+ * has been connected.
*/
private MemoryNodeStateBuilder parent;
/**
* Name of this child node within the parent builder, or {@code null}
- * for a connected builder.
+ * once this builder has been connected.
*/
private String name;
/**
- * The current state of this builder. Initially set to the immutable
- * base state until this builder gets <em>connected</em>, after which
- * this reference will point to the {@link MutableNodeState} instance
- * that records all the changes to this node.
+ * The read state of this builder. Originally the immutable base state
+ * in an unconnected builder, or the shared mutable state
+ * (see {@link #writeState}) once the this builder has been connected.
*/
- private NodeState state;
+ private NodeState readState;
+
+ /**
+ * The shared mutable state of connected builder instances, or
+ * {@code null} until this builder has been connected.
+ */
+ private MutableNodeState writeState;
/**
* Creates a new in-memory node state builder.
@@ -80,7 +109,8 @@ public class MemoryNodeStateBuilder impl
MemoryNodeStateBuilder parent, String name, NodeState base) {
this.parent = checkNotNull(parent);
this.name = checkNotNull(name);
- this.state = checkNotNull(base);
+ this.readState = checkNotNull(base);
+ this.writeState = null;
}
/**
@@ -91,60 +121,53 @@ public class MemoryNodeStateBuilder impl
public MemoryNodeStateBuilder(NodeState base) {
this.parent = null;
this.name = null;
- this.state = new MutableNodeState(checkNotNull(base));
+ MutableNodeState mstate = new MutableNodeState(checkNotNull(base));
+ this.readState = mstate;
+ this.writeState = mstate;
}
private NodeState read() {
- if (parent != null) {
- NodeState pstate = parent.read();
- if (pstate instanceof MutableNodeState) {
- MutableNodeState mstate = (MutableNodeState) pstate;
- MemoryNodeStateBuilder existing = mstate.builders.get(name);
- if (existing != null) {
- state = existing.state;
+ if (writeState == null) {
+ parent.read();
+ MutableNodeState pstate = parent.writeState;
+ if (pstate != null) {
+ MutableNodeState mstate = pstate.nodes.get(name);
+ if (mstate == null && pstate.nodes.containsKey(name)) {
+ mstate = new MutableNodeState(NULL_STATE);
+ }
+ if (mstate != null) {
parent = null;
name = null;
+ readState = mstate;
+ writeState = mstate;
}
}
}
- return state;
+ return readState;
}
private MutableNodeState write() {
- if (parent != null) {
- MutableNodeState mstate = parent.write();
- MemoryNodeStateBuilder existing = mstate.builders.get(name);
- if (existing != null) {
- state = existing.state;
- } else {
- state = new MutableNodeState(state);
- mstate.builders.put(name, this);
+ if (writeState == null) {
+ MutableNodeState pstate = parent.write();
+ MutableNodeState mstate = pstate.nodes.get(name);
+ if (mstate == null) {
+ if (pstate.nodes.containsKey(name)) {
+ mstate = new MutableNodeState(NULL_STATE);
+ } else {
+ mstate = new MutableNodeState(readState);
+ pstate.nodes.put(name, mstate);
+ }
}
parent = null;
name = null;
+ readState = mstate;
+ writeState = mstate;
}
- return (MutableNodeState) state;
+ return writeState;
}
- private void reset(NodeState newBase) {
- MutableNodeState mstate = write();
-
- mstate.base = newBase;
-
- mstate.properties.clear();
-
- Iterator<Map.Entry<String, MemoryNodeStateBuilder>> iterator =
- mstate.builders.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<String, MemoryNodeStateBuilder> entry = iterator.next();
- MemoryNodeStateBuilder childBuilder = entry.getValue();
- NodeState childBase = newBase.getChildNode(entry.getKey());
- if (childBase == null || childBuilder == null) {
- iterator.remove();
- } else {
- childBuilder.reset(childBase);
- }
- }
+ protected void reset(NodeState newBase) {
+ write().reset(newBase);
}
/**
@@ -172,8 +195,8 @@ public class MemoryNodeStateBuilder impl
protected NodeState getBaseState() {
NodeState state = read();
- if (state instanceof MutableNodeState) {
- return ((MutableNodeState) state).base;
+ if (writeState != null) {
+ return writeState.base;
} else {
return state;
}
@@ -182,8 +205,8 @@ public class MemoryNodeStateBuilder impl
@Override
public NodeState getNodeState() {
NodeState state = read();
- if (state instanceof MutableNodeState) {
- return ((MutableNodeState) state).snapshot();
+ if (writeState != null) {
+ return writeState.snapshot();
} else {
return state;
}
@@ -208,10 +231,11 @@ public class MemoryNodeStateBuilder impl
public void setNode(String name, NodeState state) {
MutableNodeState mstate = write();
- MemoryNodeStateBuilder builder = mstate.builders.get(name);
- if (builder != null) {
- builder.reset(state);
+ MutableNodeState cstate = mstate.nodes.get(name);
+ if (cstate != null) {
+ cstate.reset(state);
} else {
+ mstate.nodes.remove(name);
createChildBuilder(name, state).write();
}
@@ -223,9 +247,9 @@ public class MemoryNodeStateBuilder impl
MutableNodeState mstate = write();
if (mstate.base.getChildNode(name) != null) {
- mstate.builders.put(name, null);
+ mstate.nodes.put(name, null);
} else {
- mstate.builders.remove(name);
+ mstate.nodes.remove(name);
}
updated();
@@ -286,27 +310,25 @@ public class MemoryNodeStateBuilder impl
public NodeStateBuilder getChildBuilder(String name) {
NodeState state = read();
- if (!(state instanceof MutableNodeState)) {
+ if (writeState == null) {
NodeState base = state.getChildNode(name);
if (base != null) {
return createChildBuilder(name, base); // shortcut
}
}
+ NodeState cbase = NULL_STATE;
MutableNodeState mstate = write();
- MemoryNodeStateBuilder builder = mstate.builders.get(name);
- if (builder == null) {
- if (mstate.builders.containsKey(name)) {
- builder = createChildBuilder(name, NULL_STATE);
- builder.write();
- } else {
- NodeState base = mstate.base.getChildNode(name);
- if (base == null) {
- base = NULL_STATE;
- }
- builder = createChildBuilder(name, base);
- }
+ MutableNodeState cstate = mstate.nodes.get(name);
+ if (cstate != null) {
+ cbase = cstate.base;
+ } else if (!mstate.nodes.containsKey(name)
+ && mstate.base.hasChildNode(name)) {
+ return createChildBuilder(name, mstate.base.getChildNode(name));
}
+
+ MemoryNodeStateBuilder builder = createChildBuilder(name, cbase);
+ builder.write();
return builder;
}
@@ -331,29 +353,47 @@ public class MemoryNodeStateBuilder impl
Maps.newHashMap();
/**
- * Set of builders for added, modified or removed
- * ({@code null} value) child nodes.
+ * Set of added, modified or removed ({@code null} value)
+ * child nodes.
*/
- private final Map<String, MemoryNodeStateBuilder> builders =
+ private final Map<String, MutableNodeState> nodes =
Maps.newHashMap();
public MutableNodeState(NodeState base) {
- this.base = base;
+ this.base = checkNotNull(base);
}
- public NodeState snapshot() {
+ private NodeState snapshot() {
Map<String, PropertyState> props = Maps.newHashMap(properties);
- Map<String, NodeState> nodes = Maps.newHashMap();
- for (Map.Entry<String, MemoryNodeStateBuilder> entry
- : builders.entrySet()) {
- NodeStateBuilder builder = entry.getValue();
- if (builder != null) {
- nodes.put(entry.getKey(), builder.getNodeState());
+ Map<String, NodeState> snapshots = Maps.newHashMap();
+ for (Map.Entry<String, MutableNodeState> entry : nodes.entrySet()) {
+ String name = entry.getKey();
+ MutableNodeState node = entry.getValue();
+ if (node != null) {
+ snapshots.put(name, node.snapshot());
+ } else {
+ snapshots.put(name, null);
+ }
+ }
+ return new ModifiedNodeState(base, props, snapshots);
+ }
+
+ private void reset(NodeState newBase) {
+ base = newBase;
+ properties.clear();
+
+ Iterator<Map.Entry<String, MutableNodeState>> iterator =
+ nodes.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, MutableNodeState> entry = iterator.next();
+ MutableNodeState cstate = entry.getValue();
+ NodeState cbase = newBase.getChildNode(entry.getKey());
+ if (cbase == null || cstate == null) {
+ iterator.remove();
} else {
- nodes.put(entry.getKey(), null);
+ cstate.reset(cbase);
}
}
- return new ModifiedNodeState(base, props, nodes);
}
//-----------------------------------------------------< NodeState >--
@@ -405,8 +445,7 @@ public class MemoryNodeStateBuilder impl
public long getChildNodeCount() {
long count = base.getChildNodeCount();
- for (Map.Entry<String, MemoryNodeStateBuilder> entry
- : builders.entrySet()) {
+ for (Map.Entry<String, MutableNodeState> entry : nodes.entrySet()) {
if (base.getChildNode(entry.getKey()) != null) {
count--;
}
@@ -420,23 +459,23 @@ public class MemoryNodeStateBuilder impl
@Override
public boolean hasChildNode(String name) {
- NodeStateBuilder builder = builders.get(name);
- if (builder != null) {
+ MutableNodeState node = nodes.get(name);
+ if (node != null) {
return true;
- } else if (builders.containsKey(name)) {
+ } else if (nodes.containsKey(name)) {
return false;
}
- return base.getChildNode(name) != null;
+ return base.hasChildNode(name);
}
@Override
public Iterable<String> getChildNodeNames() {
Iterable<String> unmodified = base.getChildNodeNames();
Predicate<String> unmodifiedFilter = Predicates.not(Predicates.in(
- ImmutableSet.copyOf(builders.keySet())));
+ ImmutableSet.copyOf(nodes.keySet())));
Set<String> modified = ImmutableSet.copyOf(
- Maps.filterValues(builders, Predicates.notNull()).keySet());
+ Maps.filterValues(nodes, Predicates.notNull()).keySet());
return Iterables.concat(
Iterables.filter(unmodified, unmodifiedFilter),
modified);