| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 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 ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | type statusType string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	runningStatus statusType = "Running" | 
					
						
							|  |  |  | 	blockedStatus statusType = "Blocked" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type lockType string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	debugRLockStr lockType = "RLock" | 
					
						
							|  |  |  | 	debugWLockStr lockType = "WLock" | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // debugLockInfo - represents a single lock's information, uniquely identified by opsID.
 | 
					
						
							|  |  |  | // See debugLockInfoPerVolumePath for more context.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | type debugLockInfo struct { | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	// "RLock" or "WLock".
 | 
					
						
							|  |  |  | 	lType lockType | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Contains the backtrace of incl. the function which called (r)(un)lock.
 | 
					
						
							| 
									
										
										
										
											2016-11-24 08:36:26 +08:00
										 |  |  | 	lockSource string | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Status can be running/blocked.
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	status statusType | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Time of last status update.
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	since time.Time | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // debugLockInfoPerVolumePath - lock state information on all locks held on (volume, path).
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | type debugLockInfoPerVolumePath struct { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	counters *lockStat                // Holds stats of lock held on (volume, path)
 | 
					
						
							|  |  |  | 	lockInfo map[string]debugLockInfo // Lock information per operation ID.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // LockInfoOriginMismatch - represents error when lock origin don't match.
 | 
					
						
							|  |  |  | type LockInfoOriginMismatch struct { | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	volume     string | 
					
						
							|  |  |  | 	path       string | 
					
						
							|  |  |  | 	opsID      string | 
					
						
							| 
									
										
										
										
											2016-11-24 08:36:26 +08:00
										 |  |  | 	lockSource string | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | func (l LockInfoOriginMismatch) Error() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("No lock state stored for the lock originated at \"%s\", for <volume> %s, <path> %s, <opsID> %s", | 
					
						
							| 
									
										
										
										
											2016-11-24 08:36:26 +08:00
										 |  |  | 		l.lockSource, l.volume, l.path, l.opsID) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // LockInfoVolPathMissing - represents error when lock information is missing for a given (volume, path).
 | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | type LockInfoVolPathMissing struct { | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	volume string | 
					
						
							|  |  |  | 	path   string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | func (l LockInfoVolPathMissing) Error() string { | 
					
						
							| 
									
										
										
										
											2016-11-16 10:14:23 +08:00
										 |  |  | 	return fmt.Sprintf("No entry in debug Lock Map for Volume: %s, path: %s", l.volume, l.path) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // LockInfoOpsIDNotFound - represents error when lock info entry for a given operation ID doesn't exist.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | type LockInfoOpsIDNotFound struct { | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	volume string | 
					
						
							|  |  |  | 	path   string | 
					
						
							|  |  |  | 	opsID  string | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l LockInfoOpsIDNotFound) Error() string { | 
					
						
							| 
									
										
										
										
											2016-11-16 10:14:23 +08:00
										 |  |  | 	return fmt.Sprintf("No entry in lock info for <Operation ID> %s, <volume> %s, <path> %s", l.opsID, l.volume, l.path) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // LockInfoStateNotBlocked - represents error when lock info isn't in blocked state when it should be.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | type LockInfoStateNotBlocked struct { | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	volume string | 
					
						
							|  |  |  | 	path   string | 
					
						
							|  |  |  | 	opsID  string | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l LockInfoStateNotBlocked) Error() string { | 
					
						
							| 
									
										
										
										
											2016-11-16 10:14:23 +08:00
										 |  |  | 	return fmt.Sprintf("Lock state should be \"Blocked\" for <volume> %s, <path> %s, <opsID> %s", l.volume, l.path, l.opsID) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // Initialize lock info for given (volume, path).
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | func (n *nsLockMap) initLockInfoForVolumePath(param nsParam) { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	n.debugLockMap[param] = &debugLockInfoPerVolumePath{ | 
					
						
							|  |  |  | 		lockInfo: make(map[string]debugLockInfo), | 
					
						
							|  |  |  | 		counters: &lockStat{}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Change the state of the lock from Blocked to Running.
 | 
					
						
							| 
									
										
										
										
											2016-11-24 08:36:26 +08:00
										 |  |  | func (n *nsLockMap) statusBlockedToRunning(param nsParam, lockSource, opsID string, readLock bool) error { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// This function is called outside nsLockMap.mutex.Lock(), so must be held explicitly.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	n.lockMapMutex.Lock() | 
					
						
							|  |  |  | 	defer n.lockMapMutex.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Check whether the lock info entry for <volume, path> pair already exists.
 | 
					
						
							|  |  |  | 	_, ok := n.debugLockMap[param] | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoVolPathMissing{param.volume, param.path}) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check whether lock info entry for the given `opsID` exists.
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	lockInfo, ok := n.debugLockMap[param].lockInfo[opsID] | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoOpsIDNotFound{param.volume, param.path, opsID}) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check whether lockSource is same.
 | 
					
						
							| 
									
										
										
										
											2016-11-24 08:36:26 +08:00
										 |  |  | 	if lockInfo.lockSource != lockSource { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 		return traceError(LockInfoOriginMismatch{param.volume, param.path, opsID, lockSource}) | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Status of the lock should be set to "Blocked".
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	if lockInfo.status != blockedStatus { | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoStateNotBlocked{param.volume, param.path, opsID}) | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Change lock status to running and update the time.
 | 
					
						
							|  |  |  | 	n.debugLockMap[param].lockInfo[opsID] = newDebugLockInfo(lockSource, runningStatus, readLock) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Update global lock stats.
 | 
					
						
							|  |  |  | 	n.counters.lockGranted() | 
					
						
							|  |  |  | 	// Update (volume, pair) lock stats.
 | 
					
						
							|  |  |  | 	n.debugLockMap[param].counters.lockGranted() | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // newDebugLockInfo - Constructs a debugLockInfo value given lock source, status and type.
 | 
					
						
							|  |  |  | func newDebugLockInfo(lockSource string, status statusType, readLock bool) debugLockInfo { | 
					
						
							|  |  |  | 	lType := debugRLockStr | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	if readLock { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 		lType = debugRLockStr | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 		lType = debugWLockStr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return debugLockInfo{ | 
					
						
							|  |  |  | 		lockSource: lockSource, | 
					
						
							|  |  |  | 		lType:      lType, | 
					
						
							|  |  |  | 		status:     status, | 
					
						
							|  |  |  | 		since:      time.Now().UTC(), | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // Change the state of the lock to Blocked.
 | 
					
						
							|  |  |  | func (n *nsLockMap) statusNoneToBlocked(param nsParam, lockSource, opsID string, readLock bool) error { | 
					
						
							|  |  |  | 	_, ok := n.debugLockMap[param] | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 		// Lock info entry for (volume, pair) doesn't exist, initialize it.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		n.initLockInfoForVolumePath(param) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Mark lock status blocked for given opsID.
 | 
					
						
							|  |  |  | 	n.debugLockMap[param].lockInfo[opsID] = newDebugLockInfo(lockSource, blockedStatus, readLock) | 
					
						
							|  |  |  | 	// Update global lock stats.
 | 
					
						
							|  |  |  | 	n.counters.lockWaiting() | 
					
						
							|  |  |  | 	// Update (volume, path) lock stats.
 | 
					
						
							|  |  |  | 	n.debugLockMap[param].counters.lockWaiting() | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // deleteLockInfoEntry - Deletes the lock information for given (volume, path).
 | 
					
						
							|  |  |  | // Called when nsLk.ref count is 0.
 | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | func (n *nsLockMap) deleteLockInfoEntryForVolumePath(param nsParam) error { | 
					
						
							|  |  |  | 	// delete the lock info for the given operation.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	if _, found := n.debugLockMap[param]; !found { | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoVolPathMissing{param.volume, param.path}) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// The following stats update is relevant only in case of a
 | 
					
						
							|  |  |  | 	// ForceUnlock. In case of the last unlock on a (volume,
 | 
					
						
							|  |  |  | 	// path), this would be a no-op.
 | 
					
						
							|  |  |  | 	volumePathLocks := n.debugLockMap[param] | 
					
						
							|  |  |  | 	for _, lockInfo := range volumePathLocks.lockInfo { | 
					
						
							|  |  |  | 		granted := lockInfo.status == runningStatus | 
					
						
							|  |  |  | 		// Update global and (volume, path) stats.
 | 
					
						
							|  |  |  | 		n.counters.lockRemoved(granted) | 
					
						
							|  |  |  | 		volumePathLocks.counters.lockRemoved(granted) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	delete(n.debugLockMap, param) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | // deleteLockInfoEntry - Deletes lock info entry for given opsID.
 | 
					
						
							|  |  |  | // Called when the nsLk ref count for the given (volume, path) is
 | 
					
						
							|  |  |  | // not 0.
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | func (n *nsLockMap) deleteLockInfoEntryForOps(param nsParam, opsID string) error { | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	// delete the lock info for the given operation.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	infoMap, found := n.debugLockMap[param] | 
					
						
							|  |  |  | 	if !found { | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoVolPathMissing{param.volume, param.path}) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// The operation finished holding the lock on the resource, remove
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	// the entry for the given operation with the operation ID.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	opsIDLock, foundInfo := infoMap.lockInfo[opsID] | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	if !foundInfo { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 		// Unlock request with invalid operation ID not accepted.
 | 
					
						
							| 
									
										
										
										
											2016-11-01 01:17:14 +08:00
										 |  |  | 		return traceError(LockInfoOpsIDNotFound{param.volume, param.path, opsID}) | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:29:55 +08:00
										 |  |  | 	// Update global and (volume, path) lock status.
 | 
					
						
							|  |  |  | 	granted := opsIDLock.status == runningStatus | 
					
						
							|  |  |  | 	n.counters.lockRemoved(granted) | 
					
						
							|  |  |  | 	infoMap.counters.lockRemoved(granted) | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | 	delete(infoMap.lockInfo, opsID) | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-11 15:50:27 +08:00
										 |  |  | // Return randomly generated string ID
 | 
					
						
							| 
									
										
										
										
											2016-09-28 05:35:43 +08:00
										 |  |  | func getOpsID() string { | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	return mustGetRequestID() | 
					
						
							| 
									
										
										
										
											2016-09-01 02:39:08 +08:00
										 |  |  | } |