From c2d5644a38d0745b0f63b66f6fb8edb061f9c670 Mon Sep 17 00:00:00 2001 From: James Blair Date: Sat, 28 Oct 2023 09:30:37 +1300 Subject: [PATCH] Backport clientv3: remove v3.WithFirstKey() in Barrier.Wait() fix the unexpected blocking when using Barrier.Wait(), e.g. NewBarrier(client, a).Wait() will block if key a is not existed but a0 is existed, but it should return immediately. Signed-off-by: James Blair --- contrib/recipes/barrier.go | 2 +- integration/v3_barrier_test.go | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/contrib/recipes/barrier.go b/contrib/recipes/barrier.go index 5ab4817c7..38b51129c 100644 --- a/contrib/recipes/barrier.go +++ b/contrib/recipes/barrier.go @@ -49,7 +49,7 @@ func (b *Barrier) Release() error { // Wait blocks on the barrier key until it is deleted. If there is no key, Wait // assumes Release has already been called and returns immediately. func (b *Barrier) Wait() error { - resp, err := b.client.Get(b.ctx, b.key, v3.WithFirstKey()...) + resp, err := b.client.Get(b.ctx, b.key) if err != nil { return err } diff --git a/integration/v3_barrier_test.go b/integration/v3_barrier_test.go index 1fbc78b3c..4f1f1fda9 100644 --- a/integration/v3_barrier_test.go +++ b/integration/v3_barrier_test.go @@ -76,3 +76,42 @@ func testBarrier(t *testing.T, waiters int, chooseClient func() *clientv3.Client } } } + +func TestBarrierWaitNonexistentKey(t *testing.T) { + defer testutil.AfterTest(t) + clus := NewClusterV3(t, &ClusterConfig{Size: 1}) + defer clus.Terminate(t) + cli := clus.clients[0] + + if _, err := cli.Put(cli.Ctx(), "test-barrier-0", ""); err != nil { + t.Errorf("could not put test-barrier0, err:%v", err) + } + + donec := make(chan struct{}) + stopc := make(chan struct{}) + defer close(stopc) + + waiters := 5 + for i := 0; i < waiters; i++ { + go func() { + br := recipe.NewBarrier(cli, "test-barrier") + if err := br.Wait(); err != nil { + t.Errorf("could not wait on barrier (%v)", err) + } + select { + case donec <- struct{}{}: + case <-stopc: + } + }() + } + + // all waiters should return immediately if waiting on a nonexistent key "test-barrier" even if key "test-barrier-0" exists + timerC := time.After(time.Duration(waiters*100) * time.Millisecond) + for i := 0; i < waiters; i++ { + select { + case <-timerC: + t.Fatal("barrier timed out") + case <-donec: + } + } +}