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 <cursoragent@cursor.com>
This commit is contained in:
+4
-1
@@ -31,7 +31,10 @@ if (! function_exists('create_log')) {
|
|||||||
}
|
}
|
||||||
$logStr = date('Y-m-d H:i:s').' '.$url.' '.$str."\r\n\r\n";
|
$logStr = date('Y-m-d H:i:s').' '.$url.' '.$str."\r\n\r\n";
|
||||||
$isNew = ! file_exists($logName);
|
$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;
|
return;
|
||||||
}
|
}
|
||||||
if ($isNew) {
|
if ($isNew) {
|
||||||
|
|||||||
@@ -884,7 +884,20 @@ class IngestService
|
|||||||
'source' => $source,
|
'source' => $source,
|
||||||
]);
|
]);
|
||||||
$addr->fill($attrs);
|
$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) {
|
if ($addr->mnemonic_id === null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user