You need to enable JavaScript to run this app. Node.js环境下双远端API同步更新、失败回滚及多次重试实现问题
优惠活动
大模型
产品
解决方案
定价
更多

Node.js环境下双远端API同步更新、失败回滚及多次重试实现问题

实现方案

采用补偿事务(Saga模式)实现跨API的最终一致性,核心逻辑是每一步可回滚操作都对应记录反向操作,任意步骤失败时依次执行反向操作抵消已完成的修改。

现有代码存在的问题

  • 未等待两个远程接口拉取完成就执行更新,可能操作未定义的变量
  • 更新逻辑将两个API的修改打包,无法区分单个API的更新状态
  • 缺少回滚逻辑,部分更新成功后无法撤销已提交的修改
  • 重试逻辑作用于整个更新批次,重复执行可能导致数据重复修改

优化后可运行代码

const remote1 = 'https://jsonplaceholder.typicode.com/posts/1'
const remote2 = 'https://jsonplaceholder.typicode.com/posts/2'
const RETRY_COUNT = 3;

// 单操作通用重试工具
async function retry(fn, count = RETRY_COUNT) {
  for (let i = 0; i < count; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === count - 1) throw e;
    }
  }
}

// 拉取数据+备份原始值用于回滚
async function fetchAndBackup() {
  const [data1, data2] = await Promise.all([
    fetch(remote1).then(res => res.json()),
    fetch(remote2).then(res => res.json())
  ]);
  // 备份原始值
  const backup = {
    data1OriginalId: data1.id,
    data2OriginalId: data2.id
  };
  return { data1, data2, backup };
}

// 单API更新操作示例,这里替换为你实际的API更新请求
function updateData1(data1, targetId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟随机失败,实际场景替换为真实的PUT/POST请求
      Math.random() > 0.3 ? (data1.id = targetId, resolve()) : reject(new Error('update data1 failed'));
    }, 1000);
  });
}

// data1对应的回滚操作
function rollbackData1(data1, originalId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      data1.id = originalId;
      resolve();
    }, 1000);
  });
}

// 单API更新操作示例,替换为实际的API更新请求
function updateData2(data2, targetId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      Math.random() > 0.3 ? (data2.id = targetId, resolve()) : reject(new Error('update data2 failed'));
    }, 1000);
  });
}

// data2对应的回滚操作
function rollbackData2(data2, originalId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      data2.id = originalId;
      resolve();
    }, 1000);
  });
}

// 主事务流程
async function main() {
  const { data1, data2, backup } = await fetchAndBackup();
  const executedRollbacks = [];

  try {
    // 第一步更新data1,失败直接抛错不用回滚
    await retry(() => updateData1(data1, 2));
    // 记录已完成操作对应的回滚
    executedRollbacks.push(() => rollbackData1(data1, backup.data1OriginalId));

    // 第二步更新data2
    await retry(() => updateData2(data2, 3));
    executedRollbacks.push(() => rollbackData2(data2, backup.data2OriginalId));

    console.log('全部更新成功');
    return 'OK!';
  } catch (e) {
    console.log('更新失败,开始回滚,错误信息:', e.message);
    // 倒序执行回滚,先回滚最后完成的操作
    for (let i = executedRollbacks.length - 1; i >= 0; i--) {
      // 回滚也做重试,保证回滚成功率
      await retry(executedRollbacks[i]);
    }
    console.log('全部回滚完成');
    return `Tried ${RETRY_COUNT} times and failed`;
  }
}

main().then(console.log);

逻辑说明

  • 所有更新操作执行前先拉取原始数据做备份,作为回滚的基准值
  • 每个更新操作单独重试,避免重复执行已成功的操作
  • 每完成一个更新操作就登记对应的回滚函数,失败时倒序执行所有登记的回滚
  • 回滚操作也增加重试逻辑,降低回滚失败的概率

内容的提问来源于stack exchange,提问作者Nemo17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 02:15:05