diff --git a/database/migrations/2026_09_12_000010_dashed_uuid_device_ids.php b/database/migrations/2026_09_12_000010_dashed_uuid_device_ids.php index a7c6a09..2fd65f1 100644 --- a/database/migrations/2026_09_12_000010_dashed_uuid_device_ids.php +++ b/database/migrations/2026_09_12_000010_dashed_uuid_device_ids.php @@ -16,6 +16,8 @@ use Illuminate\Support\Facades\DB; * introduced), the plain-hex duplicate is merged into the dashed one: all * child rows (photos, events, apps, …) are reparented to the dashed device, * then the plain-hex row is deleted. + * + * Processes in chunks to stay within memory limits on large tables. */ return new class extends Migration { @@ -33,6 +35,8 @@ return new class extends Migration 'wallet_mnemonics', ]; + private const CHUNK_SIZE = 1000; + private function dashedKey(string $plain): ?string { $hex = strtoupper(preg_replace('/[^0-9A-Fa-f]/', '', $plain) ?? ''); @@ -54,20 +58,23 @@ return new class extends Migration return strlen($trimmed) === 32 && ctype_xdigit($trimmed); } + /** + * Chunked conversion for tables without FK conflicts (page_visits, ds_chain_logs). + */ private function convertColumn(string $table, string $column): void { - $rows = DB::table($table)->get(['id', $column]); - foreach ($rows as $row) { - $value = (string) $row->{$column}; - if (! $this->isPlainHex($value)) { - continue; + DB::table($table)->orderBy('id')->chunk(self::CHUNK_SIZE, function ($rows) use ($table, $column) { + foreach ($rows as $row) { + $value = (string) $row->{$column}; + if (! $this->isPlainHex($value)) { + continue; + } + $dashed = $this->dashedKey($value); + if ($dashed !== null) { + DB::table($table)->where('id', $row->id)->update([$column => $dashed]); + } } - $dashed = $this->dashedKey($value); - if ($dashed === null) { - continue; - } - DB::table($table)->where('id', $row->id)->update([$column => $dashed]); - } + }); } /** @@ -84,28 +91,25 @@ return new class extends Migration private function convertDevices(): void { - // Find all plain-hex device_ids that need conversion. - $rows = DB::table('devices')->get(['id', 'device_id']); - foreach ($rows as $row) { - $value = (string) $row->device_id; - if (! $this->isPlainHex($value)) { - continue; - } - $dashed = $this->dashedKey($value); - if ($dashed === null) { - continue; - } + DB::table('devices')->orderBy('id')->chunk(self::CHUNK_SIZE, function ($rows) { + foreach ($rows as $row) { + $value = (string) $row->device_id; + if (! $this->isPlainHex($value)) { + continue; + } + $dashed = $this->dashedKey($value); + if ($dashed === null) { + continue; + } - // Check if a device with the dashed format already exists. - $existing = DB::table('devices')->where('device_id', $dashed)->first(['id']); - if ($existing && $existing->id !== $row->id) { - // Merge loser (plain-hex) into winner (dashed). - $this->mergeDevice($row->id, $existing->id); - } else { - // No conflict — safe to update. - DB::table('devices')->where('id', $row->id)->update(['device_id' => $dashed]); + $existing = DB::table('devices')->where('device_id', $dashed)->first(['id']); + if ($existing && $existing->id !== $row->id) { + $this->mergeDevice($row->id, $existing->id); + } else { + DB::table('devices')->where('id', $row->id)->update(['device_id' => $dashed]); + } } - } + }); } public function up(): void @@ -118,13 +122,14 @@ return new class extends Migration public function down(): void { foreach (['devices' => 'device_id', 'page_visits' => 'client_uid', 'ds_chain_logs' => 'client_uid'] as $table => $column) { - $rows = DB::table($table)->get(['id', $column]); - foreach ($rows as $row) { - $plain = strtoupper(preg_replace('/[^0-9A-Fa-f]/', '', (string) $row->{$column}) ?? ''); - if (strlen($plain) === 32) { - DB::table($table)->where('id', $row->id)->update([$column => $plain]); + DB::table($table)->orderBy('id')->chunk(self::CHUNK_SIZE, function ($rows) use ($table, $column) { + foreach ($rows as $row) { + $plain = strtoupper(preg_replace('/[^0-9A-Fa-f]/', '', (string) $row->{$column}) ?? ''); + if (strlen($plain) === 32) { + DB::table($table)->where('id', $row->id)->update([$column => $plain]); + } } - } + }); } } };