最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Seata AT模式TransactionHook被刪除探究

 更新時間:2022年11月15日 10:25:06   作者:夢想實現(xiàn)家_Z  
這篇文章主要為大家介紹了Seata AT模式TransactionHook被刪除探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

兄弟們,剛剛又給seata社區(qū)修了一個BUG,有用戶提了issue反應TransactionHook在某些情況下不會被調(diào)用:

相關(guān)issue鏈接:github.com/seata/seata…,該用戶在issue中已經(jīng)指出了相關(guān)問題所在:

下面我們來看一下到底是什么原因?qū)е铝松鲜?code>BUG的產(chǎn)生。

問題定位

根據(jù)用戶的反饋,我們找到目標源碼io.seata.tm.api.TransactionalTemplate#execute()

try {
    // 開啟分布式事務(wù),獲取XID         
    beginTransaction(txInfo, tx);
    Object rs;
    try {
        // 執(zhí)行業(yè)務(wù)代碼
        rs = business.execute();
    } catch (Throwable ex) {
        // 3. 處理異常,準備回滾.
        completeTransactionAfterThrowing(txInfo, tx, ex);
        throw ex;
    }
    // 4. 提交事務(wù).
    commitTransaction(tx, txInfo);
    return rs;
} finally {
    //5. 回收現(xiàn)場
    resumeGlobalLockConfig(previousConfig);
    triggerAfterCompletion();
    cleanUp();
}

問題代碼就出在cleanUp()中,我們來看一下里面做了什么操作,最終我們定位到:

public final class TransactionHookManager {
  private static final ThreadLocal<List<TransactionHook>> LOCAL_HOOKS = new ThreadLocal<>();
  // 注冊TransactionHook
  public static void registerHook(TransactionHook transactionHook) {
      if (transactionHook == null) {
            throw new NullPointerException("transactionHook must not be null");
        }
        List<TransactionHook> transactionHooks = LOCAL_HOOKS.get();
        if (transactionHooks == null) {
            LOCAL_HOOKS.set(new ArrayList<>());
        }
        LOCAL_HOOKS.get().add(transactionHook);
    }
  // 移除當前線程上所有TransactionHook
  public static void clear() {
      LOCAL_HOOKS.remove();
  }
}

由上面的源碼可知,cleanUp()操作時把當前線程中的所有TransactionHook都清除掉了。也就是說,假如事務(wù)A和事務(wù)B共用同一個線程,當事務(wù)B處理完畢后,調(diào)用了cleanUp()回收現(xiàn)場時,把該線程當中存儲的所有TransactionHook全部清除掉了,導致事務(wù)A的生命周期中找不到該事務(wù)對應的TransactionHook,從而產(chǎn)生了BUG。

如何解決

通過與seata社區(qū)的大佬不斷地溝通,最終敲定以下方案:

1.改造TransactionHookManager.LOCAL_HOOKS,把數(shù)據(jù)類型改成ThreadLocal<Map<String, List<TransactionHook>>>Map中的key對應分布式事務(wù)XID;

2.針對當前上下文中沒有XID,那么key就為null,因為HashMap允許keynull;

3.當用戶查詢指定XID下的hook時,連同keynull對應的hook也一起返回;

  • 第一步比較好理解,因為事務(wù)A和事務(wù)B對應的TransactionHook沒有被區(qū)分出來,所以造成了清理事務(wù)B的TransactionHook時連同事務(wù)A的TransactionHook一起被清除,那么我們修改數(shù)據(jù)結(jié)構(gòu)來區(qū)分事務(wù)A和事務(wù)B的TransactionHook,以便清理的時候不會造成誤刪;

第二步為什么要針對沒有XID的時候也要能設(shè)置TransactionHook,因為有這么一段代碼:

    private void beginTransaction(TransactionInfo txInfo, GlobalTransaction tx) throws TransactionalExecutor.ExecutionException {
        try {
            // 執(zhí)行triggerBeforeBegin()
            triggerBeforeBegin();
            // 注冊分布式事務(wù),生成XID
            tx.begin(txInfo.getTimeOut(), txInfo.getName());
            // 執(zhí)行triggerAfterBegin()
            triggerAfterBegin();
        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.BeginFailure);
        }
    }

上面的代碼會產(chǎn)生一個問題,因為我們的TransactionHook依賴于XID,但是triggerBeforeBegin()執(zhí)行的時候還沒有產(chǎn)生XID,所以為了能夠在沒有XID的時候也能夠讓TransactionHook生效,我們要有一個虛值key來臨時設(shè)置TransactionHook;

第三步的設(shè)計時為了在第二步的基礎(chǔ)上,當事務(wù)開啟后獲取XID后,要保證XID獲取前注冊的TransactionHook也要生效,我們在通過XID查詢TransactionHook時要把虛值key對應的TransactionHook也一起返回;

注意事項

