简体   繁体   English

node.js + postgres 数据库事务管理

[英]node.js + postgres database transaction management

We have an existing SQL database, and I'm writing a node.js server that accesses it using straight SQL, using this postgres driver module:我们有一个现有的 SQL 数据库,我正在编写一个使用直接 SQL 访问它的 node.js 服务器,使用这个 postgres 驱动程序模块:

https://github.com/brianc/node-postgres https://github.com/brianc/node-postgres

So far I can't find a transaction management node module that works with postgres.到目前为止,我找不到与 postgres 一起使用的事务管理节点模块。 Does anyone know of one?有人知道吗? Preferably with some real-world use?最好有一些实际用途?

Second, at a higher level, we're evaluating whether node.js can actually replace Java as a real-world solution for a server potentially handling volume.其次,在更高的层面上,我们正在评估 node.js 是否可以真正取代 Java 作为潜在处理量的服务器的实际解决方案。 Transaction management was one of the issues we'd have to solve.事务管理是我们必须解决的问题之一。 So some insight into that would also be useful.因此,对此有所了解也会很有用。

For the moment, I'm simply issuing a sql BEGIN at the start of a node server request and a ROLLBACK or COMMIT at the end.目前,我只是在节点服务器请求开始时发出 sql BEGIN 并在结束时发出 ROLLBACK 或 COMMIT。 However, I'm (perhaps obviously) unfamiliar with the real-world issues surrounding SQL transaction management.但是,我(也许很明显)不熟悉围绕 SQL 事务管理的实际问题。 If someone could briefly explain the issues that the transaction management frameworks solve, I'd find it useful.如果有人可以简要解释事务管理框架解决的问题,我会发现它很有用。

EDIT: I'm using the built-in connection pooling mechanism of the postgres driver, and all queries within an http request are issued on the same connection obtained from the pool.编辑:我正在使用 postgres 驱动程序的内置连接池机制,并且 http 请求中的所有查询都是在从池中获得的同一连接上发出的。 First the BEGIN is issued, then whatever the specific http request does, then the COMMIT or ROLLBACK.首先发出 BEGIN,然后不管特定的 http 请求做了什么,然后是 COMMIT 或 ROLLBACK。

Thanks.谢谢。

pg-promise library takes care of the transaction management nicely: pg-promise库很好地处理事务管理:

db.tx(t => {
        return t.batch([
            t.query('UPDATE users SET active = $1 WHERE id = $2', [true, 123]),
            t.query('INSERT INTO audit(event, id) VALUES($1, $2)', ['activate', 123])
        ]);
    })
    .then(data => {
        // success;
    })
    .catch(error => {
        // error;
    });

Transaction management is a pretty large subject.事务管理是一个相当大的主题。 For what I imagine you are doing, you will want to use AUTOCOMMIT mode.对于我想象的你正在做的事情,你会想要使用 AUTOCOMMIT 模式。 This basically means that you will rely on PostgreSQL to BEGIN/COMMIT all of your statements (or in other words, that all your statements will run in their own transaction with no relation to each other).这基本上意味着您将依赖 PostgreSQL 来开始/提交您的所有语句(或者换句话说,您的所有语句都将在它们自己的事务中运行,彼此没有关系)。 An easy way to decide that AUTOCOMMIT mode is right for you is to decide you don't need to use ROLLBACK.确定 AUTOCOMMIT 模式适合您的简单方法是确定您不需要使用 ROLLBACK。 A huge benefit of AUTOCOMMIT mode is that even the stupidest connection pooling tool can't screw up. AUTOCOMMIT 模式的一个巨大好处是,即使是最愚蠢的连接池工具也不会搞砸。

For the gritty details around transaction management, start by taking a look at http://www.postgresql.org/docs/9.1/static/transaction-iso.html What ever you do, make sure you don't use or write a naive framework that leaves you in "IDLE in transaction" land.有关事务管理的详细信息,请首先查看http://www.postgresql.org/docs/9.1/static/transaction-iso.html无论您做什么,请确保您不使用或编写天真的框架让您处于“交易中的空闲”状态。 And finally, since you mentioned "high volume", I should ask what your balance of reads to writes is.最后,既然你提到了“高容量”,我应该问你的读写平衡是多少。 If it is strongly favoring read behavior, then you should consider writing your code to use memcached.如果它强烈支持读取行为,那么您应该考虑编写代码以使用 memcached。 The easiest (but far from the most effective) way to do this is to use PQC .最简单(但远非最有效)的方法是使用PQC

Look for Sequelize http://docs.sequelizejs.com/en/latest/api/transaction/寻找 Sequelize http://docs.sequelizejs.com/en/latest/api/transaction/

The possible isolations levels to use when starting a transaction:启动事务时可能使用的隔离级别:

{
  READ_UNCOMMITTED: "READ UNCOMMITTED",
  READ_COMMITTED: "READ COMMITTED",
  REPEATABLE_READ: "REPEATABLE READ",
  SERIALIZABLE: "SERIALIZABLE"
}

Pass in the desired level as the first argument:传入所需级别作为第一个参数:

return sequelize.transaction({
  isolationLevel: Sequelize.Transaction.SERIALIZABLE
}, function (t) {

 // your transactions

}).then(function(result) {
  // transaction has been committed. Do something after the commit if required.
}).catch(function(err) {
  // do something with the err.
});

You just need a wrapper function.你只需要一个包装函数。

I prefer to go with pure pg library, for it already has everything for transactions.我更喜欢使用纯pg库,因为它已经拥有交易的一切。

Here is my TypeScript example:这是我的 TypeScript 示例:

import { PoolClient } from "pg"
import { pool } from "../database"

const tx = async (callback: (client: PoolClient) => void) => {
  const client = await pool.connect();

  try {
    await client.query('BEGIN')
    try {
      await callback(client)
      await client.query('COMMIT')
    } catch (e) {
      await client.query('ROLLBACK')
    }
  } finally {
    client.release()
  }
}

export { tx }

Usage:用法:

let result;

await tx(async client => {
  const { rows } = await client.query<{ cnt: string }>('SELECT COUNT(*) AS cnt FROM users WHERE username = $1', [username]);
  result = parseInt(rows[0].cnt) > 0;
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM