github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ais/tgtxact.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  	"fmt"
     9  	"net/http"
    10  	"strconv"
    11  	"sync"
    12  
    13  	"github.com/NVIDIA/aistore/api/apc"
    14  	"github.com/NVIDIA/aistore/cmn"
    15  	"github.com/NVIDIA/aistore/cmn/cos"
    16  	"github.com/NVIDIA/aistore/cmn/debug"
    17  	"github.com/NVIDIA/aistore/cmn/nlog"
    18  	"github.com/NVIDIA/aistore/core"
    19  	"github.com/NVIDIA/aistore/core/meta"
    20  	"github.com/NVIDIA/aistore/nl"
    21  	"github.com/NVIDIA/aistore/res"
    22  	"github.com/NVIDIA/aistore/xact"
    23  	"github.com/NVIDIA/aistore/xact/xreg"
    24  	"github.com/NVIDIA/aistore/xact/xs"
    25  )
    26  
    27  // TODO: uplift via higher-level query and similar (#668)
    28  
    29  // verb /v1/xactions
    30  func (t *target) xactHandler(w http.ResponseWriter, r *http.Request) {
    31  	if _, err := t.parseURL(w, r, apc.URLPathXactions.L, 0, true); err != nil {
    32  		return
    33  	}
    34  	switch r.Method {
    35  	case http.MethodGet:
    36  		t.httpxget(w, r)
    37  	case http.MethodPut:
    38  		t.httpxput(w, r)
    39  	case http.MethodPost:
    40  		t.httpxpost(w, r)
    41  	default:
    42  		cmn.WriteErr405(w, r, http.MethodGet, http.MethodPut)
    43  	}
    44  }
    45  
    46  //
    47  // GET
    48  //
    49  
    50  func (t *target) httpxget(w http.ResponseWriter, r *http.Request) {
    51  	var (
    52  		xactMsg xact.QueryMsg
    53  		bck     *meta.Bck
    54  		query   = r.URL.Query()
    55  		what    = query.Get(apc.QparamWhat)
    56  	)
    57  	if uuid := query.Get(apc.QparamUUID); uuid != "" {
    58  		t.xget(w, r, what, uuid)
    59  		return
    60  	}
    61  	if cmn.ReadJSON(w, r, &xactMsg) != nil {
    62  		return
    63  	}
    64  	debug.Assert(xactMsg.Kind == "" || xact.IsValidKind(xactMsg.Kind), xactMsg.Kind)
    65  
    66  	//
    67  	// TODO: add user option to return idle xactions (separately)
    68  	//
    69  	if what == apc.WhatAllRunningXacts {
    70  		var inout = core.AllRunningInOut{Kind: xactMsg.Kind}
    71  		xreg.GetAllRunning(&inout, false /*periodic*/)
    72  		t.writeJSON(w, r, inout.Running, what)
    73  		return
    74  	}
    75  
    76  	if what != apc.WhatQueryXactStats {
    77  		t.writeErrf(w, r, fmtUnknownQue, what)
    78  		return
    79  	}
    80  
    81  	if xactMsg.Bck.Name != "" {
    82  		bck = meta.CloneBck(&xactMsg.Bck)
    83  		if err := bck.Init(t.owner.bmd); err != nil {
    84  			t.writeErr(w, r, err, http.StatusNotFound, Silent)
    85  			return
    86  		}
    87  	}
    88  	xactQuery := xreg.Flt{
    89  		ID: xactMsg.ID, Kind: xactMsg.Kind, Bck: bck, OnlyRunning: xactMsg.OnlyRunning,
    90  	}
    91  	t.xquery(w, r, what, xactQuery)
    92  }
    93  
    94  func (t *target) httpxput(w http.ResponseWriter, r *http.Request) {
    95  	var (
    96  		xargs xact.ArgsMsg
    97  		bck   *meta.Bck
    98  	)
    99  	msg, err := t.readActionMsg(w, r)
   100  	if err != nil {
   101  		return
   102  	}
   103  	if err := cos.MorphMarshal(msg.Value, &xargs); err != nil {
   104  		t.writeErrf(w, r, cmn.FmtErrMorphUnmarshal, t.si, msg.Action, msg.Value, err)
   105  		return
   106  	}
   107  	if !xargs.Bck.IsEmpty() {
   108  		bck = meta.CloneBck(&xargs.Bck)
   109  		if err := bck.Init(t.owner.bmd); err != nil && msg.Action != apc.ActXactStop {
   110  			// proceed anyway to stop
   111  			t.writeErr(w, r, err)
   112  			return
   113  		}
   114  	}
   115  	if cmn.Rom.FastV(4, cos.SmoduleAIS) {
   116  		nlog.Infoln(msg.Action, xargs.String())
   117  	}
   118  	switch msg.Action {
   119  	case apc.ActXactStart:
   120  		debug.Assert(xact.IsValidKind(xargs.Kind), xargs.String())
   121  		if xargs.Kind == apc.ActPrefetchObjects {
   122  			// TODO: consider adding `Value any` to generic `xact.ArgsMsg`
   123  			ecode, err := t.runPrefetch(xargs.ID, bck, &apc.PrefetchMsg{})
   124  			if err != nil {
   125  				t.writeErr(w, r, err, ecode)
   126  			}
   127  			return
   128  		}
   129  		// all other "startables"
   130  		xid, err := t.xstart(&xargs, bck, msg)
   131  		if err != nil {
   132  			t.writeErr(w, r, err)
   133  			return
   134  		}
   135  		if l := len(xid); l > 0 {
   136  			w.Header().Set(cos.HdrContentLength, strconv.Itoa(l))
   137  			w.Write([]byte(xid))
   138  		}
   139  	case apc.ActXactStop:
   140  		debug.Assert(xact.IsValidKind(xargs.Kind) || xact.IsValidUUID(xargs.ID), xargs.String())
   141  		err := cmn.ErrXactUserAbort
   142  		if msg.Name == cmn.ErrXactICNotifAbort.Error() {
   143  			err = cmn.ErrXactICNotifAbort
   144  		}
   145  		flt := xreg.Flt{ID: xargs.ID, Kind: xargs.Kind, Bck: bck}
   146  		xreg.DoAbort(flt, err)
   147  	default:
   148  		t.writeErrAct(w, r, msg.Action)
   149  	}
   150  }
   151  
   152  func (t *target) xget(w http.ResponseWriter, r *http.Request, what, uuid string) {
   153  	if what != apc.WhatXactStats {
   154  		t.writeErrf(w, r, fmtUnknownQue, what)
   155  		return
   156  	}
   157  	xctn, err := xreg.GetXact(uuid)
   158  	if err != nil {
   159  		t.writeErr(w, r, err)
   160  		return
   161  	}
   162  	if xctn != nil {
   163  		t.writeJSON(w, r, xctn.Snap(), what)
   164  		return
   165  	}
   166  	err = cmn.NewErrXactNotFoundError("[" + uuid + "]")
   167  	t.writeErr(w, r, err, http.StatusNotFound, Silent)
   168  }
   169  
   170  func (t *target) xquery(w http.ResponseWriter, r *http.Request, what string, xactQuery xreg.Flt) {
   171  	stats, err := xreg.GetSnap(xactQuery)
   172  	if err == nil {
   173  		t.writeJSON(w, r, stats, what)
   174  		return
   175  	}
   176  	if cmn.IsErrXactNotFound(err) {
   177  		t.writeErr(w, r, err, http.StatusNotFound, Silent)
   178  	} else {
   179  		t.writeErr(w, r, err)
   180  	}
   181  }
   182  
   183  //
   184  // PUT
   185  //
   186  
   187  func (t *target) xstart(args *xact.ArgsMsg, bck *meta.Bck, msg *apc.ActMsg) (xid string, _ error) {
   188  	const erfmb = "global xaction %q does not require bucket (%s) - ignoring it and proceeding to start"
   189  	const erfmn = "xaction %q requires a bucket to start"
   190  
   191  	if !xact.IsValidKind(args.Kind) {
   192  		return xid, fmt.Errorf(cmn.FmtErrUnknown, t, "xaction kind", args.Kind)
   193  	}
   194  	if dtor := xact.Table[args.Kind]; dtor.Scope == xact.ScopeB && bck == nil {
   195  		return xid, fmt.Errorf(erfmn, args.Kind)
   196  	}
   197  	switch args.Kind {
   198  	// 1. global x-s
   199  	case apc.ActLRU:
   200  		if bck != nil {
   201  			nlog.Errorf(erfmb, args.Kind, bck)
   202  		}
   203  		wg := &sync.WaitGroup{}
   204  		wg.Add(1)
   205  		go t.runLRU(args.ID, wg, args.Force, args.Buckets...)
   206  		wg.Wait()
   207  	case apc.ActStoreCleanup:
   208  		wg := &sync.WaitGroup{}
   209  		wg.Add(1)
   210  		go t.runStoreCleanup(args.ID, wg, args.Buckets...)
   211  		wg.Wait()
   212  	case apc.ActResilver:
   213  		if bck != nil {
   214  			nlog.Errorf(erfmb, args.Kind, bck)
   215  		}
   216  		notif := &xact.NotifXact{
   217  			Base: nl.Base{
   218  				When: core.UponTerm,
   219  				Dsts: []string{equalIC},
   220  				F:    t.notifyTerm,
   221  			},
   222  		}
   223  		wg := &sync.WaitGroup{}
   224  		wg.Add(1)
   225  		if args.ID == "" {
   226  			args.ID = cos.GenUUID()
   227  			xid = args.ID
   228  		}
   229  		go t.runResilver(res.Args{UUID: args.ID, Notif: notif}, wg)
   230  		wg.Wait()
   231  	case apc.ActLoadLomCache:
   232  		rns := xreg.RenewBckLoadLomCache(args.ID, bck)
   233  		return xid, rns.Err
   234  	case apc.ActBlobDl:
   235  		debug.Assert(msg.Name != "")
   236  		lom := core.AllocLOM(msg.Name)
   237  		err := lom.InitBck(&args.Bck)
   238  		if err == nil {
   239  			params := &core.BlobParams{
   240  				Lom: lom,
   241  				Msg: &apc.BlobMsg{}, // default tunables when executing via x-start API
   242  			}
   243  			xid, _, err = t.blobdl(params, nil /*oa*/)
   244  		}
   245  		if err != nil {
   246  			core.FreeLOM(lom)
   247  		}
   248  		return xid, err
   249  	// 3. cannot start
   250  	case apc.ActPutCopies:
   251  		return xid, fmt.Errorf("cannot start %q (is driven by PUTs into a mirrored bucket)", args)
   252  	case apc.ActDownload, apc.ActEvictObjects, apc.ActDeleteObjects, apc.ActMakeNCopies, apc.ActECEncode:
   253  		return xid, fmt.Errorf("initiating %q must be done via a separate documented API", args)
   254  	// 4. unknown
   255  	case "":
   256  		return xid, fmt.Errorf("%q: unspecified (empty) xaction kind", args)
   257  	default:
   258  		return xid, cmn.NewErrUnsupp("start xaction", args.Kind)
   259  	}
   260  	return xid, nil
   261  }
   262  
   263  //
   264  // POST
   265  //
   266  
   267  // client: plstcx.go
   268  func (t *target) httpxpost(w http.ResponseWriter, r *http.Request) {
   269  	var (
   270  		err    error
   271  		xctn   core.Xact
   272  		amsg   *apc.ActMsg
   273  		tcomsg cmn.TCObjsMsg
   274  	)
   275  	if amsg, err = t.readActionMsg(w, r); err != nil {
   276  		return
   277  	}
   278  
   279  	xactID := amsg.Name
   280  	if xctn, err = xreg.GetXact(xactID); err != nil {
   281  		t.writeErr(w, r, err)
   282  		return
   283  	}
   284  	xtco, ok := xctn.(*xs.XactTCObjs)
   285  	debug.Assert(ok)
   286  
   287  	if err = cos.MorphMarshal(amsg.Value, &tcomsg); err != nil {
   288  		t.writeErrf(w, r, cmn.FmtErrMorphUnmarshal, t.si, "special", amsg.Value, err)
   289  		return
   290  	}
   291  	xtco.Do(&tcomsg)
   292  }