Skip to content

Commit bf6beab

Browse files
committed
fix early return case
1 parent fada14f commit bf6beab

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

rest/metadatamigrationtest/metadata_migration_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,3 +1602,76 @@ func TestDeleteNonMigratedDbUnblocksBootstrapMigration(t *testing.T) {
16021602
assert.Equal(c, base.MigrationStateComplete, status.Bootstrap.State, "bucket bootstrap migration should be complete")
16031603
}, 10*time.Second, 100*time.Millisecond)
16041604
}
1605+
1606+
// TestRecheckConvergesLocalCacheOnPeerCompletedMigration ensures when a node completes the bucket bootstrap migration,
1607+
// this node's status doc reads complete but its local "migration complete" cache (IsMigrationComplete) is updated too.
1608+
// RecheckPendingBucketMetadataMigrations should converge that cache so the node stops falling back
1609+
// 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.
1614+
func TestRecheckConvergesLocalCacheOnPeerCompletedMigration(t *testing.T) {
1615+
base.TestRequiresCollections(t)
1616+
1617+
ctx := base.TestCtx(t)
1618+
tb := base.GetTestBucket(t)
1619+
defer tb.Close(ctx)
1620+
1621+
dataStore1, err := tb.GetNamedDataStore(0)
1622+
require.NoError(t, err)
1623+
rt := rest.NewRestTester(t, &rest.RestTesterConfig{
1624+
CustomTestBucket: tb.NoCloseClone(),
1625+
PersistentConfig: true,
1626+
})
1627+
defer rt.Close()
1628+
1629+
db1Cfg := rt.NewDbConfig()
1630+
db1Cfg.UseSystemMobileMetadataCollection = base.Ptr(false)
1631+
db1Cfg.Scopes = rest.ScopesConfig{
1632+
dataStore1.ScopeName(): rest.ScopeConfig{
1633+
Collections: rest.CollectionsConfig{dataStore1.CollectionName(): {}},
1634+
},
1635+
}
1636+
rest.RequireStatus(t, rt.CreateDatabase("db1", db1Cfg), http.StatusCreated)
1637+
1638+
dataStore2, err := tb.GetNamedDataStore(1)
1639+
require.NoError(t, err)
1640+
db2Cfg := rt.NewDbConfig()
1641+
db2Cfg.UseSystemMobileMetadataCollection = base.Ptr(false)
1642+
db2Cfg.Scopes = rest.ScopesConfig{
1643+
dataStore2.ScopeName(): rest.ScopeConfig{
1644+
Collections: rest.CollectionsConfig{dataStore2.CollectionName(): {}},
1645+
},
1646+
}
1647+
rest.RequireStatus(t, rt.CreateDatabase("db2", db2Cfg), http.StatusCreated)
1648+
1649+
// Opt in for db1 and run its migration. db2 stays un-migrated, so this node never completes the
1650+
// bucket migration on its own — its local IsMigrationComplete cache stays false. (db2 also
1651+
// guarantees the recheck can't complete the bucket itself, isolating the test to the cache
1652+
// convergence path.)
1653+
db1Cfg.UseSystemMobileMetadataCollection = base.Ptr(true)
1654+
rest.RequireStatus(t, rt.UpsertDbConfig("db1", db1Cfg), http.StatusCreated)
1655+
rt.ServerContext().ForceClusterCompatRefresh(t, rt.Context())
1656+
resp := rt.SendAdminRequest(http.MethodPost, "/db1/_metadata_migration?action=start", "")
1657+
rest.RequireStatus(t, resp, http.StatusOK)
1658+
rt.WaitForMetadataMigrationStatusForDB(db.BackgroundProcessStateCompleted, "db1")
1659+
1660+
conn := rt.ServerContext().BootstrapContext.Connection
1661+
1662+
// Simulate a peer node winning the completion: stamp the bucket status doc to complete directly.
1663+
// UpdateMetadataMigrationStatus only writes the doc — it does not touch this node's local cache,
1664+
// so IsMigrationComplete is still false afterwards, exactly as it would be on a peer that didn't
1665+
// run the completion itself.
1666+
_, err = conn.UpdateMetadataMigrationStatus(ctx, tb.GetName(), func(s *base.MetadataMigrationStatus) error {
1667+
s.Bootstrap.State = base.MigrationStateComplete
1668+
return nil
1669+
})
1670+
require.NoError(t, err)
1671+
require.False(t, conn.IsMigrationComplete(tb.GetName()), "precondition: local node should not have marked migration complete yet")
1672+
1673+
// The recheck observes the completed status doc; it should converge the local cache.
1674+
rt.ServerContext().RecheckPendingBucketMetadataMigrations(ctx)
1675+
1676+
require.True(t, conn.IsMigrationComplete(tb.GetName()), "finding 2: recheck observing a peer-completed status doc should converge the local migration-complete cache")
1677+
}

rest/server_context.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2415,7 +2415,7 @@ func (sc *ServerContext) RecheckPendingBucketMetadataMigrations(ctx context.Cont
24152415
return
24162416
}
24172417
for _, bucket := range buckets {
2418-
if done := sc.BootstrapContext.Connection.IsMigrationComplete(bucket); done {
2418+
if sc.BootstrapContext.Connection.IsMigrationComplete(bucket) {
24192419
// migration done for this bucket, fast path skip to next bucket
24202420
continue
24212421
}
@@ -2539,6 +2539,7 @@ func (sc *ServerContext) maybeCompleteBucketMetadataMigration(ctx context.Contex
25392539
return fmt.Errorf("read status doc for bucket %q: %w", bucketName, err)
25402540
}
25412541
if status.Bootstrap.State == base.MigrationStateComplete {
2542+
sc.BootstrapContext.Connection.SetMigrationComplete(bucketName)
25422543
return nil
25432544
}
25442545
if !status.AllDatabasesComplete(expected) {

0 commit comments

Comments
 (0)