opendaylight源码分析之InMemoryDataTre

2021-04-24  本文已影响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));
    }
上一篇 下一篇

猜你喜欢

热点阅读