Skip to content

Commit 6f55476

Browse files
tadasantclaude
andcommitted
Add hard safety checks to prevent accidental data loss
- Fail migration if DELETE count != 5 (exactly what we found) - Fail migration if UPDATE count != 1 (exactly what we found) - Remove percentage-based checks in favor of exact numbers - Add detailed logging before and after operations This ensures the migration will only proceed if it encounters exactly the data issues we identified and tested for. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent d372ac3 commit 6f55476

File tree

5 files changed

+69
-243
lines changed

5 files changed

+69
-243
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ require (
3232
github.com/jackc/pgpassfile v1.0.0 // indirect
3333
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
3434
github.com/jackc/puddle/v2 v2.2.2 // indirect
35+
github.com/lib/pq v1.10.9 // indirect
3536
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
3637
github.com/pmezard/go-difflib v1.0.0 // indirect
3738
github.com/prometheus/client_model v0.6.2 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
4242
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
4343
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
4444
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
45+
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
46+
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
4547
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
4648
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
4749
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

internal/database/migrations/008_clean_invalid_data.sql

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33

44
BEGIN;
55

6-
-- Log what we're about to clean up for audit purposes
6+
-- Safety check: Count exactly what we'll be modifying
77
DO $$
88
DECLARE
99
invalid_name_count INTEGER;
1010
empty_version_count INTEGER;
1111
invalid_status_count INTEGER;
1212
duplicate_count INTEGER;
13+
total_to_delete INTEGER;
14+
total_servers INTEGER;
1315
BEGIN
16+
-- Get total server count
17+
SELECT COUNT(*) INTO total_servers FROM servers;
18+
1419
-- Count servers with invalid name format
1520
SELECT COUNT(*) INTO invalid_name_count
1621
FROM servers
@@ -37,19 +42,38 @@ BEGIN
3742
HAVING COUNT(*) > 1
3843
) dups;
3944

40-
-- Log the cleanup operations
41-
IF invalid_name_count > 0 OR empty_version_count > 0 THEN
42-
RAISE NOTICE 'Deleting % servers with invalid names and % servers with empty versions',
43-
invalid_name_count, empty_version_count;
45+
-- Calculate total deletions (some servers might have both invalid name AND empty version)
46+
SELECT COUNT(*) INTO total_to_delete
47+
FROM servers
48+
WHERE value->>'name' NOT SIMILAR TO '[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]/[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]'
49+
OR value->>'version' IS NULL
50+
OR value->>'version' = '';
51+
52+
-- Log the cleanup operations with safety check
53+
RAISE NOTICE 'Migration 008 Data Cleanup Plan:';
54+
RAISE NOTICE ' Total servers in database: %', total_servers;
55+
56+
IF total_servers > 0 THEN
57+
RAISE NOTICE ' Servers to DELETE: % (%.2f%%)', total_to_delete, (total_to_delete::float / total_servers * 100);
58+
ELSE
59+
RAISE NOTICE ' Servers to DELETE: %', total_to_delete;
4460
END IF;
4561

46-
IF invalid_status_count > 0 THEN
47-
RAISE NOTICE 'Fixing % servers with invalid status values (changing to ''active'')',
48-
invalid_status_count;
62+
RAISE NOTICE ' - Invalid names: %', invalid_name_count;
63+
RAISE NOTICE ' - Empty versions: %', empty_version_count;
64+
RAISE NOTICE ' Servers to UPDATE (fix status): %', invalid_status_count;
65+
RAISE NOTICE ' Duplicate name+version pairs: %', duplicate_count;
66+
67+
-- SAFETY CHECK: Fail if numbers don't match what we found in production
68+
-- Based on comprehensive analysis of production data (2025-09-30), we expect:
69+
-- - 5 servers to delete (1 invalid name + 4 empty versions)
70+
-- - 1 server status to update
71+
IF total_to_delete != 5 THEN
72+
RAISE EXCEPTION 'Safety check failed: Expected to delete exactly 5 servers but would delete %. Aborting to prevent data loss.', total_to_delete;
4973
END IF;
5074

51-
IF duplicate_count > 0 THEN
52-
RAISE NOTICE 'Found % duplicate name+version combinations to clean up', duplicate_count;
75+
IF invalid_status_count != 1 THEN
76+
RAISE EXCEPTION 'Safety check failed: Expected to update exactly 1 server status but would update %. Aborting to prevent data corruption.', invalid_status_count;
5377
END IF;
5478
END $$;
5579

@@ -78,13 +102,43 @@ WHERE EXISTS (
78102
AND s2.version_id > s1.version_id
79103
);
80104

81-
-- Log completion
105+
-- Verify the operations completed as expected
82106
DO $$
83107
DECLARE
84108
remaining_count INTEGER;
109+
actual_deleted INTEGER;
110+
actual_updated INTEGER;
111+
still_invalid_names INTEGER;
112+
still_empty_versions INTEGER;
113+
still_invalid_status INTEGER;
85114
BEGIN
86115
SELECT COUNT(*) INTO remaining_count FROM servers;
87-
RAISE NOTICE 'Data cleanup complete. % servers remaining in database.', remaining_count;
116+
117+
-- Check if any invalid data remains
118+
SELECT COUNT(*) INTO still_invalid_names
119+
FROM servers
120+
WHERE value->>'name' NOT SIMILAR TO '[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]/[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]';
121+
122+
SELECT COUNT(*) INTO still_empty_versions
123+
FROM servers
124+
WHERE value->>'version' IS NULL OR value->>'version' = '';
125+
126+
SELECT COUNT(*) INTO still_invalid_status
127+
FROM servers
128+
WHERE value->>'status' IS NOT NULL
129+
AND value->>'status' != ''
130+
AND value->>'status' NOT IN ('active', 'deprecated', 'deleted');
131+
132+
RAISE NOTICE 'Data cleanup complete:';
133+
RAISE NOTICE ' Servers remaining: %', remaining_count;
134+
RAISE NOTICE ' Invalid names remaining: %', still_invalid_names;
135+
RAISE NOTICE ' Empty versions remaining: %', still_empty_versions;
136+
RAISE NOTICE ' Invalid status remaining: %', still_invalid_status;
137+
138+
-- Final safety check: Ensure we cleaned everything we intended to
139+
IF still_invalid_names > 0 OR still_empty_versions > 0 OR still_invalid_status > 0 THEN
140+
RAISE EXCEPTION 'Cleanup incomplete! Invalid data still remains. Aborting.';
141+
END IF;
88142
END $$;
89143

90144
COMMIT;

scripts/mirror_data/fetch_production_data.go

Lines changed: 0 additions & 87 deletions
This file was deleted.

scripts/mirror_data/load_production_data.go

Lines changed: 0 additions & 144 deletions
This file was deleted.

0 commit comments

Comments
 (0)