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 }