| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2019 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/dsync" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// Lock maintenance interval.
 | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	lockMaintenanceInterval = 1 * time.Minute | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Lock validity check interval.
 | 
					
						
							|  |  |  | 	lockValidityCheckInterval = 2 * time.Minute | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // To abstract a node over network.
 | 
					
						
							|  |  |  | type lockRESTServer struct { | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	ll *localLocker | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *lockRESTServer) writeErrorResponse(w http.ResponseWriter, err error) { | 
					
						
							|  |  |  | 	w.WriteHeader(http.StatusForbidden) | 
					
						
							|  |  |  | 	w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsValid - To authenticate and verify the time difference.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool { | 
					
						
							|  |  |  | 	if err := storageServerRequestValidate(r); err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | func getLockArgs(r *http.Request) (args dsync.LockArgs, err error) { | 
					
						
							|  |  |  | 	args = dsync.LockArgs{ | 
					
						
							|  |  |  | 		UID:    r.URL.Query().Get(lockRESTUID), | 
					
						
							|  |  |  | 		Source: r.URL.Query().Get(lockRESTSource), | 
					
						
							| 
									
										
										
										
											2019-08-06 02:45:30 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var resources []string | 
					
						
							|  |  |  | 	bio := bufio.NewScanner(r.Body) | 
					
						
							|  |  |  | 	for bio.Scan() { | 
					
						
							|  |  |  | 		resources = append(resources, bio.Text()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := bio.Err(); err != nil { | 
					
						
							|  |  |  | 		return args, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sort.Strings(resources) | 
					
						
							|  |  |  | 	args.Resources = resources | 
					
						
							|  |  |  | 	return args, nil | 
					
						
							| 
									
										
										
										
											2019-08-06 02:45:30 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | // LockHandler - Acquires a lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) LockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("Invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	success, err := l.ll.Lock(args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	if err == nil && !success { | 
					
						
							|  |  |  | 		err = errLockConflict | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnlockHandler - releases the acquired lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) UnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("Invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, err = l.ll.Unlock(args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	// Ignore the Unlock() "reply" return value because if err == nil, "reply" is always true
 | 
					
						
							|  |  |  | 	// Consequently, if err != nil, reply is always false
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LockHandler - Acquires an RLock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) RLockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("Invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	success, err := l.ll.RLock(args) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	if err == nil && !success { | 
					
						
							|  |  |  | 		err = errLockConflict | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlockHandler - releases the acquired read lock.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) RUnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("Invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	// Ignore the RUnlock() "reply" return value because if err == nil, "reply" is always true.
 | 
					
						
							|  |  |  | 	// Consequently, if err != nil, reply is always false
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	if _, err = l.ll.RUnlock(args); err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | // ExpiredHandler - query expired lock status.
 | 
					
						
							|  |  |  | func (l *lockRESTServer) ExpiredHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	if !l.IsValid(w, r) { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, errors.New("Invalid request")) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	args, err := getLockArgs(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.writeErrorResponse(w, err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	l.ll.mutex.Lock() | 
					
						
							|  |  |  | 	defer l.ll.mutex.Unlock() | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	// Lock found, proceed to verify if belongs to given uid.
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	for _, resource := range args.Resources { | 
					
						
							|  |  |  | 		if lri, ok := l.ll.lockMap[resource]; ok { | 
					
						
							|  |  |  | 			// Check whether uid is still active
 | 
					
						
							|  |  |  | 			for _, entry := range lri { | 
					
						
							|  |  |  | 				if entry.UID == args.UID { | 
					
						
							|  |  |  | 					l.writeErrorResponse(w, errLockNotExpired) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // nameLockRequesterInfoPair is a helper type for lock maintenance
 | 
					
						
							|  |  |  | type nameLockRequesterInfoPair struct { | 
					
						
							|  |  |  | 	name string | 
					
						
							|  |  |  | 	lri  lockRequesterInfo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getLongLivedLocks returns locks that are older than a certain time and
 | 
					
						
							|  |  |  | // have not been 'checked' for validity too soon enough
 | 
					
						
							|  |  |  | func getLongLivedLocks(interval time.Duration) map[Endpoint][]nameLockRequesterInfoPair { | 
					
						
							|  |  |  | 	nlripMap := make(map[Endpoint][]nameLockRequesterInfoPair) | 
					
						
							|  |  |  | 	for endpoint, locker := range globalLockServers { | 
					
						
							|  |  |  | 		rslt := []nameLockRequesterInfoPair{} | 
					
						
							|  |  |  | 		locker.mutex.Lock() | 
					
						
							|  |  |  | 		for name, lriArray := range locker.lockMap { | 
					
						
							|  |  |  | 			for idx := range lriArray { | 
					
						
							|  |  |  | 				// Check whether enough time has gone by since last check
 | 
					
						
							|  |  |  | 				if time.Since(lriArray[idx].TimeLastCheck) >= interval { | 
					
						
							|  |  |  | 					rslt = append(rslt, nameLockRequesterInfoPair{name: name, lri: lriArray[idx]}) | 
					
						
							|  |  |  | 					lriArray[idx].TimeLastCheck = UTCNow() | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		nlripMap[endpoint] = rslt | 
					
						
							|  |  |  | 		locker.mutex.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nlripMap | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // lockMaintenance loops over locks that have been active for some time and checks back
 | 
					
						
							|  |  |  | // with the original server whether it is still alive or not
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Following logic inside ignores the errors generated for Dsync.Active operation.
 | 
					
						
							|  |  |  | // - server at client down
 | 
					
						
							|  |  |  | // - some network error (and server is up normally)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // We will ignore the error, and we will retry later to get a resolve on this lock
 | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | func lockMaintenance(ctx context.Context, interval time.Duration) error { | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	// Validate if long lived locks are indeed clean.
 | 
					
						
							|  |  |  | 	// Get list of long lived locks to check for staleness.
 | 
					
						
							|  |  |  | 	for lendpoint, nlrips := range getLongLivedLocks(interval) { | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 		nlripsMap := make(map[string]int, len(nlrips)) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 		for _, nlrip := range nlrips { | 
					
						
							| 
									
										
										
										
											2020-03-05 08:06:17 +08:00
										 |  |  | 			// Locks are only held on first zone, make sure that
 | 
					
						
							|  |  |  | 			// we only look for ownership of locks from endpoints
 | 
					
						
							|  |  |  | 			// on first zone.
 | 
					
						
							|  |  |  | 			for _, endpoint := range globalEndpoints[0].Endpoints { | 
					
						
							|  |  |  | 				c := newLockAPI(endpoint) | 
					
						
							|  |  |  | 				if !c.IsOnline() { | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 					nlripsMap[nlrip.name]++ | 
					
						
							| 
									
										
										
										
											2020-03-05 08:06:17 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Call back to original server verify whether the lock is
 | 
					
						
							|  |  |  | 				// still active (based on name & uid)
 | 
					
						
							|  |  |  | 				expired, err := c.Expired(dsync.LockArgs{ | 
					
						
							|  |  |  | 					UID:       nlrip.lri.UID, | 
					
						
							|  |  |  | 					Resources: []string{nlrip.name}, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 					nlripsMap[nlrip.name]++ | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 					c.Close() | 
					
						
							| 
									
										
										
										
											2020-03-05 08:06:17 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 				if !expired { | 
					
						
							|  |  |  | 					nlripsMap[nlrip.name]++ | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-03-05 08:06:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// Close the connection regardless of the call response.
 | 
					
						
							|  |  |  | 				c.Close() | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Read locks we assume quorum for be N/2 success
 | 
					
						
							|  |  |  | 			quorum := globalXLSetDriveCount / 2 | 
					
						
							|  |  |  | 			if nlrip.lri.Writer { | 
					
						
							|  |  |  | 				// For write locks we need N/2+1 success
 | 
					
						
							|  |  |  | 				quorum = globalXLSetDriveCount/2 + 1 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// less than the quorum, we have locks expired.
 | 
					
						
							|  |  |  | 			if nlripsMap[nlrip.name] < quorum { | 
					
						
							|  |  |  | 				// The lock is no longer active at server that originated
 | 
					
						
							|  |  |  | 				// the lock, attempt to remove the lock.
 | 
					
						
							|  |  |  | 				globalLockServers[lendpoint].mutex.Lock() | 
					
						
							|  |  |  | 				// Purge the stale entry if it exists.
 | 
					
						
							|  |  |  | 				globalLockServers[lendpoint].removeEntryIfExists(nlrip) | 
					
						
							|  |  |  | 				globalLockServers[lendpoint].mutex.Unlock() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Start lock maintenance from all lock servers.
 | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | func startLockMaintenance(ctx context.Context) { | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	// Wait until the object API is ready
 | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 	// no need to start the lock maintenance
 | 
					
						
							|  |  |  | 	// if ObjectAPI is not initialized.
 | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 		objAPI := newObjectLayerWithoutSafeModeFn() | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 		if objAPI == nil { | 
					
						
							|  |  |  | 			time.Sleep(time.Second) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	// Initialize a new ticker with a minute between each ticks.
 | 
					
						
							|  |  |  | 	ticker := time.NewTicker(lockMaintenanceInterval) | 
					
						
							|  |  |  | 	// Stop the timer upon service closure and cleanup the go-routine.
 | 
					
						
							|  |  |  | 	defer ticker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 	r := rand.New(rand.NewSource(UTCNow().UnixNano())) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		// Verifies every minute for locks held more than 2 minutes.
 | 
					
						
							|  |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | 		case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		case <-ticker.C: | 
					
						
							| 
									
										
										
										
											2019-12-12 02:57:05 +08:00
										 |  |  | 			// Start with random sleep time, so as to avoid
 | 
					
						
							|  |  |  | 			// "synchronous checks" between servers
 | 
					
						
							|  |  |  | 			duration := time.Duration(r.Float64() * float64(lockMaintenanceInterval)) | 
					
						
							|  |  |  | 			time.Sleep(duration) | 
					
						
							| 
									
										
										
										
											2020-03-16 02:55:52 +08:00
										 |  |  | 			if err := lockMaintenance(ctx, lockValidityCheckInterval); err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-04 01:41:07 +08:00
										 |  |  | 				// Sleep right after an error.
 | 
					
						
							|  |  |  | 				duration := time.Duration(r.Float64() * float64(lockMaintenanceInterval)) | 
					
						
							|  |  |  | 				time.Sleep(duration) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // registerLockRESTHandlers - register lock rest router.
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | func registerLockRESTHandlers(router *mux.Router, endpointZones EndpointZones) { | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	queries := restQueries(lockRESTUID, lockRESTSource) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 	for _, ep := range endpointZones { | 
					
						
							|  |  |  | 		for _, endpoint := range ep.Endpoints { | 
					
						
							|  |  |  | 			if !endpoint.IsLocal { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			lockServer := &lockRESTServer{ | 
					
						
							|  |  |  | 				ll: newLocker(endpoint), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			subrouter := router.PathPrefix(path.Join(lockRESTPrefix, endpoint.Path)).Subrouter() | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodLock).HandlerFunc(httpTraceHdrs(lockServer.LockHandler)).Queries(queries...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRLock).HandlerFunc(httpTraceHdrs(lockServer.RLockHandler)).Queries(queries...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodUnlock).HandlerFunc(httpTraceHdrs(lockServer.UnlockHandler)).Queries(queries...) | 
					
						
							|  |  |  | 			subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRUnlock).HandlerFunc(httpTraceHdrs(lockServer.RUnlockHandler)).Queries(queries...) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 			subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodExpired).HandlerFunc(httpTraceAll(lockServer.ExpiredHandler)).Queries(queries...) | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			globalLockServers[endpoint] = lockServer.ll | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-23 03:16:36 +08:00
										 |  |  | 	go startLockMaintenance(GlobalContext) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } |