Encapsulate ShardedDOMDataTreeProducer layout 66/46366/2
authorRobert Varga <rovarga@cisco.com>
Sun, 18 Sep 2016 22:21:23 +0000 (00:21 +0200)
committerRobert Varga <nite@hq.sk>
Mon, 3 Oct 2016 13:49:39 +0000 (13:49 +0000)
This patch encapsulates all state related to sharding
layout into an inner class, so that this information
can be accessed concurrently.

Change-Id: If66a7ac5b574d612b31e7c52156b80580ef9f79a
Signed-off-by: Robert Varga <rovarga@cisco.com>
(cherry picked from commit 09111573277ec258069ecba5e02ad8750ca50633)

dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ProducerLayout.java [new file with mode: 0644]
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTree.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeProducer.java
dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ShardedDOMDataTreeWriteTransaction.java

diff --git a/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ProducerLayout.java b/dom/mdsal-dom-broker/src/main/java/org/opendaylight/mdsal/dom/broker/ProducerLayout.java
new file mode 100644 (file)
index 0000000..fccf22d
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.dom.broker;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
+import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
+import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
+import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ProducerLayout {
+    private static final Logger LOG = LoggerFactory.getLogger(ProducerLayout.class);
+
+    private final BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer;
+    private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children;
+    private final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap;
+
+    private ProducerLayout(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap,
+            final BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer,
+            final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children) {
+        this.shardMap = ImmutableMap.copyOf(shardMap);
+        this.idToProducer = Preconditions.checkNotNull(idToProducer);
+        this.children = Preconditions.checkNotNull(children);
+    }
+
+    static ProducerLayout create(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+        return new ProducerLayout(shardMap, mapIdsToProducer(shardMap), ImmutableMap.of());
+    }
+
+    private static BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> mapIdsToProducer(
+            final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+        final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToId = ArrayListMultimap.create();
+        // map which identifier belongs to which shard
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
+            shardToId.put(entry.getValue(), entry.getKey());
+        }
+
+        final Builder<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducerBuilder = ImmutableBiMap.builder();
+        for (final Entry<DOMDataTreeShard, Collection<DOMDataTreeIdentifier>> entry : shardToId.asMap().entrySet()) {
+            if (entry.getKey() instanceof WriteableDOMDataTreeShard) {
+                //create a single producer for all prefixes in a single shard
+                final DOMDataTreeShardProducer producer = ((WriteableDOMDataTreeShard) entry.getKey())
+                        .createProducer(entry.getValue());
+                // id mapped to producers
+                for (final DOMDataTreeIdentifier id : entry.getValue()) {
+                    idToProducerBuilder.put(id, producer);
+                }
+            } else {
+                LOG.error("Unable to create a producer for shard that's not a WriteableDOMDataTreeShard");
+            }
+        }
+
+        return idToProducerBuilder.build();
+    }
+
+    ProducerLayout addChild(final DOMDataTreeProducer producer, final Collection<DOMDataTreeIdentifier> subtrees) {
+        final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
+        cb.putAll(children);
+        for (final DOMDataTreeIdentifier s : subtrees) {
+            cb.put(s, producer);
+        }
+
+        return new ProducerLayout(shardMap, idToProducer, cb.build());
+    }
+
+    ProducerLayout reshard(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+        return new ProducerLayout(shardMap, mapIdsToProducer(shardMap), children);
+    }
+
+    boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
+        for (final DOMDataTreeIdentifier i : shardMap.keySet()) {
+            if (i.contains(subtree)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier path) {
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
+            if (e.getKey().contains(path)) {
+                // FIXME: does this match wildcards?
+                return e.getValue();
+            }
+        }
+
+        return null;
+    }
+
+    Set<DOMDataTreeIdentifier> getChildTrees() {
+        return children.keySet();
+    }
+
+    void checkAvailable(final Collection<PathArgument> base, final PathArgument child) {
+        if (!children.isEmpty()) {
+            final Collection<PathArgument> args = new ArrayList<>(base.size() + 1);
+            args.addAll(base);
+            args.add(child);
+
+            final YangInstanceIdentifier path = YangInstanceIdentifier.create(args);
+            for (final DOMDataTreeIdentifier c : children.keySet()) {
+                Preconditions.checkArgument(!c.getRootIdentifier().contains(path),
+                    "Path {%s} is not available to this cursor since it's already claimed by a child producer", path);
+            }
+        }
+    }
+
+    Map<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> createTransactions() {
+        return Maps.transformValues(idToProducer, DOMDataTreeShardProducer::createTransaction);
+    }
+}
index 6f30d207f8bc7a525648d14f991f3bc60f68be6a..4ef05c9e40a3a8162f92c8554088a4b7ffba9662 100644 (file)
@@ -183,7 +183,7 @@ public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTree
                 final ShardedDOMDataTreeProducer castedProducer = ((ShardedDOMDataTreeProducer) producer);
                 simpleLoopCheck(subtrees, castedProducer.getSubtrees());
                 // FIXME: We should also unbound listeners
