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 }