From 856bce46ea43bc670f17ae1b94d88f136968ba93 Mon Sep 17 00:00:00 2001 From: wayanrivan Date: Tue, 28 Jul 2026 14:32:40 +0700 Subject: [PATCH] update --- models/GpsTracksModels.js | 178 ++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 94 deletions(-) diff --git a/models/GpsTracksModels.js b/models/GpsTracksModels.js index 50f0009..74f5f0f 100755 --- a/models/GpsTracksModels.js +++ b/models/GpsTracksModels.js @@ -393,153 +393,143 @@ class GpsTracksModels { static bundleCreate2(logs = {}, rltm = {}) { return new Promise(async (resolve, reject) => { - 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 - + let lock = null try { - conn = await MysqlHelpers.createConnection(); - await MysqlHelpers.createTrx(conn); + const conn = await MysqlHelpers.createConnection() + await MysqlHelpers.createTrx(conn) - let rltmLength = Object.keys(rltm).length; - let resLogs = undefined; + let rltmLength = Object.keys(rltm).length + let resLogs = undefined if (Object.keys(logs).length > 0) { resLogs = await MysqlHelpers.queryTrx( conn, `INSERT INTO t_gps_tracks SET ?;`, - [logs] - ); + [logs], + ) - if (logs.action == "location") { - // Lock 1: Track Insert - trackLock = await rltmMutex.lock( - `tracks_insert:${logs.device_id}`, - { - duration: 9000, // Diubah menjadi 9 detik (9000 ms) - maxWait: 9000, - } - ); + let trackLock = null - const date = logs.crt_d; - const mm = moment.unix(date).format("MM"); - const yy = moment.unix(date).format("YY"); + try { + if (logs.action == "location") { - logs.id = resLogs.insertId; + trackLock = await rltmMutex.lock( + `tracks_insert:${logs.device_id}`, + { + duration: 90000, + maxWait: 90000, + } + ) - await MysqlHelpers.queryTrx( - conn, - `INSERT INTO tracks_${yy}${mm} SET ?`, - [logs] - ); + const date = logs.crt_d + const mm = moment.unix(date).format("MM") + const yy = moment.unix(date).format("YY") + + logs.id = resLogs.insertId + + await MysqlHelpers.queryTrx( + conn, + ` + INSERT INTO tracks_${yy}${mm} + SET ? + `, + [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") { - rltm.master_id = resLogs.insertId; - } + if (rltmLength > 0 && typeof resLogs !== "undefined") + rltm.master_id = resLogs.insertId - if (rltmLength > 0 && rltm.latitude !== null && rltm.longitude !== null) { - // Lock 2: Realtime Update + if ( + 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}`, { - duration: 9000, // Diubah menjadi 9 detik (9000 ms) - maxWait: 9000, - }); + duration: 90000, // maksimal lock dipegang 5 detik + maxWait: 90000, // menunggu lock maksimal 10 detik lalu error + }) let devices = await MysqlHelpers.queryTrx( conn, - `SELECT id FROM t_gps_tracks_rltm WHERE device_id = ?`, - [rltm.device_id] - ); - if (devices.length > 1) { + `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.device_id = ?`, + [rltm.device_id], + ) + if (devices.length > 1) await MysqlHelpers.queryTrx( conn, - `DELETE FROM t_gps_tracks_rltm WHERE device_id = ?;`, - [rltm.device_id] - ); - } + `DELETE from t_gps_tracks_rltm WHERE device_id = ?;`, + [rltm.device_id], + ) if (rltm.vhc_id != 0) { let vhcs = await MysqlHelpers.queryTrx( conn, - `SELECT id FROM t_gps_tracks_rltm WHERE vhc_id = ?`, - [rltm.vhc_id] - ); - if (vhcs.length > 1) { + `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.vhc_id = ?`, + [rltm.vhc_id], + ) + if (vhcs.length > 1) await MysqlHelpers.queryTrx( conn, - `DELETE FROM t_gps_tracks_rltm WHERE vhc_id = ?;`, - [rltm.vhc_id] - ); - } + `DELETE from t_gps_tracks_rltm WHERE vhc_id = ?;`, + [rltm.vhc_id], + ) } if (rltm.drv_id != 0) { let drvs = await MysqlHelpers.queryTrx( conn, - `SELECT id FROM t_gps_tracks_rltm WHERE drv_id = ?`, - [rltm.drv_id] - ); - if (drvs.length > 1) { + `SELECT id FROM t_gps_tracks_rltm as rltm WHERE rltm.drv_id = ?`, + [rltm.drv_id], + ) + if (drvs.length > 1) await MysqlHelpers.queryTrx( conn, - `DELETE FROM t_gps_tracks_rltm WHERE drv_id = ?;`, - [rltm.drv_id] - ); - } + `DELETE from t_gps_tracks_rltm WHERE drv_id = ?;`, + [rltm.drv_id], + ) } await MysqlHelpers.queryTrx( conn, `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({ type: "success", result: resLogs, - }); - + }) } catch (err) { - console.log("ERROR bundleCreate2 : " + (rltm.device_id || "unknown")); - - // 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); - + console.log("ERROR bundleCreate2 : " + rltm.device_id) + reject(err) } finally { - // MUTEX HANYA BOLEH DILEPAS SETELAH COMMIT / ROLLBACK SELESAI - if (trackLock) { - try { - await rltmMutex.unlock(trackLock); - } catch (unlockErr) { - console.log("ERROR unlock trackLock : " + logs.device_id, unlockErr); - } - } + // Lock selalu dilepas, baik sukses maupun gagal, + // supaya tidak deadlock request device yang sama berikutnya. if (lock) { try { - await rltmMutex.unlock(lock); + await rltmMutex.unlock(lock) } 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() {