github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/reb/utils.go (about) 1 // Package reb provides global cluster-wide rebalance upon adding/removing storage nodes. 2 /* 3 * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package reb 6 7 import ( 8 "fmt" 9 "strconv" 10 "time" 11 12 "github.com/NVIDIA/aistore/cmn" 13 "github.com/NVIDIA/aistore/cmn/cos" 14 "github.com/NVIDIA/aistore/cmn/nlog" 15 "github.com/NVIDIA/aistore/core" 16 "github.com/NVIDIA/aistore/core/meta" 17 "github.com/NVIDIA/aistore/transport" 18 "github.com/NVIDIA/aistore/xact/xs" 19 ) 20 21 func (reb *Reb) RebID() int64 { return reb.rebID.Load() } 22 func (reb *Reb) FilterAdd(uname []byte) { reb.filterGFN.Insert(uname) } 23 24 // (limited usage; compare with `abortAndBroadcast` below) 25 func (reb *Reb) AbortLocal(olderSmapV int64, err error) { 26 if xreb := reb.xctn(); xreb != nil { 27 // double-check 28 smap := reb.smap.Load() 29 if smap.Version == olderSmapV { 30 if xreb.Abort(err) { 31 nlog.Warningf("%v - aborted", err) 32 } 33 } 34 } 35 } 36 37 func (reb *Reb) xctn() *xs.Rebalance { return reb.xreb.Load() } 38 func (reb *Reb) setXact(xctn *xs.Rebalance) { reb.xreb.Store(xctn) } 39 40 func (reb *Reb) logHdr(rebID int64, smap *meta.Smap, initializing ...bool) string { 41 smapv := "v<???>" 42 if smap != nil { 43 smapv = "v" + strconv.FormatInt(smap.Version, 10) 44 } 45 s := fmt.Sprintf("%s[g%d,%s", core.T, rebID, smapv) 46 if len(initializing) > 0 { 47 return s + "]" 48 } 49 stage := stages[reb.stages.stage.Load()] 50 return fmt.Sprintf("%s,%s]", s, stage) 51 } 52 53 func (reb *Reb) warnID(remoteID int64, tid string) (s string) { 54 const warn = "t[%s] runs %s g[%d] (local g[%d])" 55 if id := reb.RebID(); id < remoteID { 56 s = fmt.Sprintf(warn, tid, "newer", remoteID, id) 57 } else { 58 s = fmt.Sprintf(warn, tid, "older", remoteID, id) 59 } 60 return 61 } 62 63 func (reb *Reb) _waitForSmap() (smap *meta.Smap, err error) { 64 smap = reb.smap.Load() 65 if smap != nil { 66 return 67 } 68 var ( 69 config = cmn.GCO.Get() 70 sleep = cmn.Rom.CplaneOperation() 71 maxwt = config.Rebalance.DestRetryTime.D() 72 curwt time.Duration 73 ) 74 maxwt = min(maxwt, config.Timeout.SendFile.D()/3) 75 nlog.Warningf("%s: waiting to start...", core.T) 76 time.Sleep(sleep) 77 for curwt < maxwt { 78 smap = reb.smap.Load() 79 if smap != nil { 80 return 81 } 82 time.Sleep(sleep) 83 curwt += sleep 84 } 85 return nil, fmt.Errorf("%s: timed out waiting for usable Smap", core.T) 86 } 87 88 // Rebalance moves to the next stage: 89 // - update internal stage 90 // - send notification to all other targets that this one is in a new stage 91 func (reb *Reb) changeStage(newStage uint32) { 92 // first, set own stage 93 reb.stages.stage.Store(newStage) 94 var ( 95 req = stageNtfn{ 96 daemonID: core.T.SID(), stage: newStage, rebID: reb.rebID.Load(), 97 } 98 hdr = transport.ObjHdr{} 99 ) 100 hdr.Opaque = reb.encodeStageNtfn(&req) 101 // second, notify all 102 if err := reb.pushes.Send(&transport.Obj{Hdr: hdr}, nil); err != nil { 103 nlog.Warningf("Failed to broadcast ack %s: %v", stages[newStage], err) 104 } 105 } 106 107 // Aborts global rebalance and notifies all other targets. 108 // (compare with `Abort` above) 109 func (reb *Reb) abortAndBroadcast(err error) { 110 xreb := reb.xctn() 111 if xreb == nil || !xreb.Abort(err) { 112 return 113 } 114 nlog.InfoDepth(1, xreb.Name(), "abort-and-bcast", err) 115 116 var ( 117 req = stageNtfn{ 118 daemonID: core.T.SID(), 119 rebID: reb.RebID(), 120 stage: rebStageAbort, 121 } 122 hdr = transport.ObjHdr{} 123 ) 124 hdr.Opaque = reb.encodeStageNtfn(&req) 125 if err := reb.pushes.Send(&transport.Obj{Hdr: hdr}, nil); err != nil { 126 nlog.Errorf("Failed to broadcast abort notification: %v", err) 127 } 128 } 129 130 // Returns if the target is quiescent: transport queue is empty, or xaction 131 // has already aborted or finished. 132 func (reb *Reb) isQuiescent() bool { 133 // Finished or aborted xaction = no traffic 134 xctn := reb.xctn() 135 if xctn == nil || xctn.IsAborted() || xctn.Finished() { 136 return true 137 } 138 139 // Check for both regular and EC transport queues are empty 140 return reb.inQueue.Load() == 0 && reb.onAir.Load() == 0 141 } 142 143 ///////////// 144 // lomAcks TODO: lomAck.q[lom.Uname()] = lom.Bprops().BID and, subsequently, LIF => LOM reinit 145 ///////////// 146 147 func (reb *Reb) lomAcks() *[cos.MultiSyncMapCount]*lomAcks { return &reb.lomacks } 148 149 func (reb *Reb) addLomAck(lom *core.LOM) { 150 lomAck := reb.lomAcks()[lom.CacheIdx()] 151 lomAck.mu.Lock() 152 lomAck.q[lom.Uname()] = lom 153 lomAck.mu.Unlock() 154 } 155 156 func (reb *Reb) delLomAck(lom *core.LOM, rebID int64, freeLOM bool) { 157 if rebID != 0 && rebID != reb.rebID.Load() { 158 return 159 } 160 lomAck := reb.lomAcks()[lom.CacheIdx()] 161 lomAck.mu.Lock() 162 if rebID == 0 || rebID == reb.rebID.Load() { 163 if lomOrig, ok := lomAck.q[lom.Uname()]; ok { 164 delete(lomAck.q, lom.Uname()) 165 if freeLOM { 166 // counting acknowledged migrations (as initiator) 167 xreb := reb.xctn() 168 xreb.ObjsAdd(1, lomOrig.SizeBytes()) 169 170 core.FreeLOM(lomOrig) 171 } 172 } 173 } 174 lomAck.mu.Unlock() 175 }