在實際代碼修改中,發(fā)現(xiàn)triggerAfterCommit()、triggerAfterRollback()triggerAfterCompletion()在被調(diào)用時始終拿不到對應的TransactionHook,最終debug下來發(fā)現(xiàn)在調(diào)用這三個方法前,上下文中的XID被解綁了,導致拿到的XID為空。代碼類似下面這樣:

try {
            // 調(diào)用triggerBeforeCommit()
            triggerBeforeCommit();
            // 提交事務(wù),清除XID
            tx.commit();
            if (Arrays.asList(GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbacked).contains(tx.getLocalStatus())) {
                throw new TransactionalExecutor.ExecutionException(tx,
                        new TimeoutException(String.format("Global transaction[%s] is timeout and will be rollback[TC].", tx.getXid())),
                        TransactionalExecutor.Code.TimeoutRollback);
            }
            // 調(diào)用triggerAfterCommit()
            triggerAfterCommit();
        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.CommitFailure);
        }

不過經(jīng)過我的一番查找,發(fā)現(xiàn)GlobalTransaction中是包含XID屬性的,所以果斷從GlobalTransaction對象中取XID傳進來。

修改后的代碼如下:

try {
            // 調(diào)用triggerBeforeCommit()
            triggerBeforeCommit();
            // 提交事務(wù),清除XID
            tx.commit();
            if (Arrays.asList(GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbacked).contains(tx.getLocalStatus())) {
                throw new TransactionalExecutor.ExecutionException(tx,
                        new TimeoutException(String.format("Global transaction[%s] is timeout and will be rollback[TC].", tx.getXid())),
                        TransactionalExecutor.Code.TimeoutRollback);
            }
            // 調(diào)用triggerAfterCommit()
            triggerAfterCommit(tx.getXid());
        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.CommitFailure);
        }

改造后的TransactionHookManager

public final class TransactionHookManager {
    private TransactionHookManager() {
    }
    private static final ThreadLocal<Map<String, List<TransactionHook>>> LOCAL_HOOKS = new ThreadLocal<>();
    /**
     * get the current hooks
     *
     * @return TransactionHook list
     */
    public static List<TransactionHook> getHooks() {
        String xid = RootContext.getXID();
        return getHooks(xid);
    }
    /**
     * get hooks by xid
     * 
     * @param xid
     * @return TransactionHook list
     */
    public static List<TransactionHook> getHooks(String xid) {
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null || hooksMap.isEmpty()) {
            return Collections.emptyList();
        }
        List<TransactionHook> hooks = new ArrayList<>();
        List<TransactionHook> localHooks = hooksMap.get(xid);
        if (StringUtils.isNotBlank(xid)) {
            List<TransactionHook> virtualHooks = hooksMap.get(null);
            if (virtualHooks != null && !virtualHooks.isEmpty()) {
                hooks.addAll(virtualHooks);
            }
        }
        if (localHooks != null && !localHooks.isEmpty()) {
            hooks.addAll(localHooks);
        }
        if (hooks.isEmpty()) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(hooks);
    }
    /**
     * add new hook
     *
     * @param transactionHook transactionHook
     */
    public static void registerHook(TransactionHook transactionHook) {
        if (transactionHook == null) {
            throw new NullPointerException("transactionHook must not be null");
        }
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null) {
            hooksMap = new HashMap<>();
            LOCAL_HOOKS.set(hooksMap);
        }
        String xid = RootContext.getXID();
        List<TransactionHook> hooks = hooksMap.get(xid);
        if (hooks == null) {
            hooks = new ArrayList<>();
            hooksMap.put(xid, hooks);
        }
        hooks.add(transactionHook);
    }
    /**
     * clear hooks by xid
     * 
     * @param xid
     */
    public static void clear(String xid) {
        Map<String, List<TransactionHook>> hooksMap = LOCAL_HOOKS.get();
        if (hooksMap == null || hooksMap.isEmpty()) {
            return;
        }
        hooksMap.remove(xid);
        if (StringUtils.isNotBlank(xid)) {
            hooksMap.remove(null);
        }
    }
}

以上就是Seata AT模式TransactionHook被刪除探究的詳細內(nèi)容,更多關(guān)于Seata AT刪除TransactionHook的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

昂仁县| 富裕县| 台湾省| 维西| 怀远县| 科技| 三穗县| 讷河市| 湟中县| 通海县| 阿拉善盟| 布拖县| 通化市| 仲巴县| 舒兰市| 汕尾市| 宁武县| 平原县| 什邡市| 平山县| 昌邑市| 白朗县| 武冈市| 曲周县| 吉木乃县| 博兴县| 罗田县| 磐安县| 南靖县| 连州市| 汉中市| 富民县| 定结县| 马鞍山市| 惠水县| 伊春市| 金昌市| 尚志市| 慈溪市| 年辖:市辖区| 霍邱县|