-                castedProducer.boundToListener(listenerContext);
+                castedProducer.bindToListener(listenerContext);
             }
 
             for (final DOMDataTreeIdentifier subtree : subtrees) {
index 022215a6b2fc90645dfba4b232d90b203f9117d9..b7c2e58e523a653a1135bf5be87ceb9f36b44c16 100644 (file)
@@ -9,17 +9,9 @@ package org.opendaylight.mdsal.dom.broker;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableBiMap.Builder;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@@ -30,9 +22,6 @@ import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerBusyException;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerException;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
-import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
-import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,9 +31,6 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
     private final Set<DOMDataTreeIdentifier> subtrees;
     private final ShardedDOMDataTree dataTree;
 
-    private BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer = ImmutableBiMap.of();
-    private Map<DOMDataTreeIdentifier, DOMDataTreeShard> idToShard;
-
     private static final AtomicReferenceFieldUpdater<ShardedDOMDataTreeProducer, ShardedDOMDataTreeWriteTransaction>
         CURRENT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ShardedDOMDataTreeProducer.class,
             ShardedDOMDataTreeWriteTransaction.class, "currentTx");
@@ -64,56 +50,21 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
             AtomicIntegerFieldUpdater.newUpdater(ShardedDOMDataTreeProducer.class, "closed");
     private volatile int closed;
 
-    @GuardedBy("this")
-    private Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children = ImmutableMap.of();
-    @GuardedBy("this")
-    private Set<YangInstanceIdentifier> childRoots = ImmutableSet.of();
+    private volatile ShardedDOMDataTreeListenerContext<?> attachedListener;
+    private volatile ProducerLayout layout;
 
-    @GuardedBy("this")
-    private ShardedDOMDataTreeListenerContext<?> attachedListener;
-
-    ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree,
+    private ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree,
                                final Collection<DOMDataTreeIdentifier> subtrees,
-                               final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap,
-                               final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToId) {
+                               final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
         this.dataTree = Preconditions.checkNotNull(dataTree);
-        if (!shardToId.isEmpty()) {
-            this.idToProducer = mapIdsToProducer(shardToId);
-        }
-        idToShard = ImmutableMap.copyOf(shardMap);
         this.subtrees = ImmutableSet.copyOf(subtrees);
+        this.layout = ProducerLayout.create(shardMap);
     }
 
     static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree,
                                       final Collection<DOMDataTreeIdentifier> subtrees,
                                       final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
