Skip to content

Commit 1d2f197

Browse files
committed
address review
1 parent bf6beab commit 1d2f197

3 files changed

Lines changed: 78 additions & 14 deletions

File tree

base/metadata_migration_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func NewMetadataMigrationStatus() *MetadataMigrationStatus {
8383
// Callers must pass the live registry-derived set so a DB added mid-migration isn't missed.
8484
func (s *MetadataMigrationStatus) AllDatabasesComplete(expected []string) bool {
8585
if len(expected) == 0 {
86-
return false
86+
return true // no databases to wait for, complete migration
8787
}
8888
for _, id := range expected {
8989
entry, ok := s.Databases[id]

rest/metadatamigrationtest/metadata_migration_test.go

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,10 +1607,6 @@ func TestDeleteNonMigratedDbUnblocksBootstrapMigration(t *testing.T) {
16071607
// this node's status doc reads complete but its local "migration complete" cache (IsMigrationComplete) is updated too.
16081608
// RecheckPendingBucketMetadataMigrations should converge that cache so the node stops falling back
16091609
// to _default._default and stops re-doing the recheck work for that bucket every poll.
1610-
//
1611-
// bug: maybeCompleteBucketMetadataMigration returns early when it sees status.Bootstrap.State == complete,
1612-
// *before* it reaches SetMigrationComplete. So the recheck sees the completed doc but never flips the local cache,
1613-
// and IsMigrationComplete stays false indefinitely.
16141610
func TestRecheckConvergesLocalCacheOnPeerCompletedMigration(t *testing.T) {
16151611
base.TestRequiresCollections(t)
16161612

@@ -1675,3 +1671,79 @@ func TestRecheckConvergesLocalCacheOnPeerCompletedMigration(t *testing.T) {
16751671

16761672
require.True(t, conn.IsMigrationComplete(tb.GetName()), "finding 2: recheck observing a peer-completed status doc should converge the local migration-complete cache")
16771673
}
1674+
1675+
// TestDeleteAllDbsCompletesPendingBootstrapMigration is similar to TestDeleteNonMigratedDbUnblocksBootstrapMigration
1676+
// except that it deletes *every* db (both the migrated db1 and the un-migrated db2) and asserts the same thing
1677+
func TestDeleteAllDbsCompletesPendingBootstrapMigration(t *testing.T) {
1678+
base.TestRequiresCollections(t)
1679+
1680+
ctx := base.TestCtx(t)
1681+
tb := base.GetTestBucket(t)
1682+
defer tb.Close(ctx)
1683+
1684+
rt := rest.NewRestTester(t, &rest.RestTesterConfig{
1685+
CustomTestBucket: tb.NoCloseClone(),
1686+
PersistentConfig: true,
1687+
})
1688+
defer rt.Close()
1689+
1690+
dataStore1, err := tb.GetNamedDataStore(0)
1691+
require.NoError(t, err)
1692+
db1Cfg := rt.NewDbConfig()
1693+
db1Cfg.UseSystemMobileMetadataCollection = base.Ptr(false)
1694+
db1Cfg.Scopes = rest.ScopesConfig{
1695+
dataStore1.ScopeName(): rest.ScopeConfig{
1696+
Collections: rest.CollectionsConfig{dataStore1.CollectionName(): {}},
1697+
},
1698+
}
1699+
rest.RequireStatus(t, rt.CreateDatabase("db1", db1Cfg), http.StatusCreated)
1700+
1701+
dataStore2, err := tb.GetNamedDataStore(1)
1702+
require.NoError(t, err)
1703+
db2Cfg := rt.NewDbConfig()
1704+
db2Cfg.UseSystemMobileMetadataCollection = base.Ptr(false)
1705+
db2Cfg.Scopes = rest.ScopesConfig{
1706+
dataStore2.ScopeName(): rest.ScopeConfig{
1707+
Collections: rest.CollectionsConfig{dataStore2.CollectionName(): {}},
1708+
},
1709+
}
1710+
rest.RequireStatus(t, rt.CreateDatabase("db2", db2Cfg), http.StatusCreated)
1711+
1712+
// Opt in for db1 and start its migration. This stamps the bucket-level status doc with
1713+
// Bootstrap.State == pending. The bucket cannot complete yet because db2 has not opted in.
1714+
db1Cfg.UseSystemMobileMetadataCollection = base.Ptr(true)
1715+
rest.RequireStatus(t, rt.UpsertDbConfig("db1", db1Cfg), http.StatusCreated)
1716+
rt.ServerContext().ForceClusterCompatRefresh(t, rt.Context())
1717+
resp := rt.SendAdminRequest(http.MethodPost, "/db1/_metadata_migration?action=start", "")
1718+
rest.RequireStatus(t, resp, http.StatusOK)
1719+
1720+
rt.WaitForMetadataMigrationStatusForDB(db.BackgroundProcessStateCompleted, "db1")
1721+
1722+
conn := rt.ServerContext().BootstrapContext.Connection
1723+
1724+
// Sanity check: with db2 still present and un-migrated, the bucket bootstrap migration is pending.
1725+
status, _, err := conn.GetMetadataMigrationStatus(ctx, tb.GetName())
1726+
require.NoError(t, err)
1727+
require.NotNil(t, status)
1728+
require.Equal(t, base.MigrationStatePending, status.Bootstrap.State, "precondition: bucket migration should be pending while db2 is un-migrated")
1729+
1730+
// Delete every database in the bucket — both the migrated db1 and the un-migrated db2 — so the
1731+
// registry has no live entries left.
1732+
rest.RequireStatus(t, rt.SendAdminRequest(http.MethodDelete, "/db2/", ""), http.StatusOK)
1733+
rest.RequireStatus(t, rt.SendAdminRequest(http.MethodDelete, "/db1/", ""), http.StatusOK)
1734+
1735+
// Re-run the recheck on every tick (mirroring the config-polling cadence) and expect the bucket
1736+
// migration to converge to complete now that nothing blocks it. It never does — this assertion
1737+
// fails on the current code because maybeComplete bails at its len(expected) == 0 guard.
1738+
require.EventuallyWithT(rt.TB(), func(c *assert.CollectT) {
1739+
rt.ServerContext().RecheckPendingBucketMetadataMigrations(ctx)
1740+
status, _, err := conn.GetMetadataMigrationStatus(ctx, tb.GetName())
1741+
if !assert.NoError(c, err) {
1742+
return
1743+
}
1744+
if !assert.NotNil(c, status) {
1745+
return
1746+
}
1747+
assert.Equal(c, base.MigrationStateComplete, status.Bootstrap.State, "deleting all dbs should let the pending bucket migration complete")
1748+
}, 5*time.Second, 100*time.Millisecond)
1749+
}

rest/server_context.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,11 +2409,7 @@ func (sc *ServerContext) RecheckPendingBucketMetadataMigrations(ctx context.Cont
24092409
if sc.BootstrapContext == nil || sc.BootstrapContext.Connection == nil {
24102410
return
24112411
}
2412-
buckets, err := sc.BootstrapContext.Connection.GetConfigBuckets(ctx)
2413-
if err != nil {
2414-
base.WarnfCtx(ctx, "Couldn't list buckets to re-check bucket metadata migration: %v", err)
2415-
return
2416-
}
2412+
buckets := sc.ClusterCompat.trackedBucketList()
24172413
for _, bucket := range buckets {
24182414
if sc.BootstrapContext.Connection.IsMigrationComplete(bucket) {
24192415
// migration done for this bucket, fast path skip to next bucket
@@ -2529,10 +2525,6 @@ func (sc *ServerContext) maybeCompleteBucketMetadataMigration(ctx context.Contex
25292525
return fmt.Errorf("read registry for bucket %q: %w", bucketName, err)
25302526
}
25312527
expected := registryMetadataIDs(registry)
2532-
if len(expected) == 0 {
2533-
// No DBs in registry → nothing to migrate
2534-
return nil
2535-
}
25362528

25372529
status, _, err := sc.BootstrapContext.Connection.GetMetadataMigrationStatus(ctx, bucketName)
25382530
if err != nil {

0 commit comments

Comments
 (0)