首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Node.js、PostgreSQL中的事务协调、乐观并发控制和事务重试

Node.js、PostgreSQL中的事务协调、乐观并发控制和事务重试
EN

Stack Overflow用户
提问于 2020-02-21 13:01:54
回答 1查看 2.8K关注 0票数 6

我希望使用PostgreSQL事务隔离来确保乐观并发控制模式的数据正确性,在这种模式中,冲突的事务会自动重新尝试,而不是我的应用程序对数据库行和表进行预先锁定。

实现这一点的一种常见方法是,web应用程序通过中间件层在代码块或重放HTTP请求中执行特定次数的重放HTTP请求,也称为HTTP重放。下面是一个用于金字塔和Python应用程序web的中间件的例子

我没有找到任何关于Node.js及其PostgreSQL驱动程序如何处理两个并发事务的情况的好信息,其中一个由于读写冲突而无法处理。PostgreSQL将回滚其中一个事务,但这是如何向应用程序发出信号的?在Python中,PSQL驱动程序会在这种情况下引发psycopg2.extensions.TransactionRollbackError对于其他SQL数据库驱动程序,这里有一些它们将引发的异常

当您将SQL事务隔离级别设置为SERIALIZABLE时,这种行为更常见,因为您倾向于在负载下得到更多冲突,所以我想优雅地处理它,而不是将HTTP 500提供给用户。

我的问题是:

  • 如何使用PostgreSQL和一些常见的ORM框架(如TypeORM )检测脏读回滚--如果需要特殊处理而重试库不能独立?
  • 是否有中间件(NestJS/Express.js/其他)处理此问题,并在数据库驱动程序出现事务回滚时自动重放HTTP请求N次?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-10-28 16:20:18

以下是在使用使用图书馆 (如TypeORM)的库时如何处理并发性:

代码语言:javascript
运行
复制
/**
 * Check error code to determine if we should retry a transaction.
 * 
 * See https://www.postgresql.org/docs/10/errcodes-appendix.html and
 * https://stackoverflow.com/a/16409293/749644
 */
function shouldRetryTransaction(err: unknown) {
  const code = typeof err === 'object' ? String((err as any).code) : null
  return code === '40001' || code === '40P01';
}

/**
 * Using a repeatable read transaction throws an error with the code 40001 
 * "serialization failure due to concurrent update" if the user was 
 * updated by another concurrent transaction.
 */
async function updateUser(data: unknown) {
  try {
    return await this.userRepo.manager.transaction(
      'REPEATABLE READ',
      async manager => {
        const user = manager.findOne(User, id);
    
        // Modify user
        // ...
    
        // Save the user
        await manager.save(user);
      }
    );
  } catch (err) {
    if (shouldRetryTransaction(err)) {
      // retry logic     
    } else {
      throw err;
    }
  }
}

对于重试事务,我建议使用一个库,比如抽象重试逻辑的async-retry

你会注意到这个模式对于简单的东西是很好的,但是如果你想传递manager (例如)。因此,事务可以在其他服务中重用),这样就会变得非常麻烦。我建议使用利用延续本地存储来传播事务的typeorm-transactional-cls-hooked库。

以下是您如何为快速应用程序重播事务:

代码语言:javascript
运行
复制
/**
 * Request replay middleware
 */

import retry from 'async-retry';

function replayOnTransactionError(fn: (req, res, next) => unknown) {
  return (req, res, next) => {
    retry(bail => {
      try {
        // Call the actual handler
        await fn(req, res, next);
      } catch (err) {
        if (!shouldRetryTransaction(err)) {
          // Bail out if we're not supposed to retry anymore
          return bail(err);
        }

        // Rethrow error to continue retrying
        throw err;
      }
    }, {
      factor: 2,
      retries: 3,
      minTimeout: 30,
    });
  }
}

app.put('/users/:id', replayOnTransactionError(async (req, res, next) => {
  // ...
}))
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60339223

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档