github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/transport/bundle/dmover.go (about) 1 // Package bundle provides multi-streaming transport with the functionality 2 // to dynamically (un)register receive endpoints, establish long-lived flows, and more. 3 /* 4 * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package bundle 7 8 import ( 9 "fmt" 10 "io" 11 "time" 12 13 "github.com/NVIDIA/aistore/api/apc" 14 "github.com/NVIDIA/aistore/cmn" 15 "github.com/NVIDIA/aistore/cmn/atomic" 16 "github.com/NVIDIA/aistore/cmn/cos" 17 "github.com/NVIDIA/aistore/cmn/debug" 18 "github.com/NVIDIA/aistore/cmn/nlog" 19 "github.com/NVIDIA/aistore/core" 20 "github.com/NVIDIA/aistore/core/meta" 21 "github.com/NVIDIA/aistore/transport" 22 ) 23 24 type ( 25 DataMover struct { 26 data struct { 27 client transport.Client 28 recv transport.RecvObj 29 streams *Streams 30 trname string 31 net string // one of cmn.KnownNetworks, empty defaults to cmn.NetIntraData 32 } 33 ack struct { 34 client transport.Client 35 recv transport.RecvObj 36 streams *Streams 37 trname string 38 net string // one of cmn.KnownNetworks, empty defaults to cmn.NetIntraControl 39 } 40 xctn core.Xact 41 config *cmn.Config 42 compression string // enum { apc.CompressNever, ... } 43 multiplier int 44 owt cmn.OWT 45 stage struct { 46 regred atomic.Bool 47 opened atomic.Bool 48 laterx atomic.Bool 49 } 50 sizePDU int32 51 maxHdrSize int32 52 } 53 // additional (and optional) params for new data mover 54 Extra struct { 55 RecvAck transport.RecvObj 56 Config *cmn.Config 57 Compression string 58 Multiplier int 59 SizePDU int32 60 MaxHdrSize int32 61 } 62 ) 63 64 var _ core.DM = (*DataMover)(nil) // via t.CopyObject() 65 66 // In re `owt` (below): data mover passes it to the target's `PutObject` 67 // to properly finalize received payload. 68 // For DMs that do not create new objects (e.g, rebalance) `owt` should 69 // be set to `OwtMigrateRepl`; all others are expected to have `OwtPut` (see e.g, CopyBucket). 70 71 func NewDataMover(trname string, recvCB transport.RecvObj, owt cmn.OWT, extra Extra) (*DataMover, error) { 72 debug.Assert(extra.Config != nil) 73 dm := &DataMover{config: extra.Config} 74 dm.owt = owt 75 dm.multiplier = extra.Multiplier 76 dm.sizePDU, dm.maxHdrSize = extra.SizePDU, extra.MaxHdrSize 77 switch extra.Compression { 78 case "": 79 dm.compression = apc.CompressNever 80 case apc.CompressAlways, apc.CompressNever: 81 dm.compression = extra.Compression 82 default: 83 return nil, fmt.Errorf("invalid compression %q", extra.Compression) 84 } 85 dm.data.trname, dm.data.recv = trname, recvCB 86 if dm.data.net == "" { 87 dm.data.net = cmn.NetIntraData 88 } 89 dm.data.client = transport.NewIntraDataClient() 90 // ack 91 if dm.ack.net == "" { 92 dm.ack.net = cmn.NetIntraControl 93 } 94 dm.ack.recv = extra.RecvAck 95 if !dm.useACKs() { 96 return dm, nil 97 } 98 dm.ack.trname = "ack." + trname 99 dm.ack.client = transport.NewIntraDataClient() 100 return dm, nil 101 } 102 103 func (dm *DataMover) useACKs() bool { return dm.ack.recv != nil } 104 func (dm *DataMover) NetD() string { return dm.data.net } 105 func (dm *DataMover) NetC() string { return dm.ack.net } 106 func (dm *DataMover) OWT() cmn.OWT { return dm.owt } 107 108 // xaction that drives and utilizes this data mover 109 func (dm *DataMover) SetXact(xctn core.Xact) { dm.xctn = xctn } 110 func (dm *DataMover) GetXact() core.Xact { return dm.xctn } 111 112 // register user's receive-data (and, optionally, receive-ack) wrappers 113 func (dm *DataMover) RegRecv() (err error) { 114 if err = transport.Handle(dm.data.trname, dm.wrapRecvData); err != nil { 115 return 116 } 117 if dm.useACKs() { 118 err = transport.Handle(dm.ack.trname, dm.wrapRecvACK) 119 } 120 dm.stage.regred.Store(true) 121 return 122 } 123 124 func (dm *DataMover) Open() { 125 dataArgs := Args{ 126 Net: dm.data.net, 127 Trname: dm.data.trname, 128 Extra: &transport.Extra{ 129 Compression: dm.compression, 130 Config: dm.config, 131 SizePDU: dm.sizePDU, 132 MaxHdrSize: dm.maxHdrSize, 133 }, 134 Ntype: core.Targets, 135 Multiplier: dm.multiplier, 136 ManualResync: true, 137 } 138 if dm.xctn != nil { 139 dataArgs.Extra.SenderID = dm.xctn.ID() 140 } 141 dm.data.streams = New(dm.data.client, dataArgs) 142 if dm.useACKs() { 143 ackArgs := Args{ 144 Net: dm.ack.net, 145 Trname: dm.ack.trname, 146 Extra: &transport.Extra{Config: dm.config}, 147 Ntype: core.Targets, 148 ManualResync: true, 149 } 150 if dm.xctn != nil { 151 ackArgs.Extra.SenderID = dm.xctn.ID() 152 } 153 dm.ack.streams = New(dm.ack.client, ackArgs) 154 } 155 dm.stage.opened.Store(true) 156 } 157 158 func (dm *DataMover) String() string { 159 s := "pre-or-post-" 160 switch { 161 case dm.stage.opened.Load(): 162 s = "open-" 163 case dm.stage.regred.Load(): 164 s = "reg-" // not open yet or closed but not unreg-ed yet 165 } 166 if dm.data.streams == nil { 167 return "dm-nil-" + s 168 } 169 if dm.data.streams.UsePDU() { 170 return "dm-pdu-" + s + dm.data.streams.Trname() 171 } 172 return "dm-" + s + dm.data.streams.Trname() 173 } 174 175 // quiesce *local* Rx 176 func (dm *DataMover) Quiesce(d time.Duration) core.QuiRes { 177 return dm.xctn.Quiesce(d, dm.quicb) 178 } 179 180 func (dm *DataMover) Close(err error) { 181 if dm == nil { 182 return 183 } 184 if !dm.stage.opened.CAS(true, false) { 185 return 186 } 187 if err == nil && dm.xctn != nil && dm.xctn.IsAborted() { 188 err = dm.xctn.AbortErr() 189 } 190 // nil: close gracefully via `fin`, otherwise abort 191 dm.data.streams.Close(err == nil) 192 if dm.useACKs() { 193 dm.ack.streams.Close(err == nil) 194 } 195 } 196 197 func (dm *DataMover) Abort() { 198 dm.data.streams.Abort() 199 if dm.useACKs() { 200 dm.ack.streams.Abort() 201 } 202 } 203 204 func (dm *DataMover) UnregRecv() { 205 if dm == nil { 206 return 207 } 208 if !dm.stage.regred.CAS(true, false) { 209 return // e.g., 2PC (begin => abort) sequence with no Open 210 } 211 if dm.xctn != nil { 212 dm.Quiesce(dm.config.Transport.QuiesceTime.D()) 213 } 214 if err := transport.Unhandle(dm.data.trname); err != nil { 215 nlog.Errorln(err) 216 } 217 if dm.useACKs() { 218 if err := transport.Unhandle(dm.ack.trname); err != nil { 219 nlog.Errorln(err) 220 } 221 } 222 } 223 224 func (dm *DataMover) Send(obj *transport.Obj, roc cos.ReadOpenCloser, tsi *meta.Snode) (err error) { 225 err = dm.data.streams.Send(obj, roc, tsi) 226 if err == nil && !transport.ReservedOpcode(obj.Hdr.Opcode) { 227 dm.xctn.OutObjsAdd(1, obj.Size()) 228 } 229 return 230 } 231 232 func (dm *DataMover) ACK(hdr *transport.ObjHdr, cb transport.ObjSentCB, tsi *meta.Snode) error { 233 return dm.ack.streams.Send(&transport.Obj{Hdr: *hdr, Callback: cb}, nil, tsi) 234 } 235 236 func (dm *DataMover) Bcast(obj *transport.Obj, roc cos.ReadOpenCloser) error { 237 return dm.data.streams.Send(obj, roc) 238 } 239 240 // 241 // private 242 // 243 244 func (dm *DataMover) quicb(_ time.Duration /*accum. sleep time*/) core.QuiRes { 245 if dm.stage.laterx.CAS(true, false) { 246 return core.QuiActive 247 } 248 return core.QuiInactiveCB 249 } 250 251 func (dm *DataMover) wrapRecvData(hdr *transport.ObjHdr, reader io.Reader, err error) error { 252 if hdr.Bck.Name != "" && hdr.ObjName != "" && hdr.ObjAttrs.Size >= 0 { 253 dm.xctn.InObjsAdd(1, hdr.ObjAttrs.Size) 254 } 255 // NOTE: in re (hdr.ObjAttrs.Size < 0) see transport.UsePDU() 256 257 dm.stage.laterx.Store(true) 258 return dm.data.recv(hdr, reader, err) 259 } 260 261 func (dm *DataMover) wrapRecvACK(hdr *transport.ObjHdr, reader io.Reader, err error) error { 262 dm.stage.laterx.Store(true) 263 return dm.ack.recv(hdr, reader, err) 264 }