Author: tomekr Date: Fri Aug 11 08:31:12 2017 New Revision: 1804757 URL: http://svn.apache.org/viewvc?rev=1804757&view=rev Log: OAK-6548: Composite node builder/state keeps references to all the ancestor states
Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java Removed: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/util/Memoizer.java Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java?rev=1804757&r1=1804756&r2=1804757&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java (original) +++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java Fri Aug 11 08:31:12 2017 @@ -22,41 +22,31 @@ import org.apache.jackrabbit.oak.api.Blo import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.PathUtils; -import org.apache.jackrabbit.oak.composite.util.Memoizer; import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; import org.apache.jackrabbit.oak.spi.state.MoveDetector; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Maps.newHashMap; import static java.lang.Long.MAX_VALUE; import static java.util.Collections.singleton; import static org.apache.jackrabbit.oak.composite.CompositeNodeState.STOP_COUNTING_CHILDREN; import static org.apache.jackrabbit.oak.composite.CompositeNodeState.accumulateChildSizes; -import static org.apache.jackrabbit.oak.composite.CompositeNodeState.wrapWithNullCheck; import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE; import static org.apache.jackrabbit.oak.spi.state.AbstractNodeState.checkValidName; class CompositeNodeBuilder implements NodeBuilder { - private static final Logger LOG = LoggerFactory.getLogger(CompositeNodeBuilder.class); - private final String path; private final CompositionContext ctx; - private Function<MountedNodeStore, NodeBuilder> nodeBuilders; + private NodeMap<NodeBuilder> nodeBuilders; private final MountedNodeStore owningStore; @@ -64,14 +54,14 @@ class CompositeNodeBuilder implements No private final CompositeNodeBuilder rootBuilder; - CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder> nodeBuilders, CompositionContext ctx) { + CompositeNodeBuilder(String path, NodeMap<NodeBuilder> nodeBuilders, CompositionContext ctx) { this(path, nodeBuilders, ctx, null); } - private CompositeNodeBuilder(String path, Function<MountedNodeStore, NodeBuilder> nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) { + private CompositeNodeBuilder(String path, NodeMap<NodeBuilder> nodeBuilders, CompositionContext ctx, CompositeNodeBuilder parent) { this.path = path; this.ctx = ctx; - this.nodeBuilders = wrapWithNullCheck(Memoizer.memoize(nodeBuilders), LOG, path); + this.nodeBuilders = nodeBuilders; this.owningStore = ctx.getOwningStore(path); this.parent = parent; if (parent == null) { @@ -82,23 +72,17 @@ class CompositeNodeBuilder implements No } NodeBuilder getNodeBuilder(MountedNodeStore mns) { - return nodeBuilders.apply(mns); + return nodeBuilders.get(mns); } @Override public CompositeNodeState getNodeState() { - Map<MountedNodeStore, NodeState> states = ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(), - nodeBuilders - .andThen(n -> n.exists() ? n.getNodeState() : MISSING_NODE))); - return new CompositeNodeState(path, states, ctx); + return new CompositeNodeState(path, nodeBuilders.getAndApply(n -> n.exists() ? n.getNodeState() : MISSING_NODE), ctx); } @Override public CompositeNodeState getBaseState() { - Map<MountedNodeStore, NodeState> states = ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(), - nodeBuilders - .andThen(NodeBuilder::getBaseState))); - return new CompositeNodeState(path, states, ctx); + return new CompositeNodeState(path, nodeBuilders.getAndApply(NodeBuilder::getBaseState), ctx); } // node or property-related methods ; directly delegate to wrapped builder @@ -208,7 +192,7 @@ class CompositeNodeBuilder implements No // Count the children in each contributing store. return accumulateChildSizes(FluentIterable.from(contributingStores) .transformAndConcat(mns -> { - NodeBuilder node = nodeBuilders.apply(mns); + NodeBuilder node = nodeBuilders.get(mns); if (node.getChildNodeCount(max) == MAX_VALUE) { return singleton(STOP_COUNTING_CHILDREN); } else { @@ -222,7 +206,7 @@ class CompositeNodeBuilder implements No public Iterable<String> getChildNodeNames() { return FluentIterable.from(ctx.getContributingStoresForBuilders(path, nodeBuilders)) .transformAndConcat(mns -> FluentIterable - .from(nodeBuilders.apply(mns).getChildNodeNames()) + .from(nodeBuilders.get(mns).getChildNodeNames()) .filter(e -> belongsToStore(mns, e))); } @@ -230,7 +214,7 @@ class CompositeNodeBuilder implements No public boolean hasChildNode(String name) { String childPath = simpleConcat(path, name); MountedNodeStore mountedStore = ctx.getOwningStore(childPath); - return nodeBuilders.apply(mountedStore).hasChildNode(name); + return nodeBuilders.get(mountedStore).hasChildNode(name); } @Override @@ -243,24 +227,22 @@ class CompositeNodeBuilder implements No } private void createAncestors(MountedNodeStore mountedNodeStore) { - NodeBuilder builder = rootBuilder.nodeBuilders.apply(mountedNodeStore); + NodeBuilder builder = rootBuilder.nodeBuilders.get(mountedNodeStore); for (String element : PathUtils.elements(path)) { builder = builder.child(element); } - - // the nodeBuilders function should be updated, to return the new node builder - Map<MountedNodeStore, NodeBuilder> map = newHashMap(ctx.getAllMountedNodeStores().stream().collect(Collectors.toMap(Function.identity(), nodeBuilders))); - map.put(mountedNodeStore, builder); - nodeBuilders = wrapWithNullCheck(m -> map.get(m), LOG, path); + synchronized(this) { + nodeBuilders = nodeBuilders.replaceNode(mountedNodeStore, builder); + } } @Override public NodeBuilder getChildNode(final String name) { String childPath = simpleConcat(path, name); if (!ctx.shouldBeComposite(childPath)) { - return nodeBuilders.apply(ctx.getOwningStore(childPath)).getChildNode(name); + return nodeBuilders.get(ctx.getOwningStore(childPath)).getChildNode(name); } - return new CompositeNodeBuilder(childPath, nodeBuilders.andThen(b -> b.getChildNode(name)), ctx, this); + return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> b.getChildNode(name)), ctx, this); } @Override @@ -273,15 +255,14 @@ class CompositeNodeBuilder implements No checkState(exists(), "This builder does not exist: " + PathUtils.getName(path)); String childPath = simpleConcat(path, name); final MountedNodeStore childStore = ctx.getOwningStore(childPath); - if (childStore != owningStore && !nodeBuilders.apply(childStore).exists()) { + if (childStore != owningStore && !nodeBuilders.get(childStore).exists()) { createAncestors(childStore); } - final NodeBuilder childBuilder = nodeBuilders.apply(childStore).setChildNode(name, nodeState); + final NodeBuilder childBuilder = nodeBuilders.get(childStore).setChildNode(name, nodeState); if (!ctx.shouldBeComposite(childPath)) { return childBuilder; } - - return new CompositeNodeBuilder(childPath, m -> m == childStore ? childBuilder : nodeBuilders.apply(m).getChildNode(name), ctx, this); + return new CompositeNodeBuilder(childPath, nodeBuilders.lazyApply(b -> b.getChildNode(name)).replaceNode(childStore, childBuilder), ctx, this); } @Override @@ -314,7 +295,7 @@ class CompositeNodeBuilder implements No } private NodeBuilder getWrappedNodeBuilder() { - return nodeBuilders.apply (owningStore); + return nodeBuilders.get(owningStore); } private void annotateSourcePath() { Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java?rev=1804757&r1=1804756&r2=1804757&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java (original) +++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java Fri Aug 11 08:31:12 2017 @@ -21,19 +21,13 @@ package org.apache.jackrabbit.oak.compos import com.google.common.collect.FluentIterable; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.commons.PathUtils; -import org.apache.jackrabbit.oak.composite.util.Memoizer; import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.AbstractNodeState; import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStateDiff; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; import static java.lang.Long.MAX_VALUE; import static java.util.Collections.singleton; @@ -41,8 +35,6 @@ import static org.apache.jackrabbit.oak. class CompositeNodeState extends AbstractNodeState { - private static final Logger LOG = LoggerFactory.getLogger(CompositeNodeState.class); - // A note on content held by node stores which is outside the mount boundaries // // As a matter of design, mounted stores will definitely hold information _above_ @@ -60,40 +52,21 @@ class CompositeNodeState extends Abstrac private final String path; + private final NodeMap<NodeState> nodeStates; + private final CompositionContext ctx; private final MountedNodeStore owningStore; - private final Function<MountedNodeStore, NodeState> nodeStates; - - CompositeNodeState(String path, Map<MountedNodeStore, NodeState> nodeStates, CompositionContext ctx) { + CompositeNodeState(String path, NodeMap<NodeState> nodeStates, CompositionContext ctx) { this.path = path; this.ctx = ctx; - this.nodeStates = wrapWithNullCheck(mns -> nodeStates.get(mns), LOG, path); + this.nodeStates = nodeStates; this.owningStore = ctx.getOwningStore(path); } - CompositeNodeState(String path, Function<MountedNodeStore, NodeState> nodeStates, CompositionContext ctx) { - this.path = path; - this.ctx = ctx; - this.nodeStates = wrapWithNullCheck(Memoizer.memoize(nodeStates), LOG, path); - this.owningStore = ctx.getOwningStore(path); - } - - static <N> Function<MountedNodeStore, N> wrapWithNullCheck(Function<MountedNodeStore, N> f, Logger log, String path) { - return mns -> { - N nodeState = f.apply(mns); - if (nodeState == null) { - // this shouldn't happen, so we need to log some more debug info - log.warn("Can't find node state for path {} and mount {}.", path, mns); - throw new IllegalStateException("Can't find the node state for mount " + mns); - } - return nodeState; - }; - } - NodeState getNodeState(MountedNodeStore mns) { - return nodeStates.apply(mns); + return nodeStates.get(mns); } @Override @@ -127,16 +100,16 @@ class CompositeNodeState extends Abstrac public boolean hasChildNode(String name) { String childPath = simpleConcat(path, name); MountedNodeStore mountedStore = ctx.getOwningStore(childPath); - return nodeStates.apply(mountedStore).hasChildNode(name); + return nodeStates.get(mountedStore).hasChildNode(name); } @Override public NodeState getChildNode(final String name) { String childPath = simpleConcat(path, name); if (!ctx.shouldBeComposite(childPath)) { - return nodeStates.apply(ctx.getOwningStore(childPath)).getChildNode(name); + return nodeStates.get(ctx.getOwningStore(childPath)).getChildNode(name); } - Function<MountedNodeStore, NodeState> newNodeStates = nodeStates.andThen(n -> n.getChildNode(name)); + NodeMap<NodeState> newNodeStates = nodeStates.lazyApply(n -> n.getChildNode(name)); return new CompositeNodeState(childPath, newNodeStates, ctx); } @@ -151,7 +124,7 @@ class CompositeNodeState extends Abstrac // Count the children in each contributing store. return accumulateChildSizes(FluentIterable.from(contributingStores) .transformAndConcat(mns -> { - NodeState node = nodeStates.apply(mns); + NodeState node = nodeStates.get(mns); if (node.getChildNodeCount(max) == MAX_VALUE) { return singleton(STOP_COUNTING_CHILDREN); } else { @@ -176,7 +149,7 @@ class CompositeNodeState extends Abstrac public Iterable<? extends ChildNodeEntry> getChildNodeEntries() { return FluentIterable.from(ctx.getContributingStoresForNodes(path, nodeStates)) .transformAndConcat(mns -> FluentIterable - .from(nodeStates.apply(mns).getChildNodeNames()) + .from(nodeStates.get(mns).getChildNodeNames()) .filter(n -> belongsToStore(mns, n))) .transform(n -> new MemoryChildNodeEntry(n, getChildNode(n))); } @@ -192,8 +165,8 @@ class CompositeNodeState extends Abstrac continue; } NodeStateDiff childrenDiffFilter = new ChildrenDiffFilter(wrappingDiff, mns, false); - NodeState contributing = nodeStates.apply(mns); - NodeState contributingBase = multiBase.nodeStates.apply(mns); + NodeState contributing = nodeStates.get(mns); + NodeState contributingBase = multiBase.nodeStates.get(mns); full = full && contributing.compareAgainstBaseState(contributingBase, childrenDiffFilter); } return full; @@ -205,11 +178,11 @@ class CompositeNodeState extends Abstrac // write operations @Override public CompositeNodeBuilder builder() { - return new CompositeNodeBuilder(path, nodeStates.andThen(NodeState::builder), ctx); + return new CompositeNodeBuilder(path, nodeStates.lazyApply(NodeState::builder), ctx); } private NodeState getWrappedNodeState() { - return nodeStates.apply(owningStore); + return nodeStates.get(owningStore); } private boolean belongsToStore(MountedNodeStore mns, String childName) { Modified: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java?rev=1804757&r1=1804756&r2=1804757&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java (original) +++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java Fri Aug 11 08:31:12 2017 @@ -81,12 +81,12 @@ class CompositionContext { } } - List<MountedNodeStore> getContributingStoresForNodes(String path, final Function<MountedNodeStore, NodeState> nodeStates) { - return getContributingStores(path, mns -> nodeStates.apply(mns).getChildNodeNames()); + List<MountedNodeStore> getContributingStoresForNodes(String path, final NodeMap<NodeState> nodeStates) { + return getContributingStores(path, mns -> nodeStates.get(mns).getChildNodeNames()); } - List<MountedNodeStore> getContributingStoresForBuilders(String path, final Function<MountedNodeStore, NodeBuilder> nodeBuilders) { - return getContributingStores(path, mns -> nodeBuilders.apply(mns).getChildNodeNames()); + List<MountedNodeStore> getContributingStoresForBuilders(String path, final NodeMap<NodeBuilder> nodeBuilders) { + return getContributingStores(path, mns -> nodeBuilders.get(mns).getChildNodeNames()); } boolean shouldBeComposite(final String path) { @@ -171,7 +171,7 @@ class CompositionContext { if (rootStates.size() != nonDefaultStores.size() + 1) { throw new IllegalArgumentException("Too many root states passed: " + rootStates.size()); } - return new CompositeNodeState("/", rootStates, this); + return new CompositeNodeState("/", NodeMap.create(rootStates), this); } } Added: jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java?rev=1804757&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java (added) +++ jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/NodeMap.java Fri Aug 11 08:31:12 2017 @@ -0,0 +1,109 @@ +/* + * 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.composite; + +import com.google.common.collect.ImmutableMap; +import org.apache.jackrabbit.oak.spi.mount.Mount; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +public class NodeMap<T> { + + private final Map<MountedNodeStore, CacheableSupplier<T>> suppliers; + + private NodeMap(Map<MountedNodeStore, CacheableSupplier<T>> suppliers) { + this.suppliers = suppliers; + } + + public static <T> NodeMap<T> create(Map<MountedNodeStore, T> nodes) { + ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<T>> suppliers = ImmutableMap.builder(); + nodes.forEach((mns, node) -> suppliers.put(mns, new CacheableSupplier<T>(node))); + return new NodeMap<>(suppliers.build()); + } + + public T get(MountedNodeStore nodeStore) { + if (!suppliers.containsKey(nodeStore)) { + Mount mount = nodeStore.getMount(); + String mountName = mount.isDefault() ? "[default]" : mount.getName(); + throw new IllegalStateException("Node is not available for the node store " + mountName); + } + return suppliers.get(nodeStore).get(); + } + + public <R> NodeMap<R> getAndApply(Function<T, R> function) { + ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<R>> newSuppliers = ImmutableMap.builder(); + suppliers.forEach((mns, node) -> newSuppliers.put(mns, node.getAndApply(function))); + return new NodeMap<>(newSuppliers.build()); + } + + public <R> NodeMap<R> lazyApply(Function<T, R> function) { + ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<R>> newSuppliers = ImmutableMap.builder(); + suppliers.forEach((mns, node) -> newSuppliers.put(mns, node.lazyApply(function))); + return new NodeMap<>(newSuppliers.build()); + } + + public NodeMap<T> replaceNode(MountedNodeStore nodeStore, T node) { + ImmutableMap.Builder<MountedNodeStore, CacheableSupplier<T>> newSuppliers = ImmutableMap.builder(); + suppliers.forEach((mns, n) -> { + if (mns != nodeStore) { + newSuppliers.put(mns, n); + } + }); + newSuppliers.put(nodeStore, new CacheableSupplier<>(node)); + return new NodeMap<>(newSuppliers.build()); + } + + private static class CacheableSupplier<T> implements Supplier<T> { + + private Supplier<T> supplier; + + private volatile T value; + + public CacheableSupplier(Supplier<T> supplier) { + this.supplier = supplier; + } + + public CacheableSupplier(T value) { + this.value = value; + } + + @Override + public T get() { + if (value == null) { + synchronized (this) { + if (value == null) { + value = supplier.get(); + supplier = null; + } + } + } + return value; + } + + public <R> CacheableSupplier<R> getAndApply(Function<T, R> function) { + return new CacheableSupplier<>(function.apply(get())); + } + + public <R> CacheableSupplier<R> lazyApply(Function<T, R> function) { + return new CacheableSupplier<>(() -> function.apply(get())); + } + } +}