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.

143 lines
3.7 KiB

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