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  }