@@ -17,6 +17,8 @@ package owner_test
17
17
import (
18
18
"context"
19
19
"fmt"
20
+ "math/rand"
21
+ "net/url"
20
22
"runtime"
21
23
"testing"
22
24
"time"
@@ -30,10 +32,12 @@ import (
30
32
"github.com/pingcap/tidb/pkg/parser/terror"
31
33
"github.com/pingcap/tidb/pkg/store/mockstore"
32
34
"github.com/pingcap/tidb/pkg/testkit"
35
+ "github.com/pingcap/tidb/pkg/util"
33
36
"github.com/pingcap/tidb/pkg/util/logutil"
34
37
"github.com/stretchr/testify/require"
35
38
clientv3 "go.etcd.io/etcd/client/v3"
36
39
"go.etcd.io/etcd/client/v3/concurrency"
40
+ "go.etcd.io/etcd/server/v3/embed"
37
41
"go.etcd.io/etcd/tests/v3/integration"
38
42
)
39
43
@@ -413,3 +417,108 @@ func deleteLeader(cli *clientv3.Client, prefixKey string) error {
413
417
_ , err = cli .Delete (context .Background (), string (resp .Kvs [0 ].Key ))
414
418
return errors .Trace (err )
415
419
}
420
+
421
+ func TestAcquireDistributedLock (t * testing.T ) {
422
+ const addrFmt = "http://127.0.0.1:%d"
423
+ cfg := embed .NewConfig ()
424
+ cfg .Dir = t .TempDir ()
425
+ // rand port in [20000, 60000)
426
+ randPort := int (rand .Int31n (40000 )) + 20000
427
+ clientAddr := fmt .Sprintf (addrFmt , randPort )
428
+ lcurl , _ := url .Parse (clientAddr )
429
+ cfg .ListenClientUrls , cfg .AdvertiseClientUrls = []url.URL {* lcurl }, []url.URL {* lcurl }
430
+ lpurl , _ := url .Parse (fmt .Sprintf (addrFmt , randPort + 1 ))
431
+ cfg .ListenPeerUrls , cfg .AdvertisePeerUrls = []url.URL {* lpurl }, []url.URL {* lpurl }
432
+ cfg .InitialCluster = "default=" + lpurl .String ()
433
+ cfg .Logger = "zap"
434
+ embedEtcd , err := embed .StartEtcd (cfg )
435
+ require .NoError (t , err )
436
+ <- embedEtcd .Server .ReadyNotify ()
437
+ t .Cleanup (func () {
438
+ embedEtcd .Close ()
439
+ })
440
+ makeEtcdCli := func (t * testing.T ) (cli * clientv3.Client ) {
441
+ cli , err := clientv3 .New (clientv3.Config {
442
+ Endpoints : []string {lcurl .String ()},
443
+ })
444
+ require .NoError (t , err )
445
+ t .Cleanup (func () {
446
+ cli .Close ()
447
+ })
448
+ return cli
449
+ }
450
+ t .Run ("acquire distributed lock with same client" , func (t * testing.T ) {
451
+ cli := makeEtcdCli (t )
452
+ getLock := make (chan struct {})
453
+ ctx := context .Background ()
454
+
455
+ release1 , err := owner .AcquireDistributedLock (ctx , cli , "test-lock" , 10 )
456
+ require .NoError (t , err )
457
+ var wg util.WaitGroupWrapper
458
+ wg .Run (func () {
459
+ // Acquire another distributed lock will be blocked.
460
+ release2 , err := owner .AcquireDistributedLock (ctx , cli , "test-lock" , 10 )
461
+ require .NoError (t , err )
462
+ getLock <- struct {}{}
463
+ release2 ()
464
+ })
465
+ timer := time .NewTimer (300 * time .Millisecond )
466
+ select {
467
+ case <- getLock :
468
+ require .FailNow (t , "acquired same lock unexpectedly" )
469
+ case <- timer .C :
470
+ release1 ()
471
+ <- getLock
472
+ }
473
+ wg .Wait ()
474
+
475
+ release1 , err = owner .AcquireDistributedLock (ctx , cli , "test-lock/1" , 10 )
476
+ require .NoError (t , err )
477
+ release2 , err := owner .AcquireDistributedLock (ctx , cli , "test-lock/2" , 10 )
478
+ require .NoError (t , err )
479
+ release1 ()
480
+ release2 ()
481
+ })
482
+
483
+ t .Run ("acquire distributed lock with different clients" , func (t * testing.T ) {
484
+ cli1 := makeEtcdCli (t )
485
+ cli2 := makeEtcdCli (t )
486
+
487
+ getLock := make (chan struct {})
488
+ ctx := context .Background ()
489
+
490
+ release1 , err := owner .AcquireDistributedLock (ctx , cli1 , "test-lock" , 10 )
491
+ require .NoError (t , err )
492
+ var wg util.WaitGroupWrapper
493
+ wg .Run (func () {
494
+ // Acquire another distributed lock will be blocked.
495
+ release2 , err := owner .AcquireDistributedLock (ctx , cli2 , "test-lock" , 10 )
496
+ require .NoError (t , err )
497
+ getLock <- struct {}{}
498
+ release2 ()
499
+ })
500
+ timer := time .NewTimer (300 * time .Millisecond )
501
+ select {
502
+ case <- getLock :
503
+ require .FailNow (t , "acquired same lock unexpectedly" )
504
+ case <- timer .C :
505
+ release1 ()
506
+ <- getLock
507
+ }
508
+ wg .Wait ()
509
+ })
510
+
511
+ t .Run ("acquire distributed lock until timeout" , func (t * testing.T ) {
512
+ cli1 := makeEtcdCli (t )
513
+ cli2 := makeEtcdCli (t )
514
+ ctx := context .Background ()
515
+
516
+ _ , err := owner .AcquireDistributedLock (ctx , cli1 , "test-lock" , 1 )
517
+ require .NoError (t , err )
518
+ cli1 .Close () // Note that release() is not invoked.
519
+
520
+ release2 , err := owner .AcquireDistributedLock (ctx , cli2 , "test-lock" , 10 )
521
+ require .NoError (t , err )
522
+ release2 ()
523
+ })
524
+ }
0 commit comments