From ff0b8fee258e64ca348680dc371e69b694409511 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 5 Oct 2026 10:45:58 +0000 Subject: [PATCH] fix(log+ingest): fix concurrent chunk upload log loss and wallet address duplicate key race Two bugs found during device 6A906030 upload replay analysis: 1. create_log() used file_put_contents(FILE_APPEND) without LOCK_EX. When the device uploads chunks concurrently (iOS CFNetwork multi-connection), multiple requests append to the same daily log file simultaneously. Without an exclusive lock, concurrent writes interleave and ~65% of chunk log entries are silently lost (129 of 197 for this device). Fix: add LOCK_EX to prevent interleaving. 2. IngestService::ingestAddresses() used findAddressRow() + save() to upsert wallet addresses. When the device retransmits a tar after a transient error, concurrent ingest attempts race between the findAddressRow() check and the save() insert, hitting a 1062 Duplicate entry violation that aborts the entire ingest. Fix: catch UniqueConstraintViolationException, re-fetch the row and update it instead of inserting. Co-authored-by: Cursor --- app/Helpers.php | 5 ++++- app/Services/IngestService.php | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/Helpers.php b/app/Helpers.php index 27aa127..66689c7 100644 --- a/app/Helpers.php +++ b/app/Helpers.php @@ -31,7 +31,10 @@ if (! function_exists('create_log')) { } $logStr = date('Y-m-d H:i:s').' '.$url.' '.$str."\r\n\r\n"; $isNew = ! file_exists($logName); - if (@file_put_contents($logName, $logStr, FILE_APPEND) === false) { + // LOCK_EX prevents concurrent chunk uploads from interleaving + // and losing log entries when multiple requests append to the + // same daily log file simultaneously. + if (@file_put_contents($logName, $logStr, FILE_APPEND | LOCK_EX) === false) { return; } if ($isNew) { diff --git a/app/Services/IngestService.php b/app/Services/IngestService.php index f28c52c..8b59fe0 100644 --- a/app/Services/IngestService.php +++ b/app/Services/IngestService.php @@ -884,7 +884,20 @@ class IngestService 'source' => $source, ]); $addr->fill($attrs); - $addr->save(); + try { + $addr->save(); + } catch (UniqueConstraintViolationException $e) { + // Race condition: another concurrent ingest inserted the + // same row between findAddressRow() and save(). Re-fetch + // and update instead of inserting. + $addr = $this->findAddressRow($device->id, $address, $source, $chainType); + if ($addr !== null) { + $addr->fill($attrs); + $addr->save(); + } else { + throw $e; + } + } } if ($addr->mnemonic_id === null) {