github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/lock-rest-server.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"time"
    23  
    24  	"github.com/minio/minio/internal/dsync"
    25  	"github.com/minio/minio/internal/grid"
    26  	"github.com/minio/minio/internal/logger"
    27  )
    28  
    29  // To abstract a node over network.
    30  type lockRESTServer struct {
    31  	ll *localLocker
    32  }
    33  
    34  // RefreshHandler - refresh the current lock
    35  func (l *lockRESTServer) RefreshHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    36  	resp := lockRPCRefresh.NewResponse()
    37  	refreshed, err := l.ll.Refresh(context.Background(), *args)
    38  	if err != nil {
    39  		return l.makeResp(resp, err)
    40  	}
    41  	if !refreshed {
    42  		return l.makeResp(resp, errLockNotFound)
    43  	}
    44  	return l.makeResp(resp, err)
    45  }
    46  
    47  // LockHandler - Acquires a lock.
    48  func (l *lockRESTServer) LockHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    49  	resp := lockRPCLock.NewResponse()
    50  	success, err := l.ll.Lock(context.Background(), *args)
    51  	if err == nil && !success {
    52  		return l.makeResp(resp, errLockConflict)
    53  	}
    54  	return l.makeResp(resp, err)
    55  }
    56  
    57  // UnlockHandler - releases the acquired lock.
    58  func (l *lockRESTServer) UnlockHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    59  	resp := lockRPCUnlock.NewResponse()
    60  	_, err := l.ll.Unlock(context.Background(), *args)
    61  	// Ignore the Unlock() "reply" return value because if err == nil, "reply" is always true
    62  	// Consequently, if err != nil, reply is always false
    63  	return l.makeResp(resp, err)
    64  }
    65  
    66  // RLockHandler - Acquires an RLock.
    67  func (l *lockRESTServer) RLockHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    68  	resp := lockRPCRLock.NewResponse()
    69  	success, err := l.ll.RLock(context.Background(), *args)
    70  	if err == nil && !success {
    71  		err = errLockConflict
    72  	}
    73  	return l.makeResp(resp, err)
    74  }
    75  
    76  // RUnlockHandler - releases the acquired read lock.
    77  func (l *lockRESTServer) RUnlockHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    78  	resp := lockRPCRUnlock.NewResponse()
    79  
    80  	// Ignore the RUnlock() "reply" return value because if err == nil, "reply" is always true.
    81  	// Consequently, if err != nil, reply is always false
    82  	_, err := l.ll.RUnlock(context.Background(), *args)
    83  	return l.makeResp(resp, err)
    84  }
    85  
    86  // ForceUnlockHandler - query expired lock status.
    87  func (l *lockRESTServer) ForceUnlockHandler(args *dsync.LockArgs) (*dsync.LockResp, *grid.RemoteErr) {
    88  	resp := lockRPCForceUnlock.NewResponse()
    89  
    90  	_, err := l.ll.ForceUnlock(context.Background(), *args)
    91  	return l.makeResp(resp, err)
    92  }
    93  
    94  var (
    95  	// Static lock handlers.
    96  	// All have the same signature.
    97  	lockRPCForceUnlock = newLockHandler(grid.HandlerLockForceUnlock)
    98  	lockRPCRefresh     = newLockHandler(grid.HandlerLockRefresh)
    99  	lockRPCLock        = newLockHandler(grid.HandlerLockLock)
   100  	lockRPCUnlock      = newLockHandler(grid.HandlerLockUnlock)
   101  	lockRPCRLock       = newLockHandler(grid.HandlerLockRLock)
   102  	lockRPCRUnlock     = newLockHandler(grid.HandlerLockRUnlock)
   103  )
   104  
   105  func newLockHandler(h grid.HandlerID) *grid.SingleHandler[*dsync.LockArgs, *dsync.LockResp] {
   106  	return grid.NewSingleHandler[*dsync.LockArgs, *dsync.LockResp](h, func() *dsync.LockArgs {
   107  		return &dsync.LockArgs{}
   108  	}, func() *dsync.LockResp {
   109  		return &dsync.LockResp{}
   110  	})
   111  }
   112  
   113  // registerLockRESTHandlers - register lock rest router.
   114  func registerLockRESTHandlers() {
   115  	lockServer := &lockRESTServer{
   116  		ll: newLocker(),
   117  	}
   118  
   119  	logger.FatalIf(lockRPCForceUnlock.Register(globalGrid.Load(), lockServer.ForceUnlockHandler), "unable to register handler")
   120  	logger.FatalIf(lockRPCRefresh.Register(globalGrid.Load(), lockServer.RefreshHandler), "unable to register handler")
   121  	logger.FatalIf(lockRPCLock.Register(globalGrid.Load(), lockServer.LockHandler), "unable to register handler")
   122  	logger.FatalIf(lockRPCUnlock.Register(globalGrid.Load(), lockServer.UnlockHandler), "unable to register handler")
   123  	logger.FatalIf(lockRPCRLock.Register(globalGrid.Load(), lockServer.RLockHandler), "unable to register handler")
   124  	logger.FatalIf(lockRPCRUnlock.Register(globalGrid.Load(), lockServer.RUnlockHandler), "unable to register handler")
   125  
   126  	globalLockServer = lockServer.ll
   127  
   128  	go lockMaintenance(GlobalContext)
   129  }
   130  
   131  func (l *lockRESTServer) makeResp(dst *dsync.LockResp, err error) (*dsync.LockResp, *grid.RemoteErr) {
   132  	*dst = dsync.LockResp{Code: dsync.RespOK}
   133  	switch err {
   134  	case nil:
   135  	case errLockNotInitialized:
   136  		dst.Code = dsync.RespLockNotInitialized
   137  	case errLockConflict:
   138  		dst.Code = dsync.RespLockConflict
   139  	case errLockNotFound:
   140  		dst.Code = dsync.RespLockNotFound
   141  	default:
   142  		dst.Code = dsync.RespErr
   143  		dst.Err = err.Error()
   144  	}
   145  	return dst, nil
   146  }
   147  
   148  const (
   149  	// Lock maintenance interval.
   150  	lockMaintenanceInterval = 1 * time.Minute
   151  
   152  	// Lock validity duration
   153  	lockValidityDuration = 1 * time.Minute
   154  )
   155  
   156  // lockMaintenance loops over all locks and discards locks
   157  // that have not been refreshed for some time.
   158  func lockMaintenance(ctx context.Context) {
   159  	if !globalIsDistErasure {
   160  		return
   161  	}
   162  
   163  	// Initialize a new ticker with 1 minute between each ticks.
   164  	lkTimer := time.NewTimer(lockMaintenanceInterval)
   165  	// Stop the timer upon returning.
   166  	defer lkTimer.Stop()
   167  
   168  	for {
   169  		// Verifies every minute for locks held more than 2 minutes.
   170  		select {
   171  		case <-ctx.Done():
   172  			return
   173  		case <-lkTimer.C:
   174  			globalLockServer.expireOldLocks(lockValidityDuration)
   175  
   176  			// Reset the timer for next cycle.
   177  			lkTimer.Reset(lockMaintenanceInterval)
   178  		}
   179  	}
   180  }