github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/tgtfcold.go (about) 1 // Package ais provides core functionality for the AIStore object storage. 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package ais 6 7 import ( 8 "io" 9 "os" 10 11 "github.com/NVIDIA/aistore/ais/s3" 12 "github.com/NVIDIA/aistore/cmn" 13 "github.com/NVIDIA/aistore/cmn/cos" 14 "github.com/NVIDIA/aistore/cmn/debug" 15 "github.com/NVIDIA/aistore/cmn/feat" 16 "github.com/NVIDIA/aistore/cmn/mono" 17 "github.com/NVIDIA/aistore/cmn/nlog" 18 "github.com/NVIDIA/aistore/core" 19 "github.com/NVIDIA/aistore/ec" 20 "github.com/NVIDIA/aistore/fs" 21 "github.com/NVIDIA/aistore/memsys" 22 "github.com/NVIDIA/aistore/stats" 23 ) 24 25 const ftcg = "Warning: failed to cold-GET" 26 27 func (goi *getOI) coldReopen(res *core.GetReaderResult) error { 28 var ( 29 t, lom = goi.t, goi.lom 30 revert string 31 ) 32 if goi.verchanged { 33 revert = fs.CSM.Gen(lom, fs.WorkfileType, fs.WorkfileColdget) 34 if err := os.Rename(lom.FQN, revert); err != nil { 35 nlog.Errorln("failed to rename prev. version - proceeding anyway", lom.FQN, "=>", revert) 36 revert = "" 37 } 38 } 39 lmfh, err := lom.CreateFile(lom.FQN) 40 if err != nil { 41 cos.Close(res.R) 42 goi._cleanup(revert, nil, nil, nil, err, "(fcreate)") 43 return err 44 } 45 46 // read remote, write local 47 var ( 48 written int64 49 buf, slab = t.gmm.AllocSize(min(res.Size, memsys.DefaultBuf2Size)) 50 cksumH = cos.NewCksumHash(lom.CksumConf().Type) 51 mw = cos.NewWriterMulti(lmfh, cksumH.H) 52 ) 53 written, err = cos.CopyBuffer(mw, res.R, buf) 54 cos.Close(res.R) 55 56 if err != nil { 57 goi._cleanup(revert, lmfh, buf, slab, err, "(rr/wl)") 58 return err 59 } 60 debug.Assertf(written == res.Size, "%s: remote-size %d != %d written", lom.Cname(), res.Size, written) 61 62 if lom.IsFeatureSet(feat.FsyncPUT) { 63 // fsync (flush) 64 if err = lmfh.Sync(); err != nil { 65 goi._cleanup(revert, lmfh, buf, slab, err, "(fsync)") 66 return err 67 } 68 } 69 err = lmfh.Close() 70 lmfh = nil 71 if err != nil { 72 goi._cleanup(revert, lmfh, buf, slab, err, "(fclose)") 73 return err 74 } 75 76 // lom (main replica) 77 lom.SetSize(written) 78 cksumH.Finalize() 79 lom.SetCksum(&cksumH.Cksum) // and return via whdr as well 80 if lom.HasCopies() { 81 if err := lom.DelAllCopies(); err != nil { 82 nlog.Errorln(err) 83 } 84 } 85 if err = lom.PersistMain(); err != nil { 86 goi._cleanup(revert, lmfh, buf, slab, err, "(persist)") 87 return err 88 } 89 90 // reopen & transmit --- 91 if lmfh, err = lom.OpenFile(); err != nil { 92 goi._cleanup(revert, nil, buf, slab, err, "(seek)") 93 return err 94 } 95 var ( 96 hrng *htrange 97 size = lom.SizeBytes() 98 reader io.Reader = lmfh 99 whdr = goi.w.Header() 100 ) 101 if goi.ranges.Range != "" { 102 // (not here if range checksum enabled) 103 rsize := goi.lom.SizeBytes() 104 if goi.ranges.Size > 0 { 105 rsize = goi.ranges.Size 106 } 107 if hrng, _, err = goi.rngToHeader(whdr, rsize); err != nil { 108 goi._cleanup(revert, lmfh, buf, slab, err, "(seek)") 109 return err 110 } 111 size = hrng.Length 112 reader = io.NewSectionReader(lmfh, hrng.Start, hrng.Length) 113 } 114 115 whdr.Set(cos.HdrContentType, cos.ContentBinary) 116 cmn.ToHeader(lom.ObjAttrs(), whdr, size) 117 if goi.dpq.isS3 { 118 // (expecting user to set bucket checksum = md5) 119 s3.SetEtag(whdr, goi.lom) 120 } 121 122 written, err = cos.CopyBuffer(goi.w, reader, buf) 123 if err != nil { 124 goi._cleanup(revert, lmfh, buf, slab, err, "(transmit)") 125 return errSendingResp 126 } 127 debug.Assertf(written == size, "%s: transmit-size %d != %d expected", lom.Cname(), written, size) 128 129 cos.Close(lmfh) 130 slab.Free(buf) 131 132 return goi._fini(revert, res.Size, written) 133 } 134 135 // stats and redundancy (compare w/ goi.txfini) 136 func (goi *getOI) _fini(revert string, fullSize, txSize int64) error { 137 // cold get stats 138 goi.t.statsT.AddMany( 139 cos.NamedVal64{Name: stats.GetColdCount, Value: 1}, 140 cos.NamedVal64{Name: stats.GetColdSize, Value: fullSize}, 141 cos.NamedVal64{Name: stats.GetColdRwLatency, Value: mono.SinceNano(goi.ltime)}, 142 ) 143 144 lom := goi.lom 145 if revert != "" { 146 if err := cos.RemoveFile(revert); err != nil { 147 nlog.InfoDepth(1, ftcg+"(rm-revert)", lom, err) 148 } 149 } 150 151 // make copies and slices (async) 152 if err := ec.ECM.EncodeObject(lom, nil); err != nil && err != ec.ErrorECDisabled { 153 nlog.InfoDepth(1, ftcg+"(ec)", lom, err) 154 } 155 goi.t.putMirror(lom) 156 157 // load 158 if err := lom.Load(true /*cache it*/, true /*locked*/); err != nil { 159 goi.lom.Unlock(true) 160 nlog.InfoDepth(1, ftcg+"(load)", lom, err) // (unlikely) 161 return errSendingResp 162 } 163 debug.Assert(lom.SizeBytes() == fullSize) 164 goi.lom.Unlock(true) 165 166 // regular get stats 167 goi.stats(txSize) 168 return nil 169 } 170 171 func (goi *getOI) _cleanup(revert string, lmfh *os.File, buf []byte, slab *memsys.Slab, err error, tag string) { 172 if lmfh != nil { 173 lmfh.Close() 174 } 175 if slab != nil { 176 slab.Free(buf) 177 } 178 if err != nil { 179 goi.lom.Remove() 180 if revert != "" { 181 if errV := os.Rename(revert, goi.lom.FQN); errV != nil { 182 nlog.Infoln(ftcg+tag+"(revert)", errV) 183 } 184 } 185 nlog.InfoDepth(1, ftcg+tag, err) 186 } 187 goi.lom.Unlock(true) 188 } 189 190 // NOTE: 191 // Streaming cold GET feature (`feat.StreamingColdGET`) puts response header on the wire _prior_ 192 // to finalizing in-cluster object. Use it at your own risk. 193 func (goi *getOI) coldStream(res *core.GetReaderResult) error { 194 var ( 195 t, lom = goi.t, goi.lom 196 revert string 197 ) 198 if goi.verchanged { 199 revert = fs.CSM.Gen(lom, fs.WorkfileType, fs.WorkfileColdget) 200 if err := os.Rename(lom.FQN, revert); err != nil { 201 nlog.Errorln("failed to rename prev. version - proceeding anyway", lom.FQN, "=>", revert) 202 revert = "" 203 } 204 } 205 lmfh, err := lom.CreateFile(lom.FQN) 206 if err != nil { 207 cos.Close(res.R) 208 goi._cleanup(revert, nil, nil, nil, err, "(fcreate)") 209 return err 210 } 211 212 // read remote, write local, transmit -- 213 214 var ( 215 written int64 216 buf, slab = t.gmm.AllocSize(min(res.Size, memsys.DefaultBuf2Size)) 217 cksum = cos.NewCksumHash(lom.CksumConf().Type) 218 mw = cos.NewWriterMulti(goi.w, lmfh, cksum.H) 219 whdr = goi.w.Header() 220 ) 221 222 // response header 223 whdr.Set(cos.HdrContentType, cos.ContentBinary) 224 cmn.ToHeader(lom.ObjAttrs(), whdr, res.Size) 225 if goi.dpq.isS3 { 226 // (expecting user to set bucket checksum = md5) 227 s3.SetEtag(whdr, goi.lom) 228 } 229 230 written, err = cos.CopyBuffer(mw, res.R, buf) 231 cos.Close(res.R) 232 233 if err != nil { 234 goi._cleanup(revert, lmfh, buf, slab, err, "(rr/wl)") 235 return errSendingResp // NOTE: cannot return err: whdr is already on the wire 236 } 237 debug.Assertf(written == res.Size, "%s: remote-size %d != %d written/transmitted", lom.Cname(), res.Size, written) 238 239 if lom.IsFeatureSet(feat.FsyncPUT) { 240 // fsync (flush) 241 if err = lmfh.Sync(); err != nil { 242 goi._cleanup(revert, lmfh, buf, slab, err, "(fsync)") 243 return errSendingResp // ditto 244 } 245 } 246 247 err = lmfh.Close() 248 lmfh = nil 249 if err != nil { 250 goi._cleanup(revert, lmfh, buf, slab, err, "(fclose)") 251 return errSendingResp // ditto 252 } 253 254 // lom (main replica) 255 lom.SetSize(written) 256 cksum.Finalize() 257 lom.SetCksum(&cksum.Cksum) 258 if lom.HasCopies() { 259 if err := lom.DelAllCopies(); err != nil { 260 nlog.Errorln(err) 261 } 262 } 263 if err = lom.PersistMain(); err != nil { 264 goi._cleanup(revert, lmfh, buf, slab, err, "(persist)") 265 return errSendingResp 266 } 267 268 slab.Free(buf) 269 270 return goi._fini(revert, res.Size, written) 271 }