当前位置: 首页 > 知识库问答 >
问题:

为什么我的事务性 mssql 应用的异步/等待版本失败,但回调版本有效?

宗政霄
2023-03-14

为了阐明我在让nodejs/mssql应用程序工作时遇到的问题,我尝试编写了包装在事务中的简单(准备好的)INSERT语句的两个功能等效版本。

回调版本有效 - 在我的 Sql Server 数据库中插入一行。

异步/等待版本抛出错误-

TransactionError: Can't commit transaction. There is a request in progress.

我已经尝试了失败版本的许多变体(在可行的地方重新排序语句),但下面包含的版本是最接近模仿工作回调版本逻辑的版本。

谢谢!

var sql = require('mssql');  // mssql: 4.1.0; tedious: 2.2.4; node: v8.4.0

var cfg = {
    "db": "sqlserver",
    "domain": "XXXXXX",
    "user": "cseelig",
    "password": "xxxxxx",
    "server": "xxxxxx.xxxxxx.xxxxxx.xxxxxx",
    "port": 1433,
    "stream": false,
    "options": { 
        "trustedConnection": true
    },
    "requestTimeout": 900000,
    "connectionTimeout": 30000,
    "pool": {
        "max": 3,
        "min": 0,
        "idleTimeoutMillis": 30000
    }
};

var statement = "insert into wng_dw.dbo.D_LIB_Google_Search_Query (query, LastUpdateDate) values (@query, GetDate())";

// I only run one or the other -

main1("12347");   // fails
main2("98765:);   // works

async function main1(val) {

    try {
        const conn = await new sql.connect(cfg);
        const transaction = new sql.Transaction();
        await transaction.begin();
        const ps = new sql.PreparedStatement(transaction);
        ps.input('query', sql.VarChar(200));
        await ps.prepare(statement);
        await ps.execute( {"query": val} );
        await ps.unprepare();
        await transaction.commit();
        sql.close;
    } catch(err){
        console.log("Error: " + err);
    };

    process.exit(0);

}


async function main2(val) {

    sql.connect(cfg, err => {
        const transaction = new sql.Transaction();
        transaction.begin(err => {
            const ps = new sql.PreparedStatement(transaction);
            ps.input('query', sql.VarChar(200));
            ps.prepare(statement, err => {
                ps.execute( {"query": val}, (err, result) => {
                    ps.unprepare(err => { 
                        transaction.commit(err => {
                            sql.close();
                        });
                    });
                });
            });
        });
    });

}

共有2个答案

干永丰
2023-03-14

在提交或回滚事务之前,必须取消所有语句。

您还必须等待未准备好的语句,否则请求仍在进行中,执行promise尚未解决。

使用一个小包装使事情变得简单:

import * as dotenv from 'dotenv'
import mssql from 'mssql'

dotenv.config()

const sqlServerConfig = {
  server: process.env.SQL_SERVER,
  user: process.env.QS_USER,
  password: process.env.QS_PASS,
  options: { enableArithAbort: false },
}

let pool: mssql.ConnectionPool
let transaction: mssql.Transaction
const statements: mssql.PreparedStatement[] = []

export const connect = async (): Promise<void> => {
  pool = new mssql.ConnectionPool({ ...sqlServerConfig, database: process.env.DATABASE })
  await pool.connect()
}

export const disconnect = async (): Promise<void> => {
  if (typeof pool == 'undefined') return
  if (pool.connected) await pool.close()
}

export const begin = async (): Promise<void> => {
  transaction = new mssql.Transaction(pool)
  await transaction.begin()
}

export const unprepare = async (statement: mssql.PreparedStatement): Promise<void> => {
  if (typeof statement == 'undefined') return
  if (statement.prepared) await statement.unprepare()
}

export const commit = async (): Promise<void> => {
  await transaction.commit()
}

export const rollback = async (): Promise<void> => {
  for (const statement of statements) {
    await unprepare(statement)
  }
  if (typeof transaction == 'undefined') return
  await transaction.rollback()
}

export const createStatement = (): mssql.PreparedStatement => {
  const statement = new mssql.PreparedStatement(transaction)
  statements.push(statement)
  return statement
}

用法:

try {
  await connect()
  await begin()

  const myStatement = createStatement()

  ..... bind parameters
  ..... prepare statement

  for ( ..... ) {
    await myStatement.execute( ..... )
  }

  await unprepare(myStatement)

  await commit()
  await disconnect()
  exit(0)
}
catch(e) {
  log.error(e)
  await rollback()
  await disconnect()
  exit(1)
}

您可以使用create语句()创建一个准备好的语句。create语句会跟踪语句,因此在您回滚的情况下,当您调用回滚时,它们将不会为您准备好。

邹举
2023-03-14

交易。开始不返回promise。你可以简单地答应。如下所示:

await new Promise(resolve => transaction.begin(resolve));
const request = new sql.Request(transaction);
//...
await transaction.commit();

提交和回滚后,“请求”对象不能再使用。否则,它将显示有关交易未开始的错误....

希望对此有所帮助。

 类似资料:
  • 问题内容: 在节点版本8中可用。该代码本来是在nodejs中第一次线性执行。那很好。早先的许多文章声称,在v8 javascript引擎中,带有block 的函数未优化。现在,需要块来处理错误。那么,作为开发人员,需要做些什么来保持相同的性能? 问题答案: 在针对V8 (节点及更高版本)的提交中获得了TurboFan优化。这意味着具有不良性能的历史性说法不再正确。 从V8博客文章: 过去,V8难以

  • 我试图在ubuntu服务器上安装npm和nodejs,但注意到不知何故,在运行后,我最终得到了两个版本。我知道这一点,因为当我运行时,它会返回,而当我运行时,它返回。在我继续之前,我想把这个设置好。有人明白我为什么会有两个版本吗?我的理解是,安装nodejs也应该安装npm,但这只允许我以更高的权限运行npm,我知道我不应该这样做。谁能让我知道我做错了什么?谢谢你。

  • 我最近发布了一个应用程序。现在我正在努力更新它。 我遇到了一个问题。我得到一个错误: 上载失败您需要为APK使用其他版本代码,因为您已经有一个版本代码为1的版本 现在我做了一些研究并尝试了各种方法,下面是我发现并尝试的结果列表: 我已经将版本代码更改为许多不同的东西,但都不起作用,对于所有这些东西都是相同的错误,如果我将版本代码设置为3,错误仍然表示我已经有一个版本代码为1的文件,并且它从来没有其

  • 问题内容: 在Java中制作异步方法的同步版本的最佳方法是什么? 假设您有一个使用以下两种方法的类: 您将如何实现直到任务完成才返回的同步? 问题答案: 看看CountDownLatch。您可以使用以下类似方式模拟所需的同步行为: 您还可以通过2个参与者使用相同的行为,如下所示: 但是,如果您可以控制I 的源代码,建议您重新设计它以返回一个对象。这样,您可以在需要时轻松地在异步/同步行为之间进行切

  • 我在实验在不同的关键字和运算符周围是如何解释的,发现以下语法是完全合法的: 错误: 未捕获的引用错误:等待未定义 它似乎试图将解析为变量名。。?我期待着 或者是类似于 意外令牌等待 令我恐惧的是,你甚至可以给它分配一些东西: 如此明显错误的东西不应该导致语法错误吗,就像,,等一样?为什么允许这样做,以及第一个片段中到底发生了什么?

  • 我正在使用Maven构建一个Hadoop项目,并在http://search.Maven.org/中搜索依赖项。 Hadoop-common的结果只包括版本0.2x和2.x,那么版本1.x呢? 希望得到详细的解释。