github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/core/ldp.go (about) 1 // Package core provides core metadata and in-cluster API 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package core 6 7 import ( 8 "context" 9 "net/http" 10 "time" 11 12 "github.com/NVIDIA/aistore/cmn" 13 "github.com/NVIDIA/aistore/cmn/cos" 14 "github.com/NVIDIA/aistore/cmn/debug" 15 ) 16 17 // NOTE: compare with ext/etl/dp.go 18 19 type ( 20 // data provider 21 DP interface { 22 Reader(lom *LOM, latestVer, sync bool) (reader cos.ReadOpenCloser, oah cos.OAH, err error) 23 } 24 25 LDP struct{} 26 27 // compare with `deferROC` from cmn/cos/io.go 28 deferROC struct { 29 cos.ReadOpenCloser 30 lif LIF 31 } 32 33 CRMD struct { 34 Err error 35 ObjAttrs *cmn.ObjAttrs 36 ErrCode int 37 Eq bool 38 } 39 ) 40 41 // interface guard 42 var _ DP = (*LDP)(nil) 43 44 func (r *deferROC) Close() (err error) { 45 err = r.ReadOpenCloser.Close() 46 r.lif.Unlock(false) 47 return 48 } 49 50 // is called under rlock; unlocks on fail 51 func (lom *LOM) NewDeferROC() (cos.ReadOpenCloser, error) { 52 fh, err := cos.NewFileHandle(lom.FQN) 53 if err == nil { 54 return &deferROC{fh, lom.LIF()}, nil 55 } 56 lom.Unlock(false) 57 return nil, cmn.NewErrFailedTo(T, "open", lom.Cname(), err) 58 } 59 60 // (compare with ext/etl/dp.go) 61 func (*LDP) Reader(lom *LOM, latestVer, sync bool) (cos.ReadOpenCloser, cos.OAH, error) { 62 lom.Lock(false) 63 loadErr := lom.Load(false /*cache it*/, true /*locked*/) 64 if loadErr == nil { 65 if latestVer || sync { 66 debug.Assert(lom.Bck().IsRemote(), lom.Bck().String()) // caller's responsibility 67 res := lom.CheckRemoteMD(true /* rlocked*/, sync, nil /*origReq*/) 68 if res.Err != nil { 69 lom.Unlock(false) 70 if !cos.IsNotExist(res.Err, res.ErrCode) { 71 res.Err = cmn.NewErrFailedTo(T, "head-latest", lom.Cname(), res.Err) 72 } 73 return nil, nil, res.Err 74 } 75 if !res.Eq { 76 // version changed 77 lom.Unlock(false) 78 goto remote 79 } 80 } 81 82 roc, err := lom.NewDeferROC() // keeping lock, reading local 83 return roc, lom, err 84 } 85 86 lom.Unlock(false) 87 if !cos.IsNotExist(loadErr, 0) { 88 return nil, nil, cmn.NewErrFailedTo(T, "ldp-load", lom.Cname(), loadErr) 89 } 90 if !lom.Bck().IsRemote() { 91 return nil, nil, cos.NewErrNotFound(T, lom.Cname()) 92 } 93 94 remote: 95 // GetObjReader and return remote (object) reader and oah for object metadata 96 // (compare w/ T.GetCold) 97 lom.SetAtimeUnix(time.Now().UnixNano()) 98 oah := &cmn.ObjAttrs{ 99 Ver: "", // TODO: differentiate between copying (same version) vs. transforming 100 Cksum: cos.NoneCksum, // will likely reassign (below) 101 Atime: lom.AtimeUnix(), 102 } 103 res := T.Backend(lom.Bck()).GetObjReader(context.Background(), lom, 0, 0) 104 105 if lom.Checksum() != nil { 106 oah.Cksum = lom.Checksum() 107 } else if res.ExpCksum != nil { 108 oah.Cksum = res.ExpCksum 109 } 110 oah.Size = res.Size 111 return cos.NopOpener(res.R), oah, res.Err 112 } 113 114 // NOTE: 115 // - [PRECONDITION]: `versioning.validate_warm_get` || QparamLatestVer 116 // - [Sync] when Sync option is used (via bucket config and/or `sync` argument) caller MUST take wlock or rlock 117 // - [MAY] delete remotely-deleted (non-existing) object and increment associated stats counter 118 // 119 // Returns NotFound also after having removed local replica (the Sync option) 120 func (lom *LOM) CheckRemoteMD(locked, sync bool, origReq *http.Request) (res CRMD) { 121 bck := lom.Bck() 122 if !bck.HasVersioningMD() { 123 // nothing to do with: in-cluster ais:// bucket, or a remote one 124 // that doesn't provide any versioning metadata 125 return CRMD{Eq: true} 126 } 127 128 oa, ecode, err := T.Backend(bck).HeadObj(context.Background(), lom, origReq) 129 if err == nil { 130 debug.Assert(ecode == 0, ecode) 131 return CRMD{ObjAttrs: oa, Eq: lom.Equal(oa), ErrCode: ecode} 132 } 133 134 if ecode == http.StatusNotFound { 135 err = cos.NewErrNotFound(T, lom.Cname()) 136 } 137 if !locked { 138 // return info (neq and, possibly, not-found), and be done 139 return CRMD{ErrCode: ecode, Err: err} 140 } 141 142 // rm remotely-deleted 143 if cos.IsNotExist(err, ecode) && (lom.VersionConf().Sync || sync) { 144 errDel := lom.Remove(locked /*force through rlock*/) 145 if errDel != nil { 146 ecode, err = 0, errDel 147 } else { 148 g.tstats.Inc(RemoteDeletedDelCount) 149 } 150 debug.Assert(err != nil) 151 return CRMD{ErrCode: ecode, Err: err} 152 } 153 154 lom.Uncache() 155 return CRMD{ErrCode: ecode, Err: err} 156 } 157 158 // NOTE: must be locked; NOTE: Sync == false (ie., not deleting) 159 func (lom *LOM) LoadLatest(latest bool) (oa *cmn.ObjAttrs, deleted bool, err error) { 160 debug.AssertFunc(func() bool { 161 rc, exclusive := lom.IsLocked() 162 return exclusive || rc > 0 163 }) 164 err = lom.Load(true /*cache it*/, true /*locked*/) 165 if err != nil { 166 if !cmn.IsErrObjNought(err) || !latest { 167 return nil, false, err 168 } 169 } 170 if latest { 171 res := lom.CheckRemoteMD(true /*locked*/, false /*synchronize*/, nil /*origReq*/) 172 if res.Eq { 173 debug.AssertNoErr(res.Err) 174 return nil, false, nil 175 } 176 if res.Err != nil { 177 deleted = cos.IsNotExist(res.Err, res.ErrCode) 178 return nil, deleted, res.Err 179 } 180 oa = res.ObjAttrs 181 } 182 return oa, false, nil 183 }