clientv3: fix the design & implementation of double barrier

Check the client count before creating the ephemeral key, do not
create the key if there are already too many clients. Check the
count after creating the key again, if the total kvs is bigger
than the expected count, then check the rev of the current key,
and take action accordingly based on its rev. If its rev is in
the first ${count}, then it's valid client, otherwise, it should
fail.

Signed-off-by: Benjamin Wang <wachao@vmware.com>
This commit is contained in:
Benjamin Wang
2022-10-20 15:52:08 +08:00
parent 0a0f0e3617
commit 8e26a1fff1
2 changed files with 109 additions and 10 deletions

View File

@@ -15,9 +15,14 @@
package recipes_test
import (
"context"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
recipe "go.etcd.io/etcd/client/v3/experimental/recipes"
"go.etcd.io/etcd/tests/v3/integration"
@@ -97,6 +102,67 @@ func TestDoubleBarrier(t *testing.T) {
}
}
func TestDoubleBarrierTooManyClients(t *testing.T) {
integration.BeforeTest(t)
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
defer clus.Terminate(t)
waiters := 10
session, err := concurrency.NewSession(clus.RandClient())
if err != nil {
t.Error(err)
}
defer session.Orphan()
b := recipe.NewDoubleBarrier(session, "test-barrier", waiters)
donec := make(chan struct{})
var (
wgDone sync.WaitGroup // make sure all clients have finished the tasks
wgEntered sync.WaitGroup // make sure all clients have entered the double barrier
)
wgDone.Add(waiters)
wgEntered.Add(waiters)
for i := 0; i < waiters; i++ {
go func() {
defer wgDone.Done()
session, err := concurrency.NewSession(clus.RandClient())
if err != nil {
t.Error(err)
}
defer session.Orphan()
bb := recipe.NewDoubleBarrier(session, "test-barrier", waiters)
if err := bb.Enter(); err != nil {
t.Errorf("could not enter on barrier (%v)", err)
}
wgEntered.Done()
<-donec
if err := bb.Leave(); err != nil {
t.Errorf("could not leave on barrier (%v)", err)
}
}()
}
// Wait until all clients have already entered the double barrier, so
// no any other client can enter the barrier.
wgEntered.Wait()
t.Log("Try to enter into double barrier")
if err := b.Enter(); err != recipe.ErrTooManyClients {
t.Errorf("Unexcepted error, expected: ErrTooManyClients, got: %v", err)
}
resp, err := clus.RandClient().Get(context.TODO(), "test-barrier/waiters", clientv3.WithPrefix())
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Make sure the extra `b.Enter()` did not create a new ephemeral key
assert.Equal(t, waiters, len(resp.Kvs))
close(donec)
wgDone.Wait()
}
func TestDoubleBarrierFailover(t *testing.T) {
integration.BeforeTest(t)