| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | //go:generate msgp -file=$GOFILE -unexported
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/dsync" | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | // Reject new lock requests immediately when this many are queued
 | 
					
						
							|  |  |  | // for the local lock mutex.
 | 
					
						
							|  |  |  | // We do not block unlocking or maintenance, but they add to the count.
 | 
					
						
							|  |  |  | // The limit is set to allow for bursty behavior,
 | 
					
						
							|  |  |  | // but prevent requests to overload the server completely.
 | 
					
						
							|  |  |  | // Rejected clients are expected to retry.
 | 
					
						
							|  |  |  | const lockMutexWaitLimit = 1000 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | // lockRequesterInfo stores various info from the client for each lock that is requested.
 | 
					
						
							|  |  |  | type lockRequesterInfo struct { | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 	Name            string // name of the resource lock was requested for
 | 
					
						
							|  |  |  | 	Writer          bool   // Bool whether write or read lock.
 | 
					
						
							|  |  |  | 	UID             string // UID to uniquely identify request of client.
 | 
					
						
							|  |  |  | 	Timestamp       int64  // Timestamp set at the time of initialization.
 | 
					
						
							|  |  |  | 	TimeLastRefresh int64  // Timestamp for last lock refresh.
 | 
					
						
							|  |  |  | 	Source          string // Contains line, function and filename requesting the lock.
 | 
					
						
							|  |  |  | 	Group           bool   // indicates if it was a group lock.
 | 
					
						
							|  |  |  | 	Owner           string // Owner represents the UUID of the owner who originally requested the lock.
 | 
					
						
							|  |  |  | 	Quorum          int    // Quorum represents the quorum required for this lock to be active.
 | 
					
						
							|  |  |  | 	idx             int    `msg:"-"` // index of the lock in the lockMap.
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isWriteLock returns whether the lock is a write or read lock.
 | 
					
						
							|  |  |  | func isWriteLock(lri []lockRequesterInfo) bool { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	return len(lri) == 1 && lri[0].Writer | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // localLocker implements Dsync.NetLocker
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | //msgp:ignore localLocker
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | type localLocker struct { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	mutex     sync.Mutex | 
					
						
							|  |  |  | 	waitMutex atomic.Int32 | 
					
						
							|  |  |  | 	lockMap   map[string][]lockRequesterInfo | 
					
						
							|  |  |  | 	lockUID   map[string]string // UUID -> resource map.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the following are updated on every cleanup defined in lockValidityDuration
 | 
					
						
							|  |  |  | 	readers         atomic.Int32 | 
					
						
							|  |  |  | 	writers         atomic.Int32 | 
					
						
							|  |  |  | 	lastCleanup     atomic.Pointer[time.Time] | 
					
						
							|  |  |  | 	locksOverloaded atomic.Int64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // getMutex will lock the mutex.
 | 
					
						
							|  |  |  | // Call the returned function to unlock.
 | 
					
						
							|  |  |  | func (l *localLocker) getMutex() func() { | 
					
						
							|  |  |  | 	l.waitMutex.Add(1) | 
					
						
							|  |  |  | 	l.mutex.Lock() | 
					
						
							|  |  |  | 	l.waitMutex.Add(-1) | 
					
						
							|  |  |  | 	return l.mutex.Unlock | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | func (l *localLocker) String() string { | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	return globalEndpoints.Localhost() | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | func (l *localLocker) canTakeLock(resources ...string) bool { | 
					
						
							|  |  |  | 	for _, resource := range resources { | 
					
						
							|  |  |  | 		_, lockTaken := l.lockMap[resource] | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if lockTaken { | 
					
						
							|  |  |  | 			return false | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (l *localLocker) Lock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-18 23:38:33 +08:00
										 |  |  | 	if len(args.Resources) > maxDeleteList { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.Lock called with more than %d resources", maxDeleteList) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	// If we have too many waiting, reject this at once.
 | 
					
						
							|  |  |  | 	if l.waitMutex.Load() > lockMutexWaitLimit { | 
					
						
							|  |  |  | 		l.locksOverloaded.Add(1) | 
					
						
							|  |  |  | 		return false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Wait for mutex
 | 
					
						
							|  |  |  | 	defer l.getMutex()() | 
					
						
							|  |  |  | 	if ctx.Err() != nil { | 
					
						
							|  |  |  | 		return false, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	if !l.canTakeLock(args.Resources...) { | 
					
						
							|  |  |  | 		// Not all locks can be taken on resources,
 | 
					
						
							|  |  |  | 		// reject it completely.
 | 
					
						
							|  |  |  | 		return false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	// No locks held on the all resources, so claim write
 | 
					
						
							|  |  |  | 	// lock on all resources at once.
 | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 	now := UTCNow() | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	for i, resource := range args.Resources { | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		l.lockMap[resource] = []lockRequesterInfo{ | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 				Name:            resource, | 
					
						
							|  |  |  | 				Writer:          true, | 
					
						
							|  |  |  | 				Source:          args.Source, | 
					
						
							|  |  |  | 				Owner:           args.Owner, | 
					
						
							|  |  |  | 				UID:             args.UID, | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 				Timestamp:       now.UnixNano(), | 
					
						
							|  |  |  | 				TimeLastRefresh: now.UnixNano(), | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 				Group:           len(args.Resources) > 1, | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 				Quorum:          *args.Quorum, | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				idx:             i, | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		l.lockUID[formatUUID(args.UID, i)] = resource | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	return true, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | func formatUUID(s string, idx int) string { | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 	return concat(s, strconv.Itoa(idx)) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | func (l *localLocker) Unlock(_ context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-18 23:38:33 +08:00
										 |  |  | 	if len(args.Resources) > maxDeleteList { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.Unlock called with more than %d resources", maxDeleteList) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	defer l.getMutex()() | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, resource := range args.Resources { | 
					
						
							| 
									
										
										
										
											2022-09-03 05:24:32 +08:00
										 |  |  | 		lri, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 		if ok && !isWriteLock(lri) { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			// Unless it is a write lock reject it.
 | 
					
						
							|  |  |  | 			err = fmt.Errorf("unlock attempted on a read locked entity: %s", resource) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 		if ok { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			reply = l.removeEntry(resource, args, &lri) || reply | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	return | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // removeEntry based on the uid of the lock message, removes a single entry from the
 | 
					
						
							|  |  |  | // lockRequesterInfo array or the whole array from the map (in case of a write lock
 | 
					
						
							|  |  |  | // or last read lock)
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | // UID and optionally owner must match for entries to be deleted.
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | func (l *localLocker) removeEntry(name string, args dsync.LockArgs, lri *[]lockRequesterInfo) bool { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	// Find correct entry to remove based on uid.
 | 
					
						
							|  |  |  | 	for index, entry := range *lri { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if entry.UID == args.UID && (args.Owner == "" || entry.Owner == args.Owner) { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 			if len(*lri) == 1 { | 
					
						
							|  |  |  | 				// Remove the write lock.
 | 
					
						
							|  |  |  | 				delete(l.lockMap, name) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Remove the appropriate read lock.
 | 
					
						
							|  |  |  | 				*lri = append((*lri)[:index], (*lri)[index+1:]...) | 
					
						
							|  |  |  | 				l.lockMap[name] = *lri | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			delete(l.lockUID, formatUUID(args.UID, entry.idx)) | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// None found return false, perhaps entry removed in previous run.
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (l *localLocker) RLock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
											  
											
												perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via 
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
  roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
  which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte 
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address 
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight 
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
   performs a single request and returns the result. Any deadline provided on the request is
   forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
   will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
	// Responses from the remote server.
	// Channel will be closed after an error or when the remote closes.
	// All responses *must* be read by the caller until either an error is returned or the channel is closed.
	// Canceling the context will cause the context cancellation error to be returned.
	Responses <-chan Response
	// Requests sent to the server.
	// If the handler is defined with 0 incoming capacity this will be nil.
	// Channel *must* be closed to signal the end of the stream.
	// If the request context is canceled, the stream will no longer process requests.
	Requests chan<- []byte
}
type Response struct {
	Msg []byte
	Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
											
										 
											2023-11-21 09:09:35 +08:00
										 |  |  | 	if len(args.Resources) != 1 { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		return false, fmt.Errorf("internal error: localLocker.RLock called with more than one resource") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	// If we have too many waiting, reject this at once.
 | 
					
						
							|  |  |  | 	if l.waitMutex.Load() > lockMutexWaitLimit { | 
					
						
							|  |  |  | 		l.locksOverloaded.Add(1) | 
					
						
							|  |  |  | 		return false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	// Wait for mutex
 | 
					
						
							|  |  |  | 	defer l.getMutex()() | 
					
						
							|  |  |  | 	if ctx.Err() != nil { | 
					
						
							|  |  |  | 		return false, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	resource := args.Resources[0] | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 	now := UTCNow() | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	lrInfo := lockRequesterInfo{ | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		Name:            resource, | 
					
						
							|  |  |  | 		Writer:          false, | 
					
						
							|  |  |  | 		Source:          args.Source, | 
					
						
							|  |  |  | 		Owner:           args.Owner, | 
					
						
							|  |  |  | 		UID:             args.UID, | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 		Timestamp:       now.UnixNano(), | 
					
						
							|  |  |  | 		TimeLastRefresh: now.UnixNano(), | 
					
						
							|  |  |  | 		Quorum:          *args.Quorum, | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 	lri, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		if reply = !isWriteLock(lri); reply { | 
					
						
							|  |  |  | 			// Unless there is a write lock
 | 
					
						
							|  |  |  | 			l.lockMap[resource] = append(l.lockMap[resource], lrInfo) | 
					
						
							| 
									
										
										
										
											2021-11-08 23:35:50 +08:00
										 |  |  | 			l.lockUID[formatUUID(args.UID, 0)] = resource | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		// No locks held on the given name, so claim (first) read lock
 | 
					
						
							|  |  |  | 		l.lockMap[resource] = []lockRequesterInfo{lrInfo} | 
					
						
							| 
									
										
										
										
											2021-11-08 23:35:50 +08:00
										 |  |  | 		l.lockUID[formatUUID(args.UID, 0)] = resource | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		reply = true | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	return reply, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:11:29 +08:00
										 |  |  | func (l *localLocker) RUnlock(_ context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	if len(args.Resources) > 1 { | 
					
						
							|  |  |  | 		return false, fmt.Errorf("internal error: localLocker.RUnlock called with more than one resource") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	defer l.getMutex()() | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	var lri []lockRequesterInfo | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	resource := args.Resources[0] | 
					
						
							|  |  |  | 	if lri, reply = l.lockMap[resource]; !reply { | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		// No lock is held on the given name
 | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	if isWriteLock(lri) { | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 		// A write-lock is held, cannot release a read lock
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		return false, fmt.Errorf("RUnlock attempted on a write locked entity: %s", resource) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 	l.removeEntry(resource, args, &lri) | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	return reply, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | type lockStats struct { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	Total          int | 
					
						
							|  |  |  | 	Writes         int | 
					
						
							|  |  |  | 	Reads          int | 
					
						
							|  |  |  | 	LockQueue      int | 
					
						
							|  |  |  | 	LocksAbandoned int | 
					
						
							|  |  |  | 	LastCleanup    *time.Time | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *localLocker) stats() lockStats { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	return lockStats{ | 
					
						
							|  |  |  | 		Total:       len(l.lockMap), | 
					
						
							|  |  |  | 		Reads:       int(l.readers.Load()), | 
					
						
							|  |  |  | 		Writes:      int(l.writers.Load()), | 
					
						
							|  |  |  | 		LockQueue:   int(l.waitMutex.Load()), | 
					
						
							|  |  |  | 		LastCleanup: l.lastCleanup.Load(), | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 06:54:46 +08:00
										 |  |  | type localLockMap map[string][]lockRequesterInfo | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *localLocker) DupLockMap() localLockMap { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	defer l.getMutex()() | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 	lockCopy := make(map[string][]lockRequesterInfo, len(l.lockMap)) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	for k, v := range l.lockMap { | 
					
						
							| 
									
										
										
										
											2023-04-04 12:23:24 +08:00
										 |  |  | 		if len(v) == 0 { | 
					
						
							|  |  |  | 			delete(l.lockMap, k) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		lockCopy[k] = append(make([]lockRequesterInfo, 0, len(v)), v...) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return lockCopy | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (l *localLocker) Close() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | // IsOnline - local locker is always online.
 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | func (l *localLocker) IsOnline() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | // IsLocal - local locker returns true.
 | 
					
						
							|  |  |  | func (l *localLocker) IsLocal() bool { | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | func (l *localLocker) ForceUnlock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	if ctx.Err() != nil { | 
					
						
							|  |  |  | 		return false, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer l.getMutex()() | 
					
						
							|  |  |  | 	if ctx.Err() != nil { | 
					
						
							| 
									
										
										
										
											2023-03-24 01:26:21 +08:00
										 |  |  | 		return false, ctx.Err() | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if len(args.UID) == 0 { | 
					
						
							|  |  |  | 		for _, resource := range args.Resources { | 
					
						
							|  |  |  | 			lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Collect uids, so we don't mutate while we delete
 | 
					
						
							|  |  |  | 			uids := make([]string, 0, len(lris)) | 
					
						
							|  |  |  | 			for _, lri := range lris { | 
					
						
							|  |  |  | 				uids = append(uids, lri.UID) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Delete collected uids:
 | 
					
						
							|  |  |  | 			for _, uid := range uids { | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 					// Just to be safe, delete uuids.
 | 
					
						
							|  |  |  | 					for idx := 0; idx < maxDeleteList; idx++ { | 
					
						
							|  |  |  | 						mapID := formatUUID(uid, idx) | 
					
						
							|  |  |  | 						if _, ok := l.lockUID[mapID]; !ok { | 
					
						
							|  |  |  | 							break | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 						delete(l.lockUID, mapID) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 				l.removeEntry(resource, dsync.LockArgs{UID: uid}, &lris) | 
					
						
							| 
									
										
										
										
											2021-08-27 23:59:36 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 		return true, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-27 23:59:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	idx := 0 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		mapID := formatUUID(args.UID, idx) | 
					
						
							|  |  |  | 		resource, ok := l.lockUID[mapID] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return idx > 0, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		lris, ok := l.lockMap[resource] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			// Unexpected  inconsistency, delete.
 | 
					
						
							|  |  |  | 			delete(l.lockUID, mapID) | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			idx++ | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 		reply = true | 
					
						
							|  |  |  | 		l.removeEntry(resource, dsync.LockArgs{UID: args.UID}, &lris) | 
					
						
							|  |  |  | 		idx++ | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (l *localLocker) Refresh(ctx context.Context, args dsync.LockArgs) (refreshed bool, err error) { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	if ctx.Err() != nil { | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 		return false, ctx.Err() | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	defer l.getMutex()() | 
					
						
							|  |  |  | 	if ctx.Err() != nil { | 
					
						
							|  |  |  | 		return false, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check whether uid is still active.
 | 
					
						
							|  |  |  | 	resource, ok := l.lockUID[formatUUID(args.UID, 0)] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return false, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	idx := 0 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		lris, ok := l.lockMap[resource] | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		if !ok { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 			// Inconsistent. Delete UID.
 | 
					
						
							|  |  |  | 			delete(l.lockUID, formatUUID(args.UID, idx)) | 
					
						
							|  |  |  | 			return idx > 0, nil | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 		now := UTCNow() | 
					
						
							|  |  |  | 		for i := range lris { | 
					
						
							|  |  |  | 			if lris[i].UID == args.UID { | 
					
						
							|  |  |  | 				lris[i].TimeLastRefresh = now.UnixNano() | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-01 01:41:17 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 		idx++ | 
					
						
							|  |  |  | 		resource, ok = l.lockUID[formatUUID(args.UID, idx)] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			// No more resources for UID, but we did update at least one.
 | 
					
						
							|  |  |  | 			return true, nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Similar to removeEntry but only removes an entry only if the lock entry exists in map.
 | 
					
						
							|  |  |  | // Caller must hold 'l.mutex' lock.
 | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | func (l *localLocker) expireOldLocks(interval time.Duration) { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	defer l.getMutex()() | 
					
						
							| 
									
										
										
										
											2020-10-27 01:29:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	var readers, writers int32 | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 	for k, lris := range l.lockMap { | 
					
						
							|  |  |  | 		modified := false | 
					
						
							|  |  |  | 		for i := 0; i < len(lris); { | 
					
						
							|  |  |  | 			lri := &lris[i] | 
					
						
							| 
									
										
										
										
											2024-07-24 18:24:01 +08:00
										 |  |  | 			if time.Since(time.Unix(0, lri.TimeLastRefresh)) > interval { | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 				delete(l.lockUID, formatUUID(lri.UID, lri.idx)) | 
					
						
							|  |  |  | 				if len(lris) == 1 { | 
					
						
							|  |  |  | 					// Remove the write lock.
 | 
					
						
							|  |  |  | 					delete(l.lockMap, lri.Name) | 
					
						
							|  |  |  | 					modified = false | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 				modified = true | 
					
						
							|  |  |  | 				// Remove the appropriate lock.
 | 
					
						
							|  |  |  | 				lris = append(lris[:i], lris[i+1:]...) | 
					
						
							|  |  |  | 				// Check same i
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 				if lri.Writer { | 
					
						
							|  |  |  | 					writers++ | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					readers++ | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 				// Move to next
 | 
					
						
							|  |  |  | 				i++ | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-28 06:10:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if modified { | 
					
						
							|  |  |  | 			l.lockMap[k] = lris | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-02-01 03:54:34 +08:00
										 |  |  | 	t := time.Now() | 
					
						
							|  |  |  | 	l.lastCleanup.Store(&t) | 
					
						
							|  |  |  | 	l.readers.Store(readers) | 
					
						
							|  |  |  | 	l.writers.Store(writers) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | func newLocker() *localLocker { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	return &localLocker{ | 
					
						
							| 
									
										
										
										
											2021-10-15 18:12:13 +08:00
										 |  |  | 		lockMap: make(map[string][]lockRequesterInfo, 1000), | 
					
						
							|  |  |  | 		lockUID: make(map[string]string, 1000), | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |