MCPcopy Index your code
hub / github.com/coder/coder / checkAndRecover

Function checkAndRecover

scripts/develop/dbrecovery.go:102–215  ·  view source on GitHub ↗

checkAndRecover is the core logic: 1. Ensure tracking table exists. 2. Read DB version. Refuse if dirty. 3. Detect untracked migrations. 4. Detect missing files (needs rollback). 5. Detect content changes (needs --db-continue). 6. Capture current disk state for next time.

(ctx context.Context, logger slog.Logger, db *sql.DB, migrDir string, cfg *devConfig)

Source from the content-addressed store, hash-verified

100// 5. Detect content changes (needs --db-continue).
101// 6. Capture current disk state for next time.
102func checkAndRecover(ctx context.Context, logger slog.Logger, db *sql.DB, migrDir string, cfg *devConfig) error {
103 if _, err := db.ExecContext(ctx, trackingDDL); err != nil {
104 return xerrors.Errorf("create tracking table: %w", err)
105 }
106
107 dbVersion, dirty, err := currentMigrationVersion(ctx, db)
108 if err != nil {
109 return xerrors.Errorf("get db version: %w", err)
110 }
111 if dbVersion < 0 {
112 return nil // Fresh DB.
113 }
114 if dirty {
115 return xerrors.Errorf(
116 "database is dirty at version %d (a migration failed halfway)\n\n"+
117 " --db-reset destroy database and start fresh\n", dbVersion)
118 }
119
120 maxTracked, err := maxTrackedVersion(ctx, db)
121 if err != nil {
122 return xerrors.Errorf("get max tracked version: %w", err)
123 }
124 if dbVersion > maxTracked && maxTracked >= 0 {
125 // Gap between tracking and DB version. This happens when
126 // the server applied migrations via Up() but develop.sh
127 // was interrupted before updateMigrationTracking ran.
128 // captureDownSQL at the end of this function backfills
129 // from disk.
130 logger.Warn(ctx, "migration tracking gap detected, will backfill",
131 slog.F("db_version", dbVersion),
132 slog.F("max_tracked", maxTracked))
133 }
134
135 // Check for missing files (rollback candidates).
136 rollbacks, err := findRollbacks(ctx, db, migrDir)
137 if err != nil {
138 return xerrors.Errorf("find rollbacks: %w", err)
139 }
140
141 if len(rollbacks) > 0 {
142 if !cfg.dbRollback {
143 var details strings.Builder
144 for _, rb := range rollbacks {
145 _, _ = fmt.Fprintf(&details, " version %d: %s (missing from disk)\n", rb.version, rb.filename)
146 }
147 return xerrors.Errorf(
148 "database has migrations that no longer exist on disk:\n%s\n"+
149 " --db-rollback roll back these migrations (preserves data)\n"+
150 " --db-reset destroy database and start fresh\n",
151 details.String())
152 }
153
154 if !contiguousFromTop(rollbacks, dbVersion) {
155 return xerrors.Errorf(
156 "cannot roll back: versions are not contiguous (%s); use --db-reset",
157 formatVersions(rollbacks))
158 }
159

Callers 1

recoverDBFunction · 0.85

Calls 13

currentMigrationVersionFunction · 0.85
maxTrackedVersionFunction · 0.85
findRollbacksFunction · 0.85
contiguousFromTopFunction · 0.85
formatVersionsFunction · 0.85
applyRollbackFunction · 0.85
findContentChangesFunction · 0.85
formatDiffFunction · 0.85
captureDownSQLFunction · 0.85
ExecContextMethod · 0.80
ErrorfMethod · 0.45
StringMethod · 0.45

Tested by

no test coverage detected