-        final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToIdentifiers = ArrayListMultimap.create();
-        // map which identifier belongs to which shard
-        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
-            shardToIdentifiers.put(entry.getValue(), entry.getKey());
-        }
-
-        return new ShardedDOMDataTreeProducer(dataTree, subtrees, shardMap, shardToIdentifiers);
-    }
-
-    private static BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> mapIdsToProducer(
-            final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToId) {
-        final Builder<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducerBuilder = ImmutableBiMap.builder();
-        for (final Entry<DOMDataTreeShard, Collection<DOMDataTreeIdentifier>> entry : shardToId.asMap().entrySet()) {
-            if (entry.getKey() instanceof WriteableDOMDataTreeShard) {
-                //create a single producer for all prefixes in a single shard
-                final DOMDataTreeShardProducer producer = ((WriteableDOMDataTreeShard) entry.getKey())
-                        .createProducer(entry.getValue());
-                // id mapped to producers
-                for (final DOMDataTreeIdentifier id : entry.getValue()) {
-                    idToProducerBuilder.put(id, producer);
-                }
-            } else {
-                LOG.error("Unable to create a producer for shard that's not a WriteableDOMDataTreeShard");
-            }
-        }
-
-        return idToProducerBuilder.build();
+        return new ShardedDOMDataTreeProducer(dataTree, subtrees, shardMap);
     }
 
     private void checkNotClosed() {
@@ -127,13 +78,7 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
     void subshardAdded(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
         checkIdle();
 
-        final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToIdentifiers = ArrayListMultimap.create();
-        // map which identifier belongs to which shard
-        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
-            shardToIdentifiers.put(entry.getValue(), entry.getKey());
-        }
-        this.idToProducer = mapIdsToProducer(shardToIdentifiers);
-        idToShard = ImmutableMap.copyOf(shardMap);
+        layout = layout.reshard(shardMap);
     }
 
     @Override
@@ -141,29 +86,14 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         checkNotClosed();
         checkIdle();
 
-        final ShardedDOMDataTreeWriteTransaction ret;
         LOG.debug("Creating transaction from producer {}", this);
 
         final ShardedDOMDataTreeWriteTransaction current = CURRENT_UPDATER.getAndSet(this, null);
+        final ShardedDOMDataTreeWriteTransaction ret;
         if (isolated) {
-            // Isolated case. If we have a previous transaction, submit it before returning this one.
-            synchronized (this) {
-                if (current != null) {
-                    submitTransaction(current);
-                }
-                ret = new ShardedDOMDataTreeWriteTransaction(this, idToProducer, childRoots);
-            }
+            ret = createIsolatedTransaction(layout, current);
         } else {
-            // Non-isolated case, see if we can reuse the transaction
-            if (current != null) {
-                LOG.debug("Reusing previous transaction {} since there is still a transaction inflight",
-                    current.getIdentifier());
-                ret = current;
-            } else {
-                synchronized (this) {
-                    ret = new ShardedDOMDataTreeWriteTransaction(this, idToProducer, childRoots);
-                }
-            }
+            ret = createReusedTransaction(layout, current);
         }
 
         final boolean success = OPEN_UPDATER.compareAndSet(this, null, ret);
@@ -171,32 +101,51 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         return ret;
     }
 
+    // This may look weird, but this has side-effects on local's producers, hence it needs to be properly synchronized
+    // so that it happens-after submitTransaction() which may have been stolen by a callback.
     @GuardedBy("this")
