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.

93 lines
2.4 KiB

  1. package weed_server
  2. import (
  3. "context"
  4. "fmt"
  5. "math/rand"
  6. "time"
  7. "github.com/chrislusf/seaweedfs/weed/pb/master_pb"
  8. )
  9. /*
  10. How exclusive lock works?
  11. -----------
  12. Shell
  13. ------
  14. When shell lock,
  15. * lease an admin token (lockTime, token)
  16. * start a goroutine to renew the admin token periodically
  17. When shell unlock
  18. * stop the renewal goroutine
  19. * sends a release lock request
  20. Master
  21. ------
  22. Master maintains:
  23. * randomNumber
  24. * lastLockTime
  25. When master receives the lease/renew request from shell
  26. If lastLockTime still fresh {
  27. if is a renew and token is valid {
  28. // for renew
  29. generate the randomNumber => token
  30. return
  31. }
  32. refuse
  33. return
  34. } else {
  35. // for fresh lease request
  36. generate the randomNumber => token
  37. return
  38. }
  39. When master receives the release lock request from shell
  40. set the lastLockTime to zero
  41. The volume server does not need to verify.
  42. This makes the lock/unlock optional, similar to what golang code usually does.
  43. */
  44. const (
  45. LockDuration = 10 * time.Second
  46. )
  47. func (ms *MasterServer) LeaseAdminToken(ctx context.Context, req *master_pb.LeaseAdminTokenRequest) (*master_pb.LeaseAdminTokenResponse, error) {
  48. resp := &master_pb.LeaseAdminTokenResponse{}
  49. if ms.adminAccessSecret != 0 && ms.adminAccessLockTime.Add(LockDuration).After(time.Now()) {
  50. if req.PreviousToken != 0 && ms.isValidToken(time.Unix(0, req.PreviousLockTime), req.PreviousToken) {
  51. // for renew
  52. ts, token := ms.generateToken()
  53. resp.Token, resp.LockTsNs = token, ts.UnixNano()
  54. return resp, nil
  55. }
  56. // refuse since still locked
  57. return resp, fmt.Errorf("already locked")
  58. }
  59. // for fresh lease request
  60. ts, token := ms.generateToken()
  61. resp.Token, resp.LockTsNs = token, ts.UnixNano()
  62. return resp, nil
  63. }
  64. func (ms *MasterServer) isValidToken(ts time.Time, token int64) bool {
  65. return ms.adminAccessLockTime.Equal(ts) && ms.adminAccessSecret == token
  66. }
  67. func (ms *MasterServer) generateToken() (ts time.Time, token int64) {
  68. ms.adminAccessLockTime = time.Now()
  69. ms.adminAccessSecret = rand.Int63()
  70. return ms.adminAccessLockTime, ms.adminAccessSecret
  71. }
  72. func (ms *MasterServer) ReleaseAdminToken(ctx context.Context, req *master_pb.ReleaseAdminTokenRequest) (*master_pb.ReleaseAdminTokenResponse, error) {
  73. resp := &master_pb.ReleaseAdminTokenResponse{}
  74. if ms.isValidToken(time.Unix(0, req.PreviousLockTime), req.PreviousToken) {
  75. ms.adminAccessSecret = 0
  76. }
  77. return resp, nil
  78. }