Realmente estoy luchando por lograr algo que en realidad considero bastante trivial y me está volviendo loco.
Solo quiero que la siguiente transacción de la base de datos MySQL se excluya mutuamente. Por lo tanto, una segunda llamada tendría que esperar hasta que finalice la primera llamada.
Ya probé el marco async-mutex de npm, envolviendo todo en promesas, la cláusula SELECT ... FOR UPDATE de MySQL, pero ninguno de estos conduce al resultado esperado.
El comportamiento actual es el siguiente: cuando 2 solicitudes HTTP ingresan simultáneamente y llaman a la siguiente función de modelo, ambas ingresan a la función canEntryBeInserted que obviamente conduce a resultados incorrectos. me gustaría tener
1 2 1 2
pero lo consigo
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) } }) }) }El siguiente código demuestra cómo una función asincrónica puede adquirir un bloqueo, lo que evita la ejecución simultánea hasta que se libera el bloqueo nuevamente.
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); }); Esto mantiene el bloqueo en el nivel de Javascript en el lock de variable global. Es posible que necesite algo más granulado fino.
También es posible un bloqueo de nivel de base de datos con select for update , pero si eso falla, la solicitud tendría que volver a intentarlo hasta que tenga éxito, porque la base de datos no emite un evento de release como el código anterior.