github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/transport/bundle/dmover.go (about)

     1  // Package bundle provides multi-streaming transport with the functionality
     2  // to dynamically (un)register receive endpoints, establish long-lived flows, and more.
     3  /*
     4   * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
     5   */
     6  package bundle
     7  
     8  import (
     9  	"fmt"
    10  	"io"
    11  	"time"
    12  
    13  	"github.com/NVIDIA/aistore/api/apc"
    14  	"github.com/NVIDIA/aistore/cmn"
    15  	"github.com/NVIDIA/aistore/cmn/atomic"
    16  	"github.com/NVIDIA/aistore/cmn/cos"
    17  	"github.com/NVIDIA/aistore/cmn/debug"
    18  	"github.com/NVIDIA/aistore/cmn/nlog"
    19  	"github.com/NVIDIA/aistore/core"
    20  	"github.com/NVIDIA/aistore/core/meta"
    21  	"github.com/NVIDIA/aistore/transport"
    22  )
    23  
    24  type (
    25  	DataMover struct {
    26  		data struct {
    27  			client  transport.Client
    28  			recv    transport.RecvObj
    29  			streams *Streams
    30  			trname  string
    31  			net     string // one of cmn.KnownNetworks, empty defaults to cmn.NetIntraData
    32  		}
    33  		ack struct {
    34  			client  transport.Client
    35  			recv    transport.RecvObj
    36  			streams *Streams
    37  			trname  string
    38  			net     string // one of cmn.KnownNetworks, empty defaults to cmn.NetIntraControl
    39  		}
    40  		xctn        core.Xact
    41  		config      *cmn.Config
    42  		compression string // enum { apc.CompressNever, ... }
    43  		multiplier  int
    44  		owt         cmn.OWT
    45  		stage       struct {
    46  			regred atomic.Bool
    47  			opened atomic.Bool
    48  			laterx atomic.Bool
    49  		}
    50  		sizePDU    int32
    51  		maxHdrSize int32
    52  	}
    53  	// additional (and optional) params for new data mover
    54  	Extra struct {
    55  		RecvAck     transport.RecvObj
    56  		Config      *cmn.Config
    57  		Compression string
    58  		Multiplier  int
    59  		SizePDU     int32
    60  		MaxHdrSize  int32
    61  	}
    62  )
    63  
    64  var _ core.DM = (*DataMover)(nil) // via t.CopyObject()
    65  
    66  // In re `owt` (below): data mover passes it to the target's `PutObject`
    67  // to properly finalize received payload.
    68  // For DMs that do not create new objects (e.g, rebalance) `owt` should
    69  // be set to `OwtMigrateRepl`; all others are expected to have `OwtPut` (see e.g, CopyBucket).
    70  
    71  func NewDataMover(trname string, recvCB transport.RecvObj, owt cmn.OWT, extra Extra) (*DataMover, error) {
    72  	debug.Assert(extra.Config != nil)
    73  	dm := &DataMover{config: extra.Config}
    74  	dm.owt = owt
    75  	dm.multiplier = extra.Multiplier
    76  	dm.sizePDU, dm.maxHdrSize = extra.SizePDU, extra.MaxHdrSize
    77  	switch extra.Compression {
    78  	case "":
    79  		dm.compression = apc.CompressNever
    80  	case apc.CompressAlways, apc.CompressNever:
    81  		dm.compression = extra.Compression
    82  	default:
    83  		return nil, fmt.Errorf("invalid compression %q", extra.Compression)
    84  	}
    85  	dm.data.trname, dm.data.recv = trname, recvCB
    86  	if dm.data.net == "" {
    87  		dm.data.net = cmn.NetIntraData
    88  	}
    89  	dm.data.client = transport.NewIntraDataClient()
    90  	// ack
    91  	if dm.ack.net == "" {
    92  		dm.ack.net = cmn.NetIntraControl
    93  	}
    94  	dm.ack.recv = extra.RecvAck
    95  	if !dm.useACKs() {
    96  		return dm, nil
    97  	}
    98  	dm.ack.trname = "ack." + trname
    99  	dm.ack.client = transport.NewIntraDataClient()
   100  	return dm, nil
   101  }
   102  
   103  func (dm *DataMover) useACKs() bool { return dm.ack.recv != nil }
   104  func (dm *DataMover) NetD() string  { return dm.data.net }
   105  func (dm *DataMover) NetC() string  { return dm.ack.net }
   106  func (dm *DataMover) OWT() cmn.OWT  { return dm.owt }
   107  
   108  // xaction that drives and utilizes this data mover
   109  func (dm *DataMover) SetXact(xctn core.Xact) { dm.xctn = xctn }
   110  func (dm *DataMover) GetXact() core.Xact     { return dm.xctn }
   111  
   112  // register user's receive-data (and, optionally, receive-ack) wrappers
   113  func (dm *DataMover) RegRecv() (err error) {
   114  	if err = transport.Handle(dm.data.trname, dm.wrapRecvData); err != nil {
   115  		return
   116  	}
   117  	if dm.useACKs() {
   118  		err = transport.Handle(dm.ack.trname, dm.wrapRecvACK)
   119  	}
   120  	dm.stage.regred.Store(true)
   121  	return
   122  }
   123  
   124  func (dm *DataMover) Open() {
   125  	dataArgs := Args{
   126  		Net:    dm.data.net,
   127  		Trname: dm.data.trname,
   128  		Extra: &transport.Extra{
   129  			Compression: dm.compression,
   130  			Config:      dm.config,
   131  			SizePDU:     dm.sizePDU,
   132  			MaxHdrSize:  dm.maxHdrSize,
   133  		},
   134  		Ntype:        core.Targets,
   135  		Multiplier:   dm.multiplier,
   136  		ManualResync: true,
   137  	}
   138  	if dm.xctn != nil {
   139  		dataArgs.Extra.SenderID = dm.xctn.ID()
   140  	}
   141  	dm.data.streams = New(dm.data.client, dataArgs)
   142  	if dm.useACKs() {
   143  		ackArgs := Args{
   144  			Net:          dm.ack.net,
   145  			Trname:       dm.ack.trname,
   146  			Extra:        &transport.Extra{Config: dm.config},
   147  			Ntype:        core.Targets,
   148  			ManualResync: true,
   149  		}
   150  		if dm.xctn != nil {
   151  			ackArgs.Extra.SenderID = dm.xctn.ID()
   152  		}
   153  		dm.ack.streams = New(dm.ack.client, ackArgs)
   154  	}
   155  	dm.stage.opened.Store(true)
   156  }
   157  
   158  func (dm *DataMover) String() string {
   159  	s := "pre-or-post-"
   160  	switch {
   161  	case dm.stage.opened.Load():
   162  		s = "open-"
   163  	case dm.stage.regred.Load():
   164  		s = "reg-" // not open yet or closed but not unreg-ed yet
   165  	}
   166  	if dm.data.streams == nil {
   167  		return "dm-nil-" + s
   168  	}
   169  	if dm.data.streams.UsePDU() {
   170  		return "dm-pdu-" + s + dm.data.streams.Trname()
   171  	}
   172  	return "dm-" + s + dm.data.streams.Trname()
   173  }
   174  
   175  // quiesce *local* Rx
   176  func (dm *DataMover) Quiesce(d time.Duration) core.QuiRes {
   177  	return dm.xctn.Quiesce(d, dm.quicb)
   178  }
   179  
   180  func (dm *DataMover) Close(err error) {
   181  	if dm == nil {
   182  		return
   183  	}
   184  	if !dm.stage.opened.CAS(true, false) {
   185  		return
   186  	}
   187  	if err == nil && dm.xctn != nil && dm.xctn.IsAborted() {
   188  		err = dm.xctn.AbortErr()
   189  	}
   190  	// nil: close gracefully via `fin`, otherwise abort
   191  	dm.data.streams.Close(err == nil)
   192  	if dm.useACKs() {
   193  		dm.ack.streams.Close(err == nil)
   194  	}
   195  }
   196  
   197  func (dm *DataMover) Abort() {
   198  	dm.data.streams.Abort()
   199  	if dm.useACKs() {
   200  		dm.ack.streams.Abort()
   201  	}
   202  }
   203  
   204  func (dm *DataMover) UnregRecv() {
   205  	if dm == nil {
   206  		return
   207  	}
   208  	if !dm.stage.regred.CAS(true, false) {
   209  		return // e.g., 2PC (begin => abort) sequence with no Open
   210  	}
   211  	if dm.xctn != nil {
   212  		dm.Quiesce(dm.config.Transport.QuiesceTime.D())
   213  	}
   214  	if err := transport.Unhandle(dm.data.trname); err != nil {
   215  		nlog.Errorln(err)
   216  	}
   217  	if dm.useACKs() {
   218  		if err := transport.Unhandle(dm.ack.trname); err != nil {
   219  			nlog.Errorln(err)
   220  		}
   221  	}
   222  }
   223  
   224  func (dm *DataMover) Send(obj *transport.Obj, roc cos.ReadOpenCloser, tsi *meta.Snode) (err error) {
   225  	err = dm.data.streams.Send(obj, roc, tsi)
   226  	if err == nil && !transport.ReservedOpcode(obj.Hdr.Opcode) {
   227  		dm.xctn.OutObjsAdd(1, obj.Size())
   228  	}
   229  	return
   230  }
   231  
   232  func (dm *DataMover) ACK(hdr *transport.ObjHdr, cb transport.ObjSentCB, tsi *meta.Snode) error {
   233  	return dm.ack.streams.Send(&transport.Obj{Hdr: *hdr, Callback: cb}, nil, tsi)
   234  }
   235  
   236  func (dm *DataMover) Bcast(obj *transport.Obj, roc cos.ReadOpenCloser) error {
   237  	return dm.data.streams.Send(obj, roc)
   238  }
   239  
   240  //
   241  // private
   242  //
   243  
   244  func (dm *DataMover) quicb(_ time.Duration /*accum. sleep time*/) core.QuiRes {
   245  	if dm.stage.laterx.CAS(true, false) {
   246  		return core.QuiActive
   247  	}
   248  	return core.QuiInactiveCB
   249  }
   250  
   251  func (dm *DataMover) wrapRecvData(hdr *transport.ObjHdr, reader io.Reader, err error) error {
   252  	if hdr.Bck.Name != "" && hdr.ObjName != "" && hdr.ObjAttrs.Size >= 0 {
   253  		dm.xctn.InObjsAdd(1, hdr.ObjAttrs.Size)
   254  	}
   255  	// NOTE: in re (hdr.ObjAttrs.Size < 0) see transport.UsePDU()
   256  
   257  	dm.stage.laterx.Store(true)
   258  	return dm.data.recv(hdr, reader, err)
   259  }
   260  
   261  func (dm *DataMover) wrapRecvACK(hdr *transport.ObjHdr, reader io.Reader, err error) error {
   262  	dm.stage.laterx.Store(true)
   263  	return dm.ack.recv(hdr, reader, err)
   264  }