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.

107 lines
2.9 KiB

  1. package weed_server
  2. import (
  3. "context"
  4. "math/rand"
  5. "time"
  6. "github.com/chrislusf/seaweedfs/weed/pb/master_pb"
  7. )
  8. /*
  9. How exclusive lock works?
  10. -----------
  11. Shell
  12. ------
  13. When shell lock,
  14. * lease an admin token (lockTime, token)
  15. * start a goroutine to renew the admin token periodically
  16. For later volume operations, send (lockTime, token) to volume servers for exclusive access
  17. * need to pause renewal a few seconds, to prevent race condition
  18. When shell unlock
  19. * stop the renewal goroutine
  20. * sends a release lock request
  21. Master
  22. ------
  23. Master maintains:
  24. * randomNumber
  25. * lastLockTime
  26. When master receives the lease/renew request from shell
  27. If lastLockTime still fresh {
  28. if is a renew and token is valid {
  29. // for renew
  30. generate the randomNumber => token
  31. return
  32. }
  33. refuse
  34. return
  35. } else {
  36. // for fresh lease request
  37. generate the randomNumber => token
  38. return
  39. }
  40. When master receives the release lock request from shell
  41. set the lastLockTime to zero
  42. When master receives the verfication request from volume servers
  43. return secret+lockTime == token && lockTime == lastLockTime
  44. Volume
  45. ------
  46. When receives (lockTime, token), ask the master whether this is valid
  47. */
  48. const (
  49. LockDuration = 10 * time.Second
  50. )
  51. func (ms *MasterServer) LeaseAdminToken(ctx context.Context, req *master_pb.LeaseAdminTokenRequest) (*master_pb.LeaseAdminTokenResponse, error) {
  52. resp := &master_pb.LeaseAdminTokenResponse{}
  53. if ms.adminAccessLockTime.Add(LockDuration).After(time.Now()) {
  54. if req.PreviousToken != 0 && ms.isValidToken(time.Unix(0, req.PreviousLockTime), req.PreviousToken) {
  55. // for renew
  56. ts, token := ms.generateToken()
  57. resp.Token, resp.LockTsNs = token, ts.UnixNano()
  58. return resp, nil
  59. }
  60. // refuse since still locked
  61. resp.Error = "already locked"
  62. return resp, nil
  63. }
  64. // for fresh lease request
  65. ts, token := ms.generateToken()
  66. resp.Token, resp.LockTsNs = token, ts.UnixNano()
  67. return resp, nil
  68. }
  69. func (ms *MasterServer) isValidToken(ts time.Time, token int64) bool {
  70. return ms.adminAccessLockTime.Equal(ts) && ms.adminAccessSecret == token
  71. }
  72. func (ms *MasterServer) generateToken() (ts time.Time, token int64) {
  73. ms.adminAccessLockTime = time.Now()
  74. ms.adminAccessSecret = rand.Int63()
  75. return ms.adminAccessLockTime, ms.adminAccessSecret
  76. }
  77. func (ms *MasterServer) ReleaseAdminToken(ctx context.Context, req *master_pb.ReleaseAdminTokenRequest) (*master_pb.ReleaseAdminTokenResponse, error) {
  78. resp := &master_pb.ReleaseAdminTokenResponse{}
  79. if ms.isValidToken(time.Unix(0, req.PreviousLockTime), req.PreviousToken) {
  80. ms.adminAccessSecret = 0
  81. }
  82. return resp, nil
  83. }
  84. func (ms *MasterServer) VerifyAdminToken(ctx context.Context, req *master_pb.VerifyAdminTokenRequest) (*master_pb.VerifyAdminTokenResponse, error) {
  85. resp := &master_pb.VerifyAdminTokenResponse{}
  86. if ms.isValidToken(time.Unix(0, req.LockTime), req.Token) {
  87. resp.IsValid = true
  88. }
  89. return resp, nil
  90. }