Skip to content

Commit 4af97fb

Browse files
committed
review: Fail when database is ahead of app
Instead of logging a warning when the database is ahead of the application, return an error.
1 parent 6b5bb6f commit 4af97fb

File tree

2 files changed

+17
-43
lines changed

2 files changed

+17
-43
lines changed

pkg/db/db.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"io/fs"
1010
"os"
1111
"path/filepath"
12-
"sync"
1312
"time"
1413

1514
"github.com/gofrs/flock"
@@ -42,8 +41,6 @@ type dao struct {
4241
//go:embed migrations/*.sql
4342
var migrations embed.FS
4443

45-
var databaseAheadWarningOnce sync.Once
46-
4744
type options struct {
4845
dbFile string
4946
migrationsFS fs.FS
@@ -205,19 +202,14 @@ func runMigrations(dbFile string, db *sql.DB, migrationsFS fs.FS, migrationsPath
205202

206203
// For fresh databases, always run migrations
207204
if !isFreshDatabase {
208-
// Check to see if the database is ahead of the migrations
205+
// Check if database version is ahead of available migrations
206+
// This happens when running older code against a database that was upgraded by newer code
209207
_, _, err = migDriver.ReadUp(version)
210208
if errors.Is(err, os.ErrNotExist) {
211-
// The database is ahead of the migrations - no migration file exists for current version
212-
// Log warning but don't fail - database is in a valid state, just newer than this code version
213-
// Use sync.Once to avoid duplicate warnings when db.New() is called multiple times in one process
214-
databaseAheadWarningOnce.Do(func() {
215-
log.Logf("Warning database version %d (%s) is newer than expected. Upgrade to the newest version to prevent issues.", version, dbFile)
216-
})
217-
return nil
218-
} else if err != nil {
219-
// Some other error occurred while reading migrations
220-
return fmt.Errorf("failed to read migration file: %w", err)
209+
return fmt.Errorf("database version %d (%s) is ahead of the current application version. Please upgrade to the latest version", version, dbFile)
210+
}
211+
if err != nil {
212+
return fmt.Errorf("failed to read migration file for version %d: %w", version, err)
221213
}
222214
}
223215

pkg/db/migration_test.go

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package db
22

33
import (
4-
"bytes"
54
"database/sql"
65
"io/fs"
76
"os"
@@ -14,8 +13,6 @@ import (
1413
"github.com/golang-migrate/migrate/v4/source/iofs"
1514
"github.com/stretchr/testify/assert"
1615
"github.com/stretchr/testify/require"
17-
18-
"github.com/docker/mcp-gateway/pkg/log"
1916
)
2017

2118
// Helper to get test migrations filesystem
@@ -145,7 +142,7 @@ func TestConcurrentMigration(t *testing.T) {
145142
func TestDatabaseAheadOfMigrationFiles(t *testing.T) {
146143
// Test scenario: Database is at version 3, but migration files only go up to version 2
147144
// This simulates running an older version of the code against a newer database
148-
// Expected: No migrations run, no error, database stays at version 3
145+
// Expected: Error indicating the code version is too old for the database
149146

150147
tempDir := t.TempDir()
151148
dbFile := filepath.Join(tempDir, "test.db")
@@ -157,42 +154,27 @@ func TestDatabaseAheadOfMigrationFiles(t *testing.T) {
157154
versionBefore := getDatabaseVersion(t, dbFile)
158155
assert.Equal(t, uint(3), versionBefore, "database should start at version 3")
159156

160-
// Capture log output
161-
var logBuf bytes.Buffer
162-
log.SetLogWriter(&logBuf)
163-
defer log.SetLogWriter(os.Stderr)
164-
165157
// Try to initialize with migrations that only go up to version 2
166-
// This should recognize database is ahead and not run any migrations
167-
dao, err := New(
158+
// This should fail with a descriptive error
159+
_, err := New(
168160
WithDatabaseFile(dbFile),
169161
WithMigrations(createLimitedMigrationsFS(t), "."),
170162
)
171-
require.NoError(t, err, "should not error when database is ahead of migration files")
172-
require.NotNil(t, dao)
173-
defer dao.Close()
163+
require.Error(t, err, "should error when database is ahead of migration files")
174164

175-
// Verify warning was logged
176-
logOutput := logBuf.String()
177-
assert.Contains(t, logOutput, "Warning database version 3", "should log warning with version")
178-
assert.Contains(t, logOutput, "is newer than expected", "should mention newer than expected")
179-
assert.Contains(t, logOutput, "Upgrade to the newest version to prevent issues.", "should suggest upgrade")
180-
assert.Contains(t, logOutput, dbFile, "should include database file path")
165+
// Verify error message is descriptive
166+
assert.Contains(t, err.Error(), "database version 3", "should mention current database version")
167+
assert.Contains(t, err.Error(), "ahead of the current application version", "should clearly state the problem")
168+
assert.Contains(t, err.Error(), dbFile, "should include database file path")
169+
assert.Contains(t, err.Error(), "upgrade to the latest version", "should suggest upgrade")
181170

182-
// Verify database version stayed at 3
171+
// Verify database version stayed at 3 (no changes were made)
183172
versionAfter := getDatabaseVersion(t, dbFile)
184173
assert.Equal(t, uint(3), versionAfter, "database should remain at version 3")
185174

186-
// Verify database is not dirty
175+
// Verify database is not dirty (error occurred before any migration attempts)
187176
dirty := getDatabaseDirtyState(t, dbFile)
188177
assert.False(t, dirty, "database should not be dirty")
189-
190-
// Verify all 3 tables still exist (they were created during setup)
191-
tables := []string{"users", "posts"}
192-
for _, table := range tables {
193-
exists := checkTableExists(t, dbFile, table)
194-
assert.True(t, exists, "table %s should still exist", table)
195-
}
196178
}
197179

198180
// Helper functions

0 commit comments

Comments
 (0)