This commit is contained in:
wayanrivan
2026-07-28 14:32:40 +07:00
parent bb293c9851
commit 856bce46ea

View File

@ -393,153 +393,143 @@ class GpsTracksModels {
static bundleCreate2(logs = {}, rltm = {}) { static bundleCreate2(logs = {}, rltm = {}) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let lock = null; let lock = null
let trackLock = null; // Scope dipindah ke atas agar bisa di-unlock di akhir
let conn = null; // Scope dipindah ke atas agar bisa di-rollback
try { try {
conn = await MysqlHelpers.createConnection(); const conn = await MysqlHelpers.createConnection()
await MysqlHelpers.createTrx(conn); await MysqlHelpers.createTrx(conn)
let rltmLength = Object.keys(rltm).length; let rltmLength = Object.keys(rltm).length
let resLogs = undefined; let resLogs = undefined
if (Object.keys(logs).length > 0) { if (Object.keys(logs).length > 0) {
resLogs = await MysqlHelpers.queryTrx( resLogs = await MysqlHelpers.queryTrx(
conn, conn,
`INSERT INTO t_gps_tracks SET ?;`, `INSERT INTO t_gps_tracks SET ?;`,
[logs] [logs],
); )
let trackLock = null
try {
if (logs.action == "location") { if (logs.action == "location") {
// Lock 1: Track Insert
trackLock = await rltmMutex.lock( trackLock = await rltmMutex.lock(
`tracks_insert:${logs.device_id}`, `tracks_insert:${logs.device_id}`,
{ {
duration: 9000, // Diubah menjadi 9 detik (9000 ms) duration: 90000,
maxWait: 9000, maxWait: 90000,
} }
); )
const date = logs.crt_d; const date = logs.crt_d
const mm = moment.unix(date).format("MM"); const mm = moment.unix(date).format("MM")
const yy = moment.unix(date).format("YY"); const yy = moment.unix(date).format("YY")
logs.id = resLogs.insertId; logs.id = resLogs.insertId
await MysqlHelpers.queryTrx( await MysqlHelpers.queryTrx(
conn, conn,
`INSERT INTO tracks_${yy}${mm} SET ?`, `
INSERT INTO tracks_${yy}${mm}
SET ?
`,
[logs] [logs]
); )
}
} finally {
if (trackLock) {
await rltmMutex.unlock(trackLock)
}
} }
// HAPUS blok finally di sini. trackLock WAJIB ditahan sampai COMMIT selesai.
} }
if (rltmLength > 0 && typeof resLogs !== "undefined") { if (rltmLength > 0 && typeof resLogs !== "undefined")
rltm.master_id = resLogs.insertId; rltm.master_id = resLogs.insertId
}
if (rltmLength > 0 && rltm.latitude !== null && rltm.longitude !== null) { if (
// Lock 2: Realtime Update rltmLength > 0 &&
rltm.latitude !== null &&
rltm.longitude !== null
) {
// === KUNCI DI SINI ===
// Lock per device_id supaya SELECT-DELETE-INSERT untuk
// device yang sama tidak berjalan bersamaan dari request/worker
// lain. Inilah yang tadinya memicu ER_DUP_ENTRY dan
// "Lock wait timeout" karena banyak transaksi menumpuk
// menunggu baris yang sama.
lock = await rltmMutex.lock(`t_gps_tracks_rltm:${rltm.device_id}`, { lock = await rltmMutex.lock(`t_gps_tracks_rltm:${rltm.device_id}`, {
duration: 9000, // Diubah menjadi 9 detik (9000 ms) duration: 90000, // maksimal lock dipegang 5 detik
maxWait: 9000, maxWait: 90000, // menunggu lock maksimal 10 detik lalu error
}); })
let devices = await MysqlHelpers.queryTrx( let devices = await MysqlHelpers.queryTrx(
conn, conn,
`SELECT id FROM t_gps_tracks_rltm WHERE device_id = ?`, `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.device_id = ?`,
[rltm.device_id] [rltm.device_id],
); )
if (devices.length > 1) { if (devices.length > 1)
await MysqlHelpers.queryTrx( await MysqlHelpers.queryTrx(
conn, conn,
`DELETE FROM t_gps_tracks_rltm WHERE device_id = ?;`, `DELETE from t_gps_tracks_rltm WHERE device_id = ?;`,
[rltm.device_id] [rltm.device_id],
); )
}
if (rltm.vhc_id != 0) { if (rltm.vhc_id != 0) {
let vhcs = await MysqlHelpers.queryTrx( let vhcs = await MysqlHelpers.queryTrx(
conn, conn,
`SELECT id FROM t_gps_tracks_rltm WHERE vhc_id = ?`, `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.vhc_id = ?`,
[rltm.vhc_id] [rltm.vhc_id],
); )
if (vhcs.length > 1) { if (vhcs.length > 1)
await MysqlHelpers.queryTrx( await MysqlHelpers.queryTrx(
conn, conn,
`DELETE FROM t_gps_tracks_rltm WHERE vhc_id = ?;`, `DELETE from t_gps_tracks_rltm WHERE vhc_id = ?;`,
[rltm.vhc_id] [rltm.vhc_id],
); )
}
} }
if (rltm.drv_id != 0) { if (rltm.drv_id != 0) {
let drvs = await MysqlHelpers.queryTrx( let drvs = await MysqlHelpers.queryTrx(
conn, conn,
`SELECT id FROM t_gps_tracks_rltm WHERE drv_id = ?`, `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.drv_id = ?`,
[rltm.drv_id] [rltm.drv_id],
); )
if (drvs.length > 1) { if (drvs.length > 1)
await MysqlHelpers.queryTrx( await MysqlHelpers.queryTrx(
conn, conn,
`DELETE FROM t_gps_tracks_rltm WHERE drv_id = ?;`, `DELETE from t_gps_tracks_rltm WHERE drv_id = ?;`,
[rltm.drv_id] [rltm.drv_id],
); )
}
} }
await MysqlHelpers.queryTrx( await MysqlHelpers.queryTrx(
conn, conn,
`INSERT INTO t_gps_tracks_rltm SET ? ON DUPLICATE KEY UPDATE ?;`, `INSERT INTO t_gps_tracks_rltm SET ? ON DUPLICATE KEY UPDATE ?;`,
[rltm, rltm] [rltm, rltm],
); )
} }
// Eksekusi Commit SEBELUM melepaskan Mutex await MysqlHelpers.commit(conn)
await MysqlHelpers.commit(conn);
resolve({ resolve({
type: "success", type: "success",
result: resLogs, result: resLogs,
}); })
} catch (err) { } catch (err) {
console.log("ERROR bundleCreate2 : " + (rltm.device_id || "unknown")); console.log("ERROR bundleCreate2 : " + rltm.device_id)
reject(err)
// WAJIB ROLLBACK JIKA ERROR! (Jika tidak, koneksi menggantung & menyebabkan Lock Wait Timeout)
if (conn) {
try {
await MysqlHelpers.rollback(conn);
} catch (rollbackErr) {
console.log("ERROR Rollback : ", rollbackErr);
}
}
reject(err);
} finally { } finally {
// MUTEX HANYA BOLEH DILEPAS SETELAH COMMIT / ROLLBACK SELESAI // Lock selalu dilepas, baik sukses maupun gagal,
if (trackLock) { // supaya tidak deadlock request device yang sama berikutnya.
try {
await rltmMutex.unlock(trackLock);
} catch (unlockErr) {
console.log("ERROR unlock trackLock : " + logs.device_id, unlockErr);
}
}
if (lock) { if (lock) {
try { try {
await rltmMutex.unlock(lock); await rltmMutex.unlock(lock)
} catch (unlockErr) { } catch (unlockErr) {
console.log("ERROR unlock lock : " + rltm.device_id, unlockErr); console.log("ERROR unlock mutex : " + rltm.device_id, unlockErr)
} }
} }
// Catatan: Jika MysqlHelpers tidak otomatis melepas (release) koneksi
// ke pool setelah commit/rollback, pastikan Anda menambahkannya di sini.
// if (conn && typeof conn.release === 'function') conn.release();
} }
}); })
} }
static async get2() { static async get2() {