如何防止MySQL交易在Node.js Express中同时执行?

发布于 2025-02-12 09:12:35 字数 2382 浏览 0 评论 0原文

我真的很努力地实现我认为非常琐碎的东西,这使我发疯。
我只希望以下MySQL数据库交易是相互排斥的。因此,第二个电话必须等到第一个电话完成。
我已经尝试了NPM的Async-Mutex框架,将所有内容包裹在MySQL中的“ select ... for select ...”中,但这些都没有导致预期的结果。
当前的行为如下:当2个HTTP请求同时加入并调用以下模型函数时,它们都会输入canentrybeinsert的功能,显然会导致错误的结果。我想拥有

1 2 1 2

但我得到

1 1 2 2

exports.addEntry = function (entry_id, type) {
    return new Promise(function (resolve, reject) {

        console.log("1")
        beginTransactionP().then(function () {
            canEntryBeInserted(entry_id, type).then(function (canBeInserted) {
                if (canBeInserted) {
                    addEntrySQL(entry_id, type).then(function(){
                        finishTransactionP().then(function() {
                            console.log("2")
                            resolve();
                        })
                    })
                } else {
                    finishTransactionP().then(function() {
                        console.log("2")
                        resolve();
                    })
                }
            })
        })
    })

}

addEntrySQL = function(entry_id, type){
    return new Promise(function (resolve, reject) {
        sql.query("INSERT INTO entry (entry_id, typ, time) VALUES (?, ?, NOW());", [entry_id, type], function (err, rows, fields) {
            if (err) {
                console.log("error: ", err);
                reject(err);
            }
            else {
                console.log("Inserted successfully.")
                resolve();
            }
        });
    })
}

canEntryBeInserted = function (entry_id, type) {
    return new Promise(function (resolve, reject) {
        sql.query("SELECT * FROM entry WHERE entry_id = ? AND typ = 1 UNION SELECT * FROM entry WHERE entry_id = ? ORDER BY entry_id DESC LIMIT 1;", [entry_id, entry_id], function (err0, rows0) {
            console.log(JSON.stringify(rows0), type)
            if (err0) {
                console.log("error: ", err0);
                reject(err0)
            }
            else if ((rows0.length == 1 && (rows0[0].typ == type || rows0[0].typ == 1)) || rows0.length == 2) {
                console.log("Error: Not a valid operation.");
                resolve(false)
            }
            else {
                resolve(true)
            }
        })
    })
}

I'm really struggling to achieve something I actually consider to be pretty trivial and it is driving me crazy.
I just want the following MySQL database transaction to be mutually exclusive. So a second call would have to wait until the first call is finished.
I already tried async-mutex framework from npm, wrapping all in promises, the SELECT ... FOR UPDATE clause from MySQL but none of these lead to the expected result.
The current behaviour is the following: when 2 HTTP requests come in simultaneously and call the following model function, they both enter the canEntryBeInserted function obviously leading to wrong results. I would like to have

1 2 1 2

but I get

1 1 2 2.

exports.addEntry = function (entry_id, type) {
    return new Promise(function (resolve, reject) {

        console.log("1")
        beginTransactionP().then(function () {
            canEntryBeInserted(entry_id, type).then(function (canBeInserted) {
                if (canBeInserted) {
                    addEntrySQL(entry_id, type).then(function(){
                        finishTransactionP().then(function() {
                            console.log("2")
                            resolve();
                        })
                    })
                } else {
                    finishTransactionP().then(function() {
                        console.log("2")
                        resolve();
                    })
                }
            })
        })
    })

}

addEntrySQL = function(entry_id, type){
    return new Promise(function (resolve, reject) {
        sql.query("INSERT INTO entry (entry_id, typ, time) VALUES (?, ?, NOW());", [entry_id, type], function (err, rows, fields) {
            if (err) {
                console.log("error: ", err);
                reject(err);
            }
            else {
                console.log("Inserted successfully.")
                resolve();
            }
        });
    })
}

canEntryBeInserted = function (entry_id, type) {
    return new Promise(function (resolve, reject) {
        sql.query("SELECT * FROM entry WHERE entry_id = ? AND typ = 1 UNION SELECT * FROM entry WHERE entry_id = ? ORDER BY entry_id DESC LIMIT 1;", [entry_id, entry_id], function (err0, rows0) {
            console.log(JSON.stringify(rows0), type)
            if (err0) {
                console.log("error: ", err0);
                reject(err0)
            }
            else if ((rows0.length == 1 && (rows0[0].typ == type || rows0[0].typ == 1)) || rows0.length == 2) {
                console.log("Error: Not a valid operation.");
                resolve(false)
            }
            else {
                resolve(true)
            }
        })
    })
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

烟若柳尘 2025-02-19 09:12:35

以下代码说明了异步函数如何获得锁定,该函数可以防止并发执行,直到再次释放锁。

var locked;
function lock(req, res, next) {
  if (locked)
    locked.on("release", next);
  else {
    locked = new EventEmitter();
    next();
  }
}
function unlock() {
  if (locked) {
    locked.emit("release");
    locked = undefined;
  }
}
app.use(lock, function(req, res) {
  console.log("1");
  setTimeout(function() {
    console.log("2");
    res.end();
    unlock();
  }, 1000);
});

这将在全局变量锁定中维护JavaScript级别上的锁定。您可能需要一些更细颗粒的东西。

选择更新的数据库级别锁定也是可能的,但是如果失败了,请求必须重试直到成功,因为数据库不会发射Release> Release Event就像上面的代码一样。

The following code demonstrates how an asynchronous function can acquire a lock, which prevents concurrent execution until the lock is released again.

var locked;
function lock(req, res, next) {
  if (locked)
    locked.on("release", next);
  else {
    locked = new EventEmitter();
    next();
  }
}
function unlock() {
  if (locked) {
    locked.emit("release");
    locked = undefined;
  }
}
app.use(lock, function(req, res) {
  console.log("1");
  setTimeout(function() {
    console.log("2");
    res.end();
    unlock();
  }, 1000);
});

This maintains the lock on the Javascript level in the global variable lock. You may need something more fine-granular.

A database level lock with select for update is also possible, but if that fails the request would have to try again until it succeeds, because the database does not emit a release event like the code above.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文