美文网首页程序员Ovirt
【Ovirt 笔记】事务相关实现分析与整理

【Ovirt 笔记】事务相关实现分析与整理

作者: 58bc06151329 | 来源:发表于2018-11-30 17:49 被阅读2次

    文前说明

    作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。

    本文仅供学习交流使用,侵权必删。
    不用于商业目的,转载请注明出处。

    分析整理的版本为 Ovirt 4.2.3 版本。

    1. Ovirt 中的事务管理

    • engine 中的 事务管理 是交由 JBoss 容器来完成的,由 JBoss 容器基于 JTA 实现。
      • JNDI 命名目录为 java:jboss/TransactionManager
      • TransactionManagerProducer 类中通过 Java CDITransactionManager 的实现类注入到程序中。
    @Produces
    @Singleton
    public TransactionManager getTransactionManager() throws NamingException {
        return (TransactionManager) new InitialContext().lookup("java:jboss/TransactionManager");
    }
    

    1.1 事务传播行为

    • engine 中只定义了三种传播行为,在 TransactionScopeOption 枚举类中进行了定义。
    传播行为 说明
    Required 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
    Suppress 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
    RequiresNew 创建新事务,无论当前存不存在事务,都创建新事务。

    1.2 事务的支持

    • engine 中定义了 TransactionSupport 工具类,用于事务的支持。
    静态方法 说明
    findTransactionManager 获取事务管理器(单例对象,存在于 CDI 中)。
    suspend 挂起当前线程关联的事务。
    resume 继续当前线程关联的事务。
    current 返回关联到当前线程的事务。
    registerRollbackHandler 回调接口,以便在事务完成时得到通知从而触发一些处理工作,如清除缓存等。可以通过此接口将回调程序注入到事务中,当事务成功提交后,回调程序将被激活。
    needToRollback 根据事务状态判断是否需要回滚。
    executeInScope 执行事务(根据不同的传播行为)。
    executeInNewTransaction 执行传播行为为 RequiresNew 的事务。
    setRollbackOnly 标记当前事务将回滚。

    1.3 执行事务

    执行事务流程
    • 其中深蓝色箭头连线表示公共执行。
    • 橘黄色箭头连线表示 RequiresNew 执行。
    • 绿色箭头连线表示 Required 执行。
    • 浅蓝色箭头连线表示 Suppress 执行。
    public static <T> T executeInScope(TransactionScopeOption scope, TransactionMethod<T> code) {
            // check if we are already in rollback
            TransactionManager tm;
            try {
                tm = findTransactionManager();
                if (needToRollback(tm.getStatus())) {
                    throw new TransactionRolledbackLocalException(
                            "Current transaction is marked for rollback, no further operations are possible or desired");
                }
            } catch (SystemException e) {
                throw new RuntimeException("Failed to check transaction status - this shouldn't ever happen");
            }
            switch (scope) {
            case RequiresNew:
                return executeInNewTransaction(code);
            case Suppress:
                return executeInSuppressed(tm, code);
            case Required:
                return executeInRequired(tm, code);
            default:
                throw new RuntimeException("Undefined Scope: " + scope);
            }
    }
    

    1.3.1 excute 方法

    protected final void execute() {
            setCommandStatus(CommandStatus.ACTIVE);
            getReturnValue().setValid(true);
            getReturnValue().setIsSynchronous(true);
    
            if (shouldPersistCommand()) {
                persistCommandIfNeeded();
                commandCoordinatorUtil.persistCommandAssociatedEntities(getCommandId(), getSubjectEntities());
            }
    
            executionHandler.addStep(getExecutionContext(), StepEnum.EXECUTING, null);
    
            handleCommandStepAndEntities();
    
            try {
                handleTransactivity();
                TransactionSupport.executeInScope(scope, this);
            } catch (TransactionRolledbackLocalException e) {
                log.info("Transaction was aborted in '{}'", this.getClass().getName());
                // Transaction was aborted - we must sure we compensation for all previous applicative stages of the command
                compensate();
            } finally {
                try {
                    if (getCommandShouldBeLogged()) {
                        logCommand();
                    }
                    if (getSucceeded()) {
                        if (getCommandShouldBeLogged()) {
                            logRenamedEntity();
                        }
                        // only after creating all tasks, we can start polling them (we
                        // don't want
                        // to start polling before all tasks were created, otherwise we
                        // might change
                        // the VM/VmTemplate status to 'Down'/'OK' too soon.
                        startPollingAsyncTasks();
                    }
                } finally {
                    if (noAsyncOperations() && !executionHandler.checkIfJobHasTasks(getExecutionContext())) {
                        executionHandler.endJob(getExecutionContext(), getSucceeded());
                    }
                }
            }
    }
    
    • handleTransactivity 通过该方法来设置传播行为。
    步骤 说明
    第一步 通过 Command 的参数设置该传播行为。
    第二步 根据 Command 是否设置了 @NonTransactiveCommandAttribute,决定是否采用 Suppress 传播行为。
    第三步 设置了 @NonTransactiveCommandAttribute,同时又设置了 forceCompensation 属性为 true,最终执行第一步的传播行为。
    • executeInScope 执行过程抛出异常,则会执行补偿机制
      • 可以通过 Command 参数中的 shouldbelogged 属性设置是否在执行完成 excuete 后打印日志。
      • 也可以在 Command 过程中通过 setCommandShouldBeLogged 方法设置。
      • excuete 执行成功后,开始跟轮询踪异步任务。

    1.3.2 endAction 方法

    • handleCommandExecutionEnded 判断是否需要执行 endAction。
      • 所有的 Command 命令默认执行 endAction。
      • 但是如果该 Command 中包含父 Command 参数,则判断该 Command 的结束程序的属性(Command 参数中可以设置(默认为 PARENT_MANAGED 不自动执行 endAction))是否为 FLOW_MANAGED 或 COMMAND_MANAGED,如果是则执行,否则不执行。
    private boolean handleCommandExecutionEnded() {
            boolean shouldEndAction = parentHasCallback() ? isEndProcedureApplicableToEndAction() : true;
    
            CommandStatus newStatus = isEndSuccessfully() ? CommandStatus.SUCCEEDED : CommandStatus.FAILED;
            if (getCallback() == null) {
                setCommandStatus(newStatus);
    
                if (!shouldEndAction) {
                    logEndWillBeExecutedByParent(newStatus);
                }
            }
    
            return shouldEndAction;
    }
    
    • endAction 与 execute 有类似的流程,同样需要设置传播行为和是否打印日志。

    相关文章

      网友评论

        本文标题:【Ovirt 笔记】事务相关实现分析与整理

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