美文网首页
opendaylight源码分析之InMemoryDataTre

opendaylight源码分析之InMemoryDataTre

作者: 生饼 | 来源:发表于2021-04-24 17:15 被阅读0次

    1 基本步骤

    
        // 创建实例
        DataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.OPERATIONAL);
        dataTree.setSchemaContext(schemaContext);
    
        // 获取一个snapshot
        DataTreeSnapshot snapshot = dataTree.takeSnapshot();
    
        // 修改snapshot
        DataTreeModification modification = snapshot.newModification();
        YangInstanceIdentifier yiid = YangInstanceIdentifier.of(TEST1_OUTER_CONTAINER1_QNAME);
        ContainerNode outerContainer1 = buildOuterContainer1();
        modification.write(yiid, outerContainer1);
        modification.ready();
    
        // 验证
        dataTree.validate(modification);
    
        // 生成一个新的DataTree
        DataTreeCandidate candidate = dataTree.prepare(modification);
    
        // 写入DataTree
        dataTree.commit(candidate);
    
    

    2 创建DataTree实例

    InMemoryDataTree包含的字段:

    InMemoryDataTree {
       private final DataTreeConfiguration treeConfig;
        private final boolean maskMandatory;
        private volatile DataTreeState state;
    }
    
    final class DataTreeState {
        private final LatestOperationHolder holder;
        private final SchemaContext schemaContext;
        private final TreeNode root;
    }
    

    TreeNode的几个实现类:

    MaterializedContainerNode
    ValueNode
    LazyContainerNode
    SimpleContainerNode
    

    TreeNode包含关键信息:

    NormalizedNode<?, ?> data;
    Version version;
    

    创建InMemoryDataTree时,如果没有指定YangInstanceIdentifier,则treeconfig中的YangInstanceIdentifier值为YangInstanceIdentifier.EMPTY;TreeNode的NormalizedNode的PathArgument的QName值为

    QName NAME = QName.create(URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"), null, "data")
    

    data值为null,TreeNode是SimpleContainerNode

    如果指定YangInstanceIdentifier,treeconfig中的YangInstanceIdentifier值为YangInstanceIdentifier,NormalizedNode值为:

        final PathArgument arg = path.getLastPathArgument();
        if (arg instanceof NodeIdentifier) {
            return ImmutableContainerNodeBuilder.create().withNodeIdentifier((NodeIdentifier) arg).build();
        }
        if (arg instanceof NodeIdentifierWithPredicates) {
            return ImmutableNodes.mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) arg).build();
        }
    

    TreeNode是SimpleContainerNode

    DataSchemaContextNode包含了schema node的PathArgumentDataSchemaNodeDataSchemaContextNode的实例:

    ContainerContextNode
    
    UnkeyedListMixinContextNode
    OrderedMapMixinContextNode
    UnorderedMapMixinContextNode
    
    LeafContextNode
    
    OrderedLeafListMixinContextNode
    UnorderedLeafListMixinContextNode
    
    

    3 snapshot

    InMemoryDataTreeSnapshot {
        // 值为 UpgradableModificationApplyOperation
        private final RootModificationApplyOperation applyOper;
        private final SchemaContext schemaContext;
        private final TreeNode rootNode;    
    }
    
    

    3 修改datatree

    基本思路为:
    使用InMemoryDataTreeSnapshot产生一个InMemoryDataTreeModification对象,同时产生一个新的Version,代表这个修改。
    从根节点开始到修改的节点,每个节点创建一个ModifiedNode,它保存了节点的PathArgument、原始的TreeNode,修改的孩子等。write的节点标记的LogicOperation.WRITE,所有的父节点标记为LogicOperation.TOUCH

    class InMemoryDataTreeModification {
        // UpgradableModificationApplyOperation, 不同的节点有不通的数据修改方式,strategyTree可以遍历到语法树的所有节点的修改方法
        // 根节点对应的 Strategy,为 ContainerModificationStrategy
        private final RootModificationApplyOperation strategyTree;
        private final InMemoryDataTreeSnapshot snapshot;
        private final ModifiedNode rootNode;
        // 本次修改对应的新版本号
        private final Version version;        
       
        public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
            // 当前modification还没有关闭
            checkSealed();
            // data 所表示节点的identifirer 与 path的identifier需要一致
            checkIdentifierReferencesData(path, data);
            // 创建modifiedNode
            resolveModificationFor(path).write(data);
        }        
        
        /*
         * 从根节点开始,直到被修改节点,创建modified node
         */
        private OperationWithModification resolveModificationFor(final YangInstanceIdentifier path) {
            upgradeIfPossible();
    
            /*
             * Walk the strategy and modification trees in-sync, creating modification nodes as needed.
             *
             * If the user has provided wrong input, we may end up with a bunch of TOUCH nodes present
             * ending with an empty one, as we will throw the exception below. This fact could end up
             * being a problem, as we'd have bunch of phantom operations.
             *
             * That is fine, as we will prune any empty TOUCH nodes in the last phase of the ready
             * process.
             */
            ModificationApplyOperation operation = strategyTree;
            ModifiedNode modification = rootNode;
    
            int i = 1;
            for (final PathArgument pathArg : path.getPathArguments()) {
                // 孩子 schema node对应的strategy
                final Optional<ModificationApplyOperation> potential = operation.getChild(pathArg);
                if (!potential.isPresent()) {
                    throw new SchemaValidationFailedException(String.format("Child %s is not present in schema tree.",
                            path.getAncestor(i)));
                }
                operation = potential.get();
                ++i;
    
                // 孩子节点对应的modified node
                modification = modification.modifyChild(pathArg, operation, version);
            }
    
            return OperationWithModification.from(operation, modification);
        }
    
        
    }
    
    
    class ModifiedNode {
        // 根据 节点的 child policy确定实例是hashmap,还是linked hashmap
        private final Map<PathArgument, ModifiedNode> children;
        // 修改前的TreeNode
        private final Optional<TreeNode> original;
        // 节点的identifier
        private final PathArgument identifier;
        private LogicalOperation operation = LogicalOperation.NONE;
    
        // 修改后的 tree node
        private Optional<TreeNode> snapshotCache;
    
        // 修改后的值
        private NormalizedNode<?, ?> value;
        // 修改后标记的修改类型
        private ModificationType modType;
    }
    
    ModifiedNode modifyChild(@Nonnull final PathArgument child, @Nonnull final ModificationApplyOperation childOper,
            @Nonnull final Version modVersion) {
        clearSnapshot();
        if (operation == LogicalOperation.NONE) {
            updateOperationType(LogicalOperation.TOUCH);
        }
    
        // 这个节点是否已经创建了 ModifiedNode
        final ModifiedNode potential = children.get(child);
        if (potential != null) {
            return potential;
        }
    
        // 为孩子节点创建treenode
        final Optional<TreeNode> currentMetadata = findOriginalMetadata(child, modVersion);
    
    
        final ModifiedNode newlyCreated = new ModifiedNode(child, currentMetadata, childOper.getChildPolicy());
        if (operation == LogicalOperation.MERGE && value != null) {
            /*
             * We are attempting to modify a previously-unmodified part of a MERGE node. If the
             * value contains this component, we need to materialize it as a MERGE modification.
             */
            @SuppressWarnings({ "rawtypes", "unchecked" })
            final Optional<NormalizedNode<?, ?>> childData = ((NormalizedNodeContainer)value).getChild(child);
            if (childData.isPresent()) {
                childOper.mergeIntoModifiedNode(newlyCreated, childData.get(), modVersion);
            }
        }
    
        children.put(child, newlyCreated);
        return newlyCreated;
    }
    
    
    

    ModificationApplyOperation代表了修改datatree节点的修改策略,常用节点的策略有:

    UpgradableModificationApplyOperation extends RootModificationApplyOperation
    
    ContainerModificationStrategy
    
    ListEntryModificationStrategy
    OrderedMapModificationStrategy
    UnkeyedListItemModificationStrategy
    UnkeyedListModificationStrategy
    UnorderedMapModificationStrategy
    
    LeafModificationStrategy
    
    LeafSetEntryModificationStrategy
    OrderedLeafSetModificationStrategy
    UnorderedLeafSetModificationStrategy
    
    
        /*
         * 根据孩子节点的schemanode类型,确定相应的modification strategy
         */
        public static ModificationApplyOperation from(final DataSchemaNode schemaNode, final DataTreeConfiguration treeConfig) {
            if (treeConfig.getTreeType() == TreeType.CONFIGURATION) {
                Preconditions.checkArgument(schemaNode.isConfiguration(), "Supplied %s does not belongs to configuration tree.", schemaNode.getPath());
            }
            if (schemaNode instanceof ContainerSchemaNode) {
                final ContainerSchemaNode containerSchema = (ContainerSchemaNode) schemaNode;
                if (containerSchema.isPresenceContainer()) {
                    return new PresenceContainerModificationStrategy(containerSchema, treeConfig);
                } else {
                    return new StructuralContainerModificationStrategy(containerSchema, treeConfig);
                }
            } else if (schemaNode instanceof ListSchemaNode) {
                return fromListSchemaNode((ListSchemaNode) schemaNode, treeConfig);
            } else if (schemaNode instanceof ChoiceSchemaNode) {
                return new ChoiceModificationStrategy((ChoiceSchemaNode) schemaNode, treeConfig);
            } else if (schemaNode instanceof LeafListSchemaNode) {
                return fromLeafListSchemaNode((LeafListSchemaNode) schemaNode, treeConfig);
            } else if (schemaNode instanceof LeafSchemaNode) {
                return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
            }
            throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
        }
    

    4 validate

    逐级检查节点,snapshot中的节点与当前datatree中的节点需要一致,没有改变:

    1)节点要不都存在,要不都不存在

    对于写入的节点还要检查:

    1)如果存在,version一致

    2)如果存在,subversion一致

    入口方法为AbstractDataTreeTip.validate(), 这个方法会从根节点开始逐级调用节点对应的ModificationApplyOperationcheckApplicable()方法

    5 prepare

    以当前Datatree内容为基础,根据data tree snapshot修改后的内容,生成替换当前Datatree的candidate Datatree。不用snapshot修改后的dataree替换当前datatree的值,是因为这样可以支持不同子树的同时修改。candidate Datatree包含了当前datatree,修改后的待替换的datatree信息。

    TreeNode的实现类

    LazyContainerNode
    MaterializedContainerNode
    SimpleContainerNode
    

    上述TreeNode实现类相应的mutable类:

    LazyMutableContainerNode
    MaterializedMutableContainerNode
    

    入口方法为:AbstractDataTreeTip.prepare(),这个方法会从根节点开始深度优先遍历,调用相关节点的apply()applyTouch()applyWrite()等方法。

        /*
         * 创建 candidate datatree
         */
        @Override
        final Optional<TreeNode> apply(final ModifiedNode modification, final Optional<TreeNode> currentMeta, final Version version) {
            switch (modification.getOperation()) {
            case DELETE:
                LOG.info("++++++++++++apply DELETE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
                // Deletion of a non-existing node is a no-op, report it as such
                modification.resolveModificationType(currentMeta.isPresent() ? ModificationType.DELETE : ModificationType.UNMODIFIED);
                return modification.setSnapshot(Optional.absent());
            case TOUCH:
                LOG.info("++++++++++++apply TOUCH: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
                Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification %s",
                        modification);
    
                return modification.setSnapshot(Optional.of(applyTouch(modification, currentMeta.get(), version)));
    
            case MERGE:
                LOG.info("++++++++++++apply MERGE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
                final TreeNode result;
    
                if (!currentMeta.isPresent()) {
                    // This is a slight optimization: a merge on a non-existing node equals to a write. Written data
                    // structure is usually verified when the transaction is sealed. To preserve correctness, we have
                    // to run that validation here.
                    modification.resolveModificationType(ModificationType.WRITE);
                    result = applyWrite(modification, currentMeta, version);
                    verifyStructure(result.getData(), true);
                } else {
                    result = applyMerge(modification, currentMeta.get(), version);
                }
    
                return modification.setSnapshot(Optional.of(result));
            case WRITE:
                LOG.info("++++++++++++apply WRITE: {}, class name: {}", modification.getIdentifier(), getClass().getSimpleName());
                modification.resolveModificationType(ModificationType.WRITE);
                return modification.setSnapshot(Optional.of(applyWrite(modification, currentMeta, version)));
            case NONE:
                LOG.info("++++++++++++apply NONE: {}, class name: {}", modification.getIdentifier());
                modification.resolveModificationType(ModificationType.UNMODIFIED);
                return currentMeta;
            default:
                throw new IllegalArgumentException("Provided modification type is not supported.");
            }
        }
        
        protected TreeNode applyTouch(final ModifiedNode modification, final TreeNode currentMeta, final Version version) {
            /*
             * The user may have issued an empty merge operation. In this case we do not perform
             * a data tree mutation, do not pass GO, and do not collect useless garbage. It
             * also means the ModificationType is UNMODIFIED.
             */
            final Collection<ModifiedNode> children = modification.getChildren();
            if (!children.isEmpty()) {
                // builder 初始化为 当前data tree的 normalized node值
                @SuppressWarnings("rawtypes")
                final NormalizedNodeContainerBuilder dataBuilder = createBuilder(currentMeta.getData());
                // 当前 data tree变为 可变
                final MutableTreeNode newMeta = currentMeta.mutable();
    
                // 设置节点的subversion为修改后的version
                newMeta.setSubtreeVersion(version);
                final TreeNode ret = mutateChildren(newMeta, dataBuilder, version, children);
    
                /*
                 * It is possible that the only modifications under this node were empty merges,
                 * which were turned into UNMODIFIED. If that is the case, we can turn this operation
                 * into UNMODIFIED, too, potentially cascading it up to root. This has the benefit
                 * of speeding up any users, who can skip processing child nodes.
                 *
                 * In order to do that, though, we have to check all child operations are UNMODIFIED.
                 * Let's do precisely that, stopping as soon we find a different result.
                 */
                for (final ModifiedNode child : children) {
                    if (child.getModificationType() != ModificationType.UNMODIFIED) {
                        // 设置修改类型
                        modification.resolveModificationType(ModificationType.SUBTREE_MODIFIED);
                        return ret;
                    }
                }
            }
    
            // The merge operation did not have any children, or all of them turned out to be UNMODIFIED, hence do not
            // replace the metadata node.
            modification.resolveModificationType(ModificationType.UNMODIFIED);
            return currentMeta;
        }
    
        protected TreeNode applyWrite(final ModifiedNode modification,
                final Optional<TreeNode> currentMeta, final Version version) {
            final NormalizedNode<?, ?> newValue = modification.getWrittenValue();
            final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version);
    
            // 整个节点都是修改后的节点
            if (modification.getChildren().isEmpty()) {
                return newValueMeta;
            }
    
    
            // 节点修改后,又修改了它的子节点
    
            /*
             * This is where things get interesting. The user has performed a write and
             * then she applied some more modifications to it. So we need to make sense
             * of that an apply the operations on top of the written value. We could have
             * done it during the write, but this operation is potentially expensive, so
             * we have left it out of the fast path.
             *
             * As it turns out, once we materialize the written data, we can share the
             * code path with the subtree change. So let's create an unsealed TreeNode
             * and run the common parts on it -- which end with the node being sealed.
             *
             * FIXME: this code needs to be moved out from the prepare() path and into
             *        the read() and seal() paths. Merging of writes needs to be charged
             *        to the code which originated this, not to the code which is
             *        attempting to make it visible.
             */
            final MutableTreeNode mutable = newValueMeta.mutable();
            mutable.setSubtreeVersion(version);
    
            @SuppressWarnings("rawtypes")
            final NormalizedNodeContainerBuilder dataBuilder = createBuilder(newValue);
            final TreeNode result = mutateChildren(mutable, dataBuilder, version, modification.getChildren());
    
            // We are good to go except one detail: this is a single logical write, but
            // we have a result TreeNode which has been forced to materialized, e.g. it
            // is larger than it needs to be. Create a new TreeNode to host the data.
            return TreeNodeFactory.createTreeNode(result.getData(), version);
        }
    
        
    

    6 commit

    用candidate data tree替换当前的data tree,会进一步通过比较地址的方式校验当前datatree是不是candidate中的old data tree,也就是要求在生成candidate data tree后,当前data tree不能发生了变化。

    InMemoryDataTree.commit()方法实现了具体的实现逻辑:

        public void commit(final DataTreeCandidate candidate) {
            if (candidate instanceof NoopDataTreeCandidate) {
                return;
            }
            Preconditions.checkArgument(candidate instanceof InMemoryDataTreeCandidate, "Invalid candidate class %s", candidate.getClass());
            final InMemoryDataTreeCandidate c = (InMemoryDataTreeCandidate)candidate;
    
            if (LOG.isTraceEnabled()) {
                LOG.trace("Data Tree is {}", NormalizedNodes.toStringTree(c.getTipRoot().getData()));
            }
    
            final TreeNode newRoot = c.getTipRoot();
            DataTreeState currentState, newState;
            do {
                currentState = state;
                // 执行 commit()函数时 datatree 的 tree node
                final TreeNode currentRoot = currentState.getRoot();
                LOG.debug("Updating datastore from {} to {}", currentRoot, newRoot);
    
                // 执行 prepare时 datatree 的tree node
                final TreeNode oldRoot = c.getBeforeRoot();
                LOG.info("======currentRoot: {}, oldRoot: {}, newRoot: {}",
                        System.identityHashCode(currentRoot),
                        System.identityHashCode(oldRoot),
                        System.identityHashCode(newRoot));
                if (oldRoot != currentRoot) {
                    final String oldStr = simpleToString(oldRoot);
                    final String currentStr = simpleToString(currentRoot);
                    throw new IllegalStateException("Store tree " + currentStr + " and candidate base " + oldStr + " differ.");
                }
    
                newState = currentState.withRoot(newRoot);
                LOG.trace("Updated state from {} to {}", currentState, newState);
            } while (!STATE_UPDATER.compareAndSet(this, currentState, newState));
        }
    

    相关文章

      网友评论

          本文标题:opendaylight源码分析之InMemoryDataTre

          本文链接:https://www.haomeiwen.com/subject/ffjlrltx.html