From cd08e2b0565d1c1c9090ebd161cd971dbafed3fb Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 8 Jan 2026 02:03:44 +0100 Subject: [PATCH] fix(test): fix flaky TestAddMultipleGCLive race condition added time.Sleep after spawning GC goroutines to ensure they reach GCLock() before the test proceeds. without this, the adder's maybePauseForGC() might check GCRequested() before GC has even requested the lock, causing the lock to not be released and GC to block indefinitely. this matches the existing pattern in TestAddGCLive which already had this sleep. also replaced context.Background() with t.Context() in both TestAddMultipleGCLive and TestAddGCLive for proper test lifecycle management. --- core/coreunix/add_test.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/core/coreunix/add_test.go b/core/coreunix/add_test.go index a11dd13e4..d5b06176a 100644 --- a/core/coreunix/add_test.go +++ b/core/coreunix/add_test.go @@ -30,6 +30,7 @@ import ( const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" func TestAddMultipleGCLive(t *testing.T) { + ctx := t.Context() r := &repo.Mock{ C: config.Config{ Identity: config.Identity{ @@ -38,13 +39,13 @@ func TestAddMultipleGCLive(t *testing.T) { }, D: syncds.MutexWrap(datastore.NewMapDatastore()), } - node, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r}) + node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) if err != nil { t.Fatal(err) } out := make(chan interface{}, 10) - adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG) + adder, err := NewAdder(ctx, node.Pinning, node.Blockstore, node.DAG) if err != nil { t.Fatal(err) } @@ -67,7 +68,7 @@ func TestAddMultipleGCLive(t *testing.T) { go func() { defer close(out) - _, _ = adder.AddAllAndPin(context.Background(), slf) + _, _ = adder.AddAllAndPin(ctx, slf) // Ignore errors for clarity - the real bug would be gc'ing files while adding them, not this resultant error }() @@ -80,9 +81,12 @@ func TestAddMultipleGCLive(t *testing.T) { gc1started := make(chan struct{}) go func() { defer close(gc1started) - gc1out = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gc1out = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() + // Give GC goroutine time to reach GCLock (will block there waiting for adder) + time.Sleep(time.Millisecond * 100) + // GC shouldn't get the lock until after the file is completely added select { case <-gc1started: @@ -119,9 +123,12 @@ func TestAddMultipleGCLive(t *testing.T) { gc2started := make(chan struct{}) go func() { defer close(gc2started) - gc2out = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gc2out = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() + // Give GC goroutine time to reach GCLock + time.Sleep(time.Millisecond * 100) + select { case <-gc2started: t.Fatal("gc shouldn't have started yet") @@ -155,6 +162,7 @@ func TestAddMultipleGCLive(t *testing.T) { } func TestAddGCLive(t *testing.T) { + ctx := t.Context() r := &repo.Mock{ C: config.Config{ Identity: config.Identity{ @@ -163,13 +171,13 @@ func TestAddGCLive(t *testing.T) { }, D: syncds.MutexWrap(datastore.NewMapDatastore()), } - node, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r}) + node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) if err != nil { t.Fatal(err) } out := make(chan interface{}) - adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG) + adder, err := NewAdder(ctx, node.Pinning, node.Blockstore, node.DAG) if err != nil { t.Fatal(err) } @@ -193,7 +201,7 @@ func TestAddGCLive(t *testing.T) { go func() { defer close(addDone) defer close(out) - _, err := adder.AddAllAndPin(context.Background(), slf) + _, err := adder.AddAllAndPin(ctx, slf) if err != nil { t.Error(err) } @@ -211,7 +219,7 @@ func TestAddGCLive(t *testing.T) { gcstarted := make(chan struct{}) go func() { defer close(gcstarted) - gcout = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gcout = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() // gc shouldn't start until we let the add finish its current file. @@ -255,9 +263,6 @@ func TestAddGCLive(t *testing.T) { last = c } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - set := cid.NewSet() err = dag.Walk(ctx, dag.GetLinksWithDAG(node.DAG), last, set.Visit) if err != nil {