github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/tgtimpl.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  	"context"
     9  	"errors"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"time"
    14  
    15  	"github.com/NVIDIA/aistore/ais/backend"
    16  	"github.com/NVIDIA/aistore/api/apc"
    17  	"github.com/NVIDIA/aistore/cmn"
    18  	"github.com/NVIDIA/aistore/cmn/cos"
    19  	"github.com/NVIDIA/aistore/cmn/debug"
    20  	"github.com/NVIDIA/aistore/cmn/mono"
    21  	"github.com/NVIDIA/aistore/cmn/nlog"
    22  	"github.com/NVIDIA/aistore/core"
    23  	"github.com/NVIDIA/aistore/core/meta"
    24  	"github.com/NVIDIA/aistore/fs"
    25  	"github.com/NVIDIA/aistore/memsys"
    26  	"github.com/NVIDIA/aistore/stats"
    27  	"github.com/NVIDIA/aistore/transport/bundle"
    28  	"github.com/NVIDIA/aistore/xact/xreg"
    29  )
    30  
    31  // interface guard
    32  var _ core.Target = (*target)(nil)
    33  
    34  func (t *target) FSHC(err error, path string) { t.fsErr(err, path) }
    35  func (t *target) PageMM() *memsys.MMSA        { return t.gmm }
    36  func (t *target) ByteMM() *memsys.MMSA        { return t.smm }
    37  
    38  func (*target) DataClient() *http.Client { return g.client.data }
    39  
    40  func (*target) GetAllRunning(inout *core.AllRunningInOut, periodic bool) {
    41  	xreg.GetAllRunning(inout, periodic)
    42  }
    43  
    44  func (t *target) Health(si *meta.Snode, timeout time.Duration, query url.Values) ([]byte, int, error) {
    45  	return t.reqHealth(si, timeout, query, t.owner.smap.get())
    46  }
    47  
    48  func (t *target) Backend(bck *meta.Bck) core.Backend {
    49  	if bck.IsRemoteAIS() {
    50  		return t.backend[apc.AIS]
    51  	}
    52  	if bck.IsHTTP() {
    53  		return t.backend[apc.HTTP]
    54  	}
    55  	provider := bck.Provider
    56  	if bck.Props != nil {
    57  		provider = bck.RemoteBck().Provider
    58  	}
    59  	config := cmn.GCO.Get()
    60  	if _, ok := config.Backend.Providers[provider]; ok {
    61  		bp, k := t.backend[provider]
    62  		debug.Assert(k, provider)
    63  		if bp != nil {
    64  			return bp
    65  		}
    66  		// nil when configured & not-built
    67  	}
    68  	c, _ := backend.NewDummyBackend(t)
    69  	return c
    70  }
    71  
    72  func (t *target) PutObject(lom *core.LOM, params *core.PutParams) error {
    73  	debug.Assert(params.WorkTag != "" && !params.Atime.IsZero())
    74  	workFQN := fs.CSM.Gen(lom, fs.WorkfileType, params.WorkTag)
    75  	poi := allocPOI()
    76  	{
    77  		poi.t = t
    78  		poi.lom = lom
    79  		poi.config = cmn.GCO.Get()
    80  		poi.r = params.Reader
    81  		poi.workFQN = workFQN
    82  		poi.atime = params.Atime.UnixNano()
    83  		poi.xctn = params.Xact
    84  		poi.size = params.Size
    85  		poi.owt = params.OWT
    86  		poi.skipEC = params.SkipEC
    87  		poi.coldGET = params.ColdGET
    88  	}
    89  	if poi.owt != cmn.OwtPut {
    90  		poi.cksumToUse = params.Cksum
    91  	}
    92  	_, err := poi.putObject()
    93  	freePOI(poi)
    94  	debug.Assert(err != nil || params.Size <= 0 || params.Size == lom.SizeBytes(true), lom.String(), params.Size, lom.SizeBytes(true))
    95  	return err
    96  }
    97  
    98  func (t *target) FinalizeObj(lom *core.LOM, workFQN string, xctn core.Xact, owt cmn.OWT) (ecode int, err error) {
    99  	if err = cos.Stat(workFQN); err != nil {
   100  		return
   101  	}
   102  	poi := allocPOI()
   103  	{
   104  		poi.t = t
   105  		poi.atime = time.Now().UnixNano()
   106  		poi.lom = lom
   107  		poi.workFQN = workFQN
   108  		poi.owt = owt
   109  		poi.xctn = xctn
   110  	}
   111  	ecode, err = poi.finalize()
   112  	freePOI(poi)
   113  	return
   114  }
   115  
   116  func (t *target) EvictObject(lom *core.LOM) (ecode int, err error) {
   117  	ecode, err = t.DeleteObject(lom, true /*evict*/)
   118  	return
   119  }
   120  
   121  func (t *target) HeadObjT2T(lom *core.LOM, si *meta.Snode) bool {
   122  	return t.headt2t(lom, si, t.owner.smap.get())
   123  }
   124  
   125  // CopyObject:
   126  // - either creates a full replica of the source object (the `lom` argument)
   127  // - or transforms the object
   128  //
   129  // In both cases, the result is placed at the `params`-defined destination
   130  // in accordance with the configured destination bucket policies.
   131  //
   132  // Destination object _may_ have a different name and _may_ be located in a different bucket.
   133  //
   134  // Scenarios include (but are not limited to):
   135  //   - if both src and dst LOMs are from local buckets the copying then takes place between AIS targets
   136  //     (of this same cluster);
   137  //   - if the src is located in a remote bucket, we always first make sure it is also present in
   138  //     the AIS cluster (by performing a cold GET if need be).
   139  //   - if the dst is cloud, we perform a regular PUT logic thus also making sure that the new
   140  //     replica gets created in the cloud bucket of _this_ AIS cluster.
   141  func (t *target) CopyObject(lom *core.LOM, dm core.DM, params *core.CopyParams) (size int64, err error) {
   142  	coi := (*copyOI)(params)
   143  	// defaults
   144  	coi.OWT = cmn.OwtCopy
   145  	coi.Finalize = false
   146  	if coi.ObjnameTo == "" {
   147  		coi.ObjnameTo = lom.ObjName
   148  	}
   149  	realDM, ok := dm.(*bundle.DataMover) // TODO -- FIXME: eliminate typecast
   150  	debug.Assert(ok)
   151  
   152  	size, err = coi.do(t, realDM, lom)
   153  
   154  	coi.stats(size, err)
   155  	return size, err
   156  }
   157  
   158  // use `backend.GetObj` (compare w/ other instances calling `backend.GetObjReader`)
   159  func (t *target) GetCold(ctx context.Context, lom *core.LOM, owt cmn.OWT) (ecode int, err error) {
   160  	// 1. lock
   161  	switch owt {
   162  	case cmn.OwtGetPrefetchLock:
   163  		// do nothing
   164  	case cmn.OwtGetTryLock, cmn.OwtGetLock:
   165  		if owt == cmn.OwtGetTryLock {
   166  			if !lom.TryLock(true) {
   167  				if cmn.Rom.FastV(4, cos.SmoduleAIS) {
   168  					nlog.Warningf("%s: %s(%s) is busy", t, lom, owt)
   169  				}
   170  				return 0, cmn.ErrSkip // e.g. prefetch can skip it and keep on going
   171  			}
   172  		} else {
   173  			lom.Lock(true)
   174  		}
   175  	default:
   176  		// for cmn.OwtGet, see goi.getCold
   177  		debug.Assert(false, owt.String())
   178  		return http.StatusInternalServerError, errors.New("invalid " + owt.String())
   179  	}
   180  
   181  	// 2. GET remote object and store it
   182  	now := mono.NanoTime()
   183  	if ecode, err = t.Backend(lom.Bck()).GetObj(ctx, lom, owt, nil /*origReq*/); err != nil {
   184  		if owt != cmn.OwtGetPrefetchLock {
   185  			lom.Unlock(true)
   186  		}
   187  		nlog.Infoln(t.String()+":", "failed to GET remote", lom.Cname()+":", err, ecode)
   188  		return ecode, err
   189  	}
   190  
   191  	// 3. unlock
   192  	switch owt {
   193  	case cmn.OwtGetPrefetchLock:
   194  		// do nothing
   195  	case cmn.OwtGetTryLock, cmn.OwtGetLock:
   196  		lom.Unlock(true)
   197  	}
   198  
   199  	// 4. stats
   200  	t.statsT.AddMany(
   201  		cos.NamedVal64{Name: stats.GetColdCount, Value: 1},
   202  		cos.NamedVal64{Name: stats.GetColdSize, Value: lom.SizeBytes()},
   203  		cos.NamedVal64{Name: stats.GetColdRwLatency, Value: mono.SinceNano(now)},
   204  	)
   205  	return 0, nil
   206  }
   207  
   208  func (t *target) GetColdBlob(params *core.BlobParams, oa *cmn.ObjAttrs) (xctn core.Xact, err error) {
   209  	debug.Assert(params.Lom != nil)
   210  	debug.Assert(params.Msg != nil)
   211  	_, xctn, err = t.blobdl(params, oa)
   212  	return xctn, err
   213  }
   214  
   215  func (t *target) Promote(params *core.PromoteParams) (ecode int, err error) {
   216  	lom := core.AllocLOM(params.ObjName)
   217  	if err = lom.InitBck(params.Bck.Bucket()); err == nil {
   218  		ecode, err = t._promote(params, lom)
   219  	}
   220  	core.FreeLOM(lom)
   221  	return
   222  }
   223  
   224  func (t *target) _promote(params *core.PromoteParams, lom *core.LOM) (ecode int, err error) {
   225  	smap := t.owner.smap.get()
   226  	tsi, local, erh := lom.HrwTarget(&smap.Smap)
   227  	if erh != nil {
   228  		return 0, erh
   229  	}
   230  	var size int64
   231  	if local {
   232  		size, ecode, err = t._promLocal(params, lom)
   233  	} else {
   234  		size, err = t._promRemote(params, lom, tsi, smap)
   235  		if err == nil && size >= 0 && params.Xact != nil {
   236  			params.Xact.OutObjsAdd(1, size)
   237  		}
   238  	}
   239  	if err != nil {
   240  		return
   241  	}
   242  	if size >= 0 && params.Xact != nil {
   243  		params.Xact.ObjsAdd(1, size) // (as initiator)
   244  	}
   245  	if params.DeleteSrc {
   246  		if errRm := cos.RemoveFile(params.SrcFQN); errRm != nil {
   247  			nlog.Errorf("%s: failed to remove promoted source %q: %v", t, params.SrcFQN, errRm)
   248  		}
   249  	}
   250  	return
   251  }
   252  
   253  func (t *target) _promLocal(params *core.PromoteParams, lom *core.LOM) (fileSize int64, ecode int, err error) {
   254  	var (
   255  		cksum     *cos.CksumHash
   256  		workFQN   string
   257  		extraCopy = true
   258  	)
   259  	fileSize = -1
   260  
   261  	if err = lom.Load(true /*cache it*/, false /*locked*/); err == nil && !params.OverwriteDst {
   262  		return
   263  	}
   264  	if params.DeleteSrc {
   265  		// To use `params.SrcFQN` as `workFQN`, make sure both are
   266  		// located on the same filesystem. About "filesystem sharing" see also:
   267  		// * https://github.com/NVIDIA/aistore/blob/main/docs/overview.md#terminology
   268  		mi, _, err := fs.FQN2Mpath(params.SrcFQN)
   269  		extraCopy = err != nil || !mi.FS.Equal(lom.Mountpath().FS)
   270  	}
   271  	if extraCopy {
   272  		workFQN = fs.CSM.Gen(lom, fs.WorkfileType, fs.WorkfilePut)
   273  		buf, slab := t.gmm.Alloc()
   274  		fileSize, cksum, err = cos.CopyFile(params.SrcFQN, workFQN, buf, lom.CksumType())
   275  		slab.Free(buf)
   276  		if err != nil {
   277  			return
   278  		}
   279  		lom.SetCksum(cksum.Clone())
   280  	} else {
   281  		// avoid extra copy: use the source as `workFQN`
   282  		var fi os.FileInfo
   283  		fi, err = os.Stat(params.SrcFQN)
   284  		if err != nil {
   285  			if os.IsNotExist(err) {
   286  				err = nil
   287  			}
   288  			return
   289  		}
   290  
   291  		fileSize = fi.Size()
   292  		workFQN = params.SrcFQN
   293  		if params.Cksum != nil {
   294  			lom.SetCksum(params.Cksum) // already computed somewhere else, use it
   295  		} else {
   296  			clone := lom.CloneMD(params.SrcFQN)
   297  			if cksum, err = clone.ComputeCksum(lom.CksumType()); err != nil {
   298  				core.FreeLOM(clone)
   299  				return
   300  			}
   301  			lom.SetCksum(cksum.Clone())
   302  			core.FreeLOM(clone)
   303  		}
   304  	}
   305  	if params.Cksum != nil && cksum != nil {
   306  		if !cksum.Equal(params.Cksum) {
   307  			err = cos.NewErrDataCksum(
   308  				cksum.Clone(),
   309  				params.Cksum,
   310  				params.SrcFQN+" => "+lom.String() /*detail*/)
   311  			return
   312  		}
   313  	}
   314  	poi := allocPOI()
   315  	{
   316  		poi.atime = time.Now().UnixNano()
   317  		poi.t = t
   318  		poi.config = params.Config
   319  		poi.lom = lom
   320  		poi.workFQN = workFQN
   321  		poi.owt = cmn.OwtPromote
   322  		poi.xctn = params.Xact
   323  	}
   324  	lom.SetSize(fileSize)
   325  	ecode, err = poi.finalize()
   326  	freePOI(poi)
   327  	return
   328  }
   329  
   330  // TODO: use DM streams
   331  // TODO: Xact.InObjsAdd on the receive side
   332  func (t *target) _promRemote(params *core.PromoteParams, lom *core.LOM, tsi *meta.Snode, smap *smapX) (int64, error) {
   333  	lom.FQN = params.SrcFQN
   334  
   335  	// when not overwriting check w/ remote target first (and separately)
   336  	if !params.OverwriteDst && t.headt2t(lom, tsi, smap) {
   337  		return -1, nil
   338  	}
   339  
   340  	coiParams := core.AllocCOI()
   341  	{
   342  		coiParams.BckTo = lom.Bck()
   343  		coiParams.OWT = cmn.OwtPromote
   344  		coiParams.Xact = params.Xact
   345  		coiParams.Config = params.Config
   346  	}
   347  	coi := (*copyOI)(coiParams)
   348  	size, err := coi.send(t, nil /*DM*/, lom, lom.ObjName, tsi)
   349  	core.FreeCOI(coiParams)
   350  
   351  	return size, err
   352  }
   353  
   354  //
   355  // implements health.fspathDispatcher interface
   356  //
   357  
   358  func (t *target) DisableMpath(mpath, reason string) (err error) {
   359  	nlog.Warningf("Disabling mountpath %s: %s", mpath, reason)
   360  	_, err = t.fsprg.disableMpath(mpath, true /*dont-resilver*/) // NOTE: not resilvering upon FSCH calling
   361  	return
   362  }