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.

132 lines
3.3 KiB

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