github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/mrf.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/madmin-go/v3"
    25  	"github.com/minio/pkg/v2/wildcard"
    26  )
    27  
    28  const (
    29  	mrfOpsQueueSize = 100000
    30  )
    31  
    32  // partialOperation is a successful upload/delete of an object
    33  // but not written in all disks (having quorum)
    34  type partialOperation struct {
    35  	bucket              string
    36  	object              string
    37  	versionID           string
    38  	allVersions         bool
    39  	setIndex, poolIndex int
    40  	queued              time.Time
    41  	scanMode            madmin.HealScanMode
    42  }
    43  
    44  // mrfState sncapsulates all the information
    45  // related to the global background MRF.
    46  type mrfState struct {
    47  	opCh chan partialOperation
    48  }
    49  
    50  // Add a partial S3 operation (put/delete) when one or more disks are offline.
    51  func (m *mrfState) addPartialOp(op partialOperation) {
    52  	if m == nil {
    53  		return
    54  	}
    55  
    56  	select {
    57  	case m.opCh <- op:
    58  	default:
    59  	}
    60  }
    61  
    62  var healSleeper = newDynamicSleeper(5, time.Second, false)
    63  
    64  // healRoutine listens to new disks reconnection events and
    65  // issues healing requests for queued objects belonging to the
    66  // corresponding erasure set
    67  func (m *mrfState) healRoutine(z *erasureServerPools) {
    68  	for {
    69  		select {
    70  		case <-GlobalContext.Done():
    71  			return
    72  		case u, ok := <-m.opCh:
    73  			if !ok {
    74  				return
    75  			}
    76  
    77  			// We might land at .metacache, .trash, .multipart
    78  			// no need to heal them skip, only when bucket
    79  			// is '.minio.sys'
    80  			if u.bucket == minioMetaBucket {
    81  				// No MRF needed for temporary objects
    82  				if wildcard.Match("buckets/*/.metacache/*", u.object) {
    83  					continue
    84  				}
    85  				if wildcard.Match("tmp/*", u.object) {
    86  					continue
    87  				}
    88  				if wildcard.Match("multipart/*", u.object) {
    89  					continue
    90  				}
    91  				if wildcard.Match("tmp-old/*", u.object) {
    92  					continue
    93  				}
    94  			}
    95  
    96  			now := time.Now()
    97  			if now.Sub(u.queued) < time.Second {
    98  				// let recently failed networks to reconnect
    99  				// making MRF wait for 1s before retrying,
   100  				// i.e 4 reconnect attempts.
   101  				time.Sleep(time.Second)
   102  			}
   103  
   104  			// wait on timer per heal
   105  			wait := healSleeper.Timer(context.Background())
   106  
   107  			scan := madmin.HealNormalScan
   108  			if u.scanMode != 0 {
   109  				scan = u.scanMode
   110  			}
   111  			if u.object == "" {
   112  				healBucket(u.bucket, scan)
   113  			} else {
   114  				if u.allVersions {
   115  					z.serverPools[u.poolIndex].sets[u.setIndex].listAndHeal(u.bucket, u.object, u.scanMode, healObjectVersionsDisparity)
   116  				} else {
   117  					healObject(u.bucket, u.object, u.versionID, scan)
   118  				}
   119  			}
   120  
   121  			wait()
   122  		}
   123  	}
   124  }