Race condition pada flash queue worker dapat memicu eksekusi multi-thread menulis partisi memori secara bersamaan, menyebabkan korupsi boot image, kegagalan verifikasi signature, atau device mengalami hard brick. Kasus bricking mirip pada proses flashing via low-level interface seperti fastboot (misalnya relocking bootloader KY-42C) saat state locking dieksekusi sebelum penulisan payload partisi selesai diverifikasi.
Akar Masalah Konkurensi pada Flashing Firmware Fleet
Flashing firmware Over-The-Air (OTA) berbeda dengan task background standar web. Flashing melibatkan sequence stateful: pengunduhan binary, verifikasi hash, transisi boot mode, penulisan block storage, dan switching partisi boot (A/B slot). Masalah muncul saat beberapa trigger update dipicu bersamaan untuk satu target hardware id yang sama, seperti polling interval pendek, event retry otomatis, atau intervensi admin dashboard.
Jika dua worker mengambil job untuk device yang sama tanpa koordinasi state terpusat, terjadi overlapping writing:
- Worker A memulai upload payload firmware versi X.
- Worker B memicu restart bootloader atau flashing versi Y.
- Device terputus di tengah penulisan partisi boot/system, menghasilkan corrupt partition table.
Arsitektur Worker Pool dan Distributed Lock
Penyelesaian race condition ini membutuhkan pemisahan antara ingestion job dan eksekusi hardware menggunakan Distributed Lock (Redlock pattern) serta Idempotency Key berbasis SHA-256 dari binary firmware yang ditargetkan.
// Pseudocode Redis Lock + Flash Execution Job
async function processFlashJob(job) {
const { deviceId, firmwareSha256, firmwareUrl } = job.data;
const resourceKey = `lock:device:flash:${deviceId}`;
const ttl = 60000; // 60 detik lock window
// 1. Dapatkan Distributed Lock via Redlock
const lock = await redlock.acquire([resourceKey], ttl);
try {
// 2. Cek Idempotency State di Database/Redis
const deviceState = await db.devices.findOne({ where: { id: deviceId } });
if (deviceState.current_sha256 === firmwareSha256 && deviceState.flash_status === 'SUCCESS') {
job.log('Firmware hash identik sudah terpasang. Skip flash.');
return;
}
// 3. Heartbeat extender saat flash berjalan lama
const heartbeat = setInterval(async () => {
await lock.extend(ttl);
}, 30000);
await db.devices.update({ flash_status: 'FLASHING' }, { where: { id: deviceId } });
// 4. Eksekusi Flashing ke Transport Layer (Fastboot / OTA Push)
await executeFirmwarePipeline(deviceId, firmwareUrl, firmwareSha256);
clearInterval(heartbeat);
await db.devices.update({
flash_status: 'SUCCESS',
current_sha256: firmwareSha256
}, { where: { id: deviceId } });
} finally {
await lock.release();
}
}Mitigasi Kegagalan Worker Tanpa Menyebabkan Device Bricked
Ketika worker crash atau OOM kill terjadi di tengah eksekusi, lock akan expired secara natural via TTL. Namun, status device bisa tertinggal di state FLASHING. Untuk menghindari retry buta yang mengeksekusi payload kedua saat payload pertama belum jelas statusnya di bootloader, terapkan mitigasi berikut:
1. Validasi Hash SHA-256 Sebelum Flash Execution
Binary firmware harus dihitung SHA-256 checksum-nya saat upload dan disimpan di manifest. Device atau flashing agent harus memvalidasi hash integrity sebelum menandai partisi target sebagai aktif. Jangan pernah menjalankan relock bootloader atau reboot slot tanpa verifikasi signature block storage.
2. Pola A/B (Seamless) Partitioning
Hindari penulisan langsung ke partisi yang sedang berjalan (recovery/active system). Worker harus selalu menulis ke inactive slot (Slot B jika device sedang di Slot A). Jika worker mati sebelum konfirmasi penulisan tuntas, bootloader tetap berada di Slot A dan tidak terjadi brick.
3. Exponential Backoff dengan Jitter
Retry otomatis pada antrean antarmuka hardware harus menerapkan backoff bertahap untuk memberi jeda watchdog hardware menyelesaikan rollback internal:
{
"attempts": 3,
"backoff": {
"type": "exponential",
"delay": 15000
}
}Trade-offs dan Pertimbangan Operasional
Distributed locking via Redlock menambah ketergantungan pada cluster Redis. Jika koneksi Redis latency tinggi, heartbeat lock extender dapat terlambat dieksekusi, menyebabkan worker lain menganggap lock telah hangus saat penulisan flash masih berlangsung.
Alternatif lebih sederhana: gunakan serial queue per-device channel pada message broker (seperti partition key berbasis device_id di Kafka atau RabbitMQ consistent hash exchange) agar seluruh perintah untuk satu hardware tertentu dieksekusi secara serial oleh worker yang sama tanpa distributed lock terdistribusi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!