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  }