You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.1 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. package exclusive_locks
  2. import (
  3. "context"
  4. "sync/atomic"
  5. "time"
  6. "github.com/chrislusf/seaweedfs/weed/glog"
  7. "github.com/chrislusf/seaweedfs/weed/pb/master_pb"
  8. "github.com/chrislusf/seaweedfs/weed/wdclient"
  9. )
  10. const (
  11. RenewInteval = 4 * time.Second
  12. SafeRenewInteval = 3 * time.Second
  13. InitLockInteval = 1 * time.Second
  14. AdminLockName = "admin"
  15. )
  16. type ExclusiveLocker struct {
  17. token int64
  18. lockTsNs int64
  19. isLocking bool
  20. masterClient *wdclient.MasterClient
  21. }
  22. func NewExclusiveLocker(masterClient *wdclient.MasterClient) *ExclusiveLocker {
  23. return &ExclusiveLocker{
  24. masterClient: masterClient,
  25. }
  26. }
  27. func (l *ExclusiveLocker) IsLocking() bool {
  28. return l.isLocking
  29. }
  30. func (l *ExclusiveLocker) GetToken() (token int64, lockTsNs int64) {
  31. for time.Unix(0, atomic.LoadInt64(&l.lockTsNs)).Add(SafeRenewInteval).Before(time.Now()) {
  32. // wait until now is within the safe lock period, no immediate renewal to change the token
  33. time.Sleep(100 * time.Millisecond)
  34. }
  35. return atomic.LoadInt64(&l.token), atomic.LoadInt64(&l.lockTsNs)
  36. }
  37. func (l *ExclusiveLocker) RequestLock(clientName string) {
  38. if l.isLocking {
  39. return
  40. }
  41. ctx, cancel := context.WithCancel(context.Background())
  42. defer cancel()
  43. // retry to get the lease
  44. for {
  45. if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
  46. resp, err := client.LeaseAdminToken(ctx, &master_pb.LeaseAdminTokenRequest{
  47. PreviousToken: atomic.LoadInt64(&l.token),
  48. PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
  49. LockName: AdminLockName,
  50. ClientName: clientName,
  51. })
  52. if err == nil {
  53. atomic.StoreInt64(&l.token, resp.Token)
  54. atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
  55. }
  56. return err
  57. }); err != nil {
  58. println("lock:", err.Error())
  59. time.Sleep(InitLockInteval)
  60. } else {
  61. break
  62. }
  63. }
  64. l.isLocking = true
  65. // start a goroutine to renew the lease
  66. go func() {
  67. ctx2, cancel2 := context.WithCancel(context.Background())
  68. defer cancel2()
  69. for l.isLocking {
  70. if err := l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
  71. resp, err := client.LeaseAdminToken(ctx2, &master_pb.LeaseAdminTokenRequest{
  72. PreviousToken: atomic.LoadInt64(&l.token),
  73. PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
  74. LockName: AdminLockName,
  75. ClientName: clientName,
  76. })
  77. if err == nil {
  78. atomic.StoreInt64(&l.token, resp.Token)
  79. atomic.StoreInt64(&l.lockTsNs, resp.LockTsNs)
  80. // println("ts", l.lockTsNs, "token", l.token)
  81. }
  82. return err
  83. }); err != nil {
  84. glog.Errorf("failed to renew lock: %v", err)
  85. return
  86. } else {
  87. time.Sleep(RenewInteval)
  88. }
  89. }
  90. }()
  91. }
  92. func (l *ExclusiveLocker) ReleaseLock() {
  93. l.isLocking = false
  94. ctx, cancel := context.WithCancel(context.Background())
  95. defer cancel()
  96. l.masterClient.WithClient(func(client master_pb.SeaweedClient) error {
  97. client.ReleaseAdminToken(ctx, &master_pb.ReleaseAdminTokenRequest{
  98. PreviousToken: atomic.LoadInt64(&l.token),
  99. PreviousLockTime: atomic.LoadInt64(&l.lockTsNs),
  100. LockName: AdminLockName,
  101. })
  102. return nil
  103. })
  104. atomic.StoreInt64(&l.token, 0)
  105. atomic.StoreInt64(&l.lockTsNs, 0)
  106. }