-    private void submitTransaction(final ShardedDOMDataTreeWriteTransaction current) {
-        lastTx = current;
-        current.doSubmit(this::transactionSuccessful, this::transactionFailed);
+    private ShardedDOMDataTreeWriteTransaction createTransaction(final ProducerLayout local) {
+        return new ShardedDOMDataTreeWriteTransaction(this, local.createTransactions(), local);
+
     }
 
-    @GuardedBy("this")
-    private boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
-        for (final DOMDataTreeIdentifier i : idToShard.keySet()) {
-            if (i.contains(subtree)) {
-                return true;
-            }
+    // Isolated case. If we have a previous transaction, submit it before returning this one.
+    private synchronized ShardedDOMDataTreeWriteTransaction createIsolatedTransaction(
+            final ProducerLayout local, final ShardedDOMDataTreeWriteTransaction current) {
+        if (current != null) {
+            submitTransaction(current);
         }
 
-        return false;
+        return createTransaction(local);
     }
 
-    @GuardedBy("this")
-    private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier domDataTreeIdentifier) {
-        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
-            if (e.getKey().contains(domDataTreeIdentifier)) {
-                return e.getValue();
+    private ShardedDOMDataTreeWriteTransaction createReusedTransaction(final ProducerLayout local,
+            final ShardedDOMDataTreeWriteTransaction current) {
+        if (current != null) {
+            // Lock-free fast path
+            if (local.equals(current.getLayout())) {
+                LOG.debug("Reusing previous transaction {} since there is still a transaction inflight",
+                    current.getIdentifier());
+                return current;
             }
+
+            synchronized (this) {
+                submitTransaction(current);
+                return createTransaction(local);
+            }
+        }
+
+        // Null indicates we have not seen a previous transaction -- which does not mean it is ready, as it may have
+        // been stolen and in is process of being submitted.
+        synchronized (this) {
+            return createTransaction(local);
         }
+    }
 
-        return null;
+    @GuardedBy("this")
+    private void submitTransaction(final ShardedDOMDataTreeWriteTransaction tx) {
+        lastTx = tx;
+        tx.doSubmit(this::transactionSuccessful, this::transactionFailed);
     }
 
     @Override
@@ -204,45 +153,36 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         checkNotClosed();
         checkIdle();
 
+        final ProducerLayout local = layout;
+
         for (final DOMDataTreeIdentifier s : subtrees) {
             // Check if the subtree was visible at any time
-            Preconditions.checkArgument(haveSubtree(s), "Subtree %s was never available in producer %s", s, this);
+            Preconditions.checkArgument(local.haveSubtree(s), "Subtree %s was never available in producer %s", s, this);
             // Check if the subtree has not been delegated to a child
-            final DOMDataTreeProducer child = lookupChild(s);
+            final DOMDataTreeProducer child = local.lookupChild(s);
             Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child);
 
             // Check if part of the requested subtree is not delegated to a child.
-            for (final DOMDataTreeIdentifier c : children.keySet()) {
+            for (final DOMDataTreeIdentifier c : local.getChildTrees()) {
                 Preconditions.checkArgument(!s.contains(c),
                     "Subtree %s cannot be delegated as it is a superset of already-delegated %s", s, c);
             }
         }
 
-        synchronized (this) {
-            final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees);
-            final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
-            cb.putAll(children);
-            for (final DOMDataTreeIdentifier s : subtrees) {
-                cb.put(s, ret);
-            }
 
-            children = cb.build();
-            childRoots = ImmutableSet.copyOf(Collections2.transform(children.keySet(),
-                DOMDataTreeIdentifier::getRootIdentifier));
-            return ret;
+        final DOMDataTreeProducer ret;
+        synchronized (this) {
+            ret = dataTree.createProducer(this, subtrees);
         }
+
+        layout = local.addChild(ret, subtrees);
+        return ret;
     }
 
     boolean isDelegatedToChild(final DOMDataTreeIdentifier path) {
-        for (final DOMDataTreeIdentifier c : children.keySet()) {
-            if (c.contains(path)) {
-                return true;
-            }
-        }
-        return false;
+        return layout.lookupChild(path) != null;
     }
 
-
     @Override
     public void close() throws DOMDataTreeProducerException {
         if (openTx != null) {
@@ -317,6 +257,7 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         LOG.debug("Transaction {} failed", tx.getIdentifier(), throwable);
 
         tx.onTransactionFailure(throwable);
+        // FIXME: transaction failure should result in a hard error
         transactionCompleted(tx);
     }
 
@@ -327,10 +268,15 @@ class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
         }
     }
 
-    synchronized void boundToListener(final ShardedDOMDataTreeListenerContext<?> listener) {
-        // FIXME: Add option to detach
-        Preconditions.checkState(this.attachedListener == null, "Producer %s is already attached to other listener.",
-                listener.getListener());
+    void bindToListener(final ShardedDOMDataTreeListenerContext<?> listener) {
+        Preconditions.checkNotNull(listener);
+
+        final ShardedDOMDataTreeListenerContext<?> local = attachedListener;
+        if (local != null) {
+            throw new IllegalStateException(String.format("Producer %s is already attached to listener %s", this,
+                local.getListener()));
+        }
+
         this.attachedListener = listener;
     }
 }
index fe938d87685ed8941746778d50bd7040b13ccedb..53feef2dc4aa6a87c69734eaba1ce011aca8acf4 100644 (file)
@@ -9,19 +9,16 @@ package org.opendaylight.mdsal.dom.broker;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
-import java.util.Collection;
+import java.util.ArrayDeque;
 import java.util.Deque;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
@@ -33,9 +30,7 @@ import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
-import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
@@ -48,9 +43,9 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
             TransactionCommitFailedExceptionMapper.create("submit");
     private static final AtomicLong COUNTER = new AtomicLong();
 
-    private final Map<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> idToTransaction;
-    private final Collection<YangInstanceIdentifier> childBoundaries;
+    private final Map<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> transactions;
     private final ShardedDOMDataTreeProducer producer;
+    private final ProducerLayout layout;
     private final String identifier;
 
     private final SettableFuture<Void> future = SettableFuture.create();
@@ -64,18 +59,16 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
     private DOMDataTreeWriteCursor openCursor;
 
     ShardedDOMDataTreeWriteTransaction(final ShardedDOMDataTreeProducer producer,
-                                       final Map<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer,
-                                       final Set<YangInstanceIdentifier> childRoots) {
+        final Map<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> transactions, final ProducerLayout layout) {
         this.producer = Preconditions.checkNotNull(producer);
+        this.transactions = ImmutableMap.copyOf(transactions);
+        this.layout = Preconditions.checkNotNull(layout);
         this.identifier = "SHARDED-DOM-" + COUNTER.getAndIncrement();
-        idToTransaction = ImmutableMap.copyOf(Maps.transformValues(idToProducer,
-            DOMDataTreeShardProducer::createTransaction));
-        childBoundaries = Preconditions.checkNotNull(childRoots);
         LOG.debug("Created new transaction {}", identifier);
     }
 
     private DOMDataTreeShardWriteTransaction lookup(final DOMDataTreeIdentifier prefix) {
-        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> e : idToTransaction.entrySet()) {
+        for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> e : transactions.entrySet()) {
             if (e.getKey().contains(prefix)) {
                 Preconditions.checkArgument(!producer.isDelegatedToChild(prefix),
                     "Path %s is delegated to child producer.", prefix);
@@ -101,7 +94,7 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
         if (openCursor != null) {
             openCursor.close();
         }
-        for (final DOMDataTreeShardWriteTransaction tx : idToTransaction.values()) {
+        for (final DOMDataTreeShardWriteTransaction tx : transactions.values()) {
             tx.close();
         }
 
@@ -135,7 +128,7 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
             final BiConsumer<ShardedDOMDataTreeWriteTransaction, Throwable> failure) {
 
         final ListenableFuture<List<Void>> listListenableFuture = Futures.allAsList(
-            idToTransaction.values().stream().map(tx -> {
+            transactions.values().stream().map(tx -> {
                 LOG.debug("Readying tx {}", identifier);
                 tx.ready();
                 return tx.submit();
@@ -167,10 +160,9 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
     }
 
     private class DelegatingCursor implements DOMDataTreeWriteCursor {
-
+        private final Deque<PathArgument> path = new ArrayDeque<>();
         private final DOMDataTreeWriteCursor delegate;
         private final DOMDataTreeIdentifier rootPosition;
-        private final Deque<PathArgument> path = new LinkedList<>();
 
         DelegatingCursor(final DOMDataTreeWriteCursor delegate, final DOMDataTreeIdentifier rootPosition) {
             this.delegate = Preconditions.checkNotNull(delegate);
@@ -244,16 +236,11 @@ final class ShardedDOMDataTreeWriteTransaction implements DOMDataTreeCursorAware
         }
 
         void checkAvailable(final PathArgument child) {
-            path.add(child);
-            final YangInstanceIdentifier yid = YangInstanceIdentifier.create(path);
-            childBoundaries.forEach(id -> {
-                if (id.contains(yid)) {
-                    path.removeLast();
-                    throw new IllegalArgumentException("Path {" + yid + "} is not available to this cursor"
-                            + " since it's already claimed by a child producer");
-                }
-            });
-            path.removeLast();
+            layout.checkAvailable(path, child);
         }
     }
+
+    ProducerLayout getLayout() {
+        return layout;
+    }
 }