github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/xact/xreg/xreg.go (about) 1 // Package xreg provides registry and (renew, find) functions for AIS eXtended Actions (xactions). 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package xreg 6 7 import ( 8 "fmt" 9 "sort" 10 "sync" 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/feat" 19 "github.com/NVIDIA/aistore/cmn/nlog" 20 "github.com/NVIDIA/aistore/core" 21 "github.com/NVIDIA/aistore/core/meta" 22 "github.com/NVIDIA/aistore/hk" 23 "github.com/NVIDIA/aistore/xact" 24 ) 25 26 // TODO: some of these constants must be configurable or derived from the config 27 const ( 28 initialCap = 256 // initial capacity 29 keepOldThreshold = 256 // keep so many 30 31 waitPrevAborted = 2 * time.Second 32 waitLimitedCoex = 3 * time.Second 33 ) 34 35 type WPR int 36 37 const ( 38 WprAbort = iota + 1 39 WprUse 40 WprKeepAndStartNew 41 ) 42 43 type ( 44 Renewable interface { 45 New(args Args, bck *meta.Bck) Renewable // new xaction stub that can be `Start`-ed. 46 Start() error // starts an xaction, will be called when entry is stored into registry 47 Kind() string 48 Get() core.Xact 49 WhenPrevIsRunning(prevEntry Renewable) (action WPR, err error) 50 Bucket() *meta.Bck 51 UUID() string 52 } 53 // used in constructions 54 Args struct { 55 Custom any // Additional arguments that are specific for a given xact. 56 UUID string 57 } 58 RenewBase struct { 59 Args 60 Bck *meta.Bck 61 } 62 // simplified non-JSON QueryMsg (internal AIS use) 63 Flt struct { 64 Bck *meta.Bck 65 OnlyRunning *bool 66 ID string 67 Kind string 68 Buckets []*meta.Bck 69 } 70 ) 71 72 // private 73 type ( 74 // Represents result of renewing given xact. 75 RenewRes struct { 76 Entry Renewable // Depending on situation can be new or old entry. 77 Err error // Error that occurred during renewal. 78 UUID string // "" if a new entry has been created, ID of the existing xaction otherwise 79 } 80 // Selects subset of xactions to abort. 81 abortArgs struct { 82 err error // original cause (or reason), e.g. cmn.ErrUserAbort 83 // criteria 84 bcks []*meta.Bck // run on a slice of buckets 85 scope []int // one of { ScopeG, ScopeB, ... } enum 86 kind string // all of a kind 87 newreb bool // (rebalance is starting) vs (dtor.AbortRebRes) 88 } 89 90 entries struct { 91 active []Renewable // running entries - finished entries are gradually removed 92 roActive []Renewable // read-only copy 93 all []Renewable 94 mtx sync.RWMutex 95 } 96 // All entries in the registry. The entries are periodically cleaned up 97 // to make sure that we don't keep old entries forever. 98 registry struct { 99 renewMtx sync.RWMutex // TODO: revisit to optimiz out 100 entries entries 101 bckXacts map[string]Renewable 102 nonbckXacts map[string]Renewable 103 finDelta atomic.Int64 104 } 105 ) 106 107 // default global registry that keeps track of all running xactions 108 // In addition, the registry retains already finished xactions subject to lazy cleanup via `hk`. 109 var dreg *registry 110 111 ////////////////////// 112 // xaction registry // 113 ////////////////////// 114 115 func Init() { 116 dreg = newRegistry() 117 xact.IncFinished = dreg.incFinished 118 } 119 120 func TestReset() { dreg = newRegistry() } // tests only 121 122 func newRegistry() (r *registry) { 123 return ®istry{ 124 entries: entries{ 125 all: make([]Renewable, 0, initialCap), 126 active: make([]Renewable, 0, 32), 127 roActive: make([]Renewable, 0, 64), 128 }, 129 bckXacts: make(map[string]Renewable, 32), 130 nonbckXacts: make(map[string]Renewable, 32), 131 } 132 } 133 134 // register w/housekeeper periodic registry cleanups 135 func RegWithHK() { 136 hk.Reg("x-old"+hk.NameSuffix, dreg.hkDelOld, 0) 137 hk.Reg("x-prune-active"+hk.NameSuffix, dreg.hkPruneActive, 0) 138 } 139 140 func GetXact(uuid string) (core.Xact, error) { return dreg.getXact(uuid) } 141 142 func (r *registry) getXact(uuid string) (xctn core.Xact, err error) { 143 if !xact.IsValidUUID(uuid) { 144 err = fmt.Errorf("invalid UUID %q", uuid) 145 return 146 } 147 e := &r.entries 148 e.mtx.RLock() 149 outer: 150 for _, entries := range [][]Renewable{e.active, e.all} { // tradeoff: fewer active, higher priority 151 for _, entry := range entries { 152 x := entry.Get() 153 if x != nil && x.ID() == uuid { 154 xctn = x 155 break outer 156 } 157 } 158 } 159 e.mtx.RUnlock() 160 return 161 } 162 163 func GetAllRunning(inout *core.AllRunningInOut, periodic bool) { 164 dreg.entries.getAllRunning(inout, periodic) 165 } 166 167 func (e *entries) getAllRunning(inout *core.AllRunningInOut, periodic bool) { 168 var roActive []Renewable 169 if periodic { 170 roActive = e.roActive 171 roActive = roActive[:len(e.active)] 172 } else { 173 roActive = make([]Renewable, len(e.active)) 174 } 175 e.mtx.RLock() 176 copy(roActive, e.active) 177 e.mtx.RUnlock() 178 179 for _, entry := range roActive { 180 var ( 181 xctn = entry.Get() 182 k = xctn.Kind() 183 ) 184 if inout.Kind != "" && inout.Kind != k { 185 continue 186 } 187 if !xctn.Running() { 188 continue 189 } 190 var ( 191 xqn = k + xact.LeftID + xctn.ID() + xact.RightID // e.g. "make-n-copies[fGhuvvn7t]" 192 isIdle bool 193 ) 194 if inout.Idle != nil { 195 if _, ok := xctn.(xact.Demand); ok { 196 isIdle = xctn.Snap().IsIdle() 197 } 198 } 199 if isIdle { 200 inout.Idle = append(inout.Idle, xqn) 201 } else { 202 inout.Running = append(inout.Running, xqn) 203 } 204 } 205 206 sort.Strings(inout.Running) 207 sort.Strings(inout.Idle) 208 } 209 210 func GetRunning(flt Flt) Renewable { return dreg.getRunning(flt) } 211 212 func (r *registry) getRunning(flt Flt) (entry Renewable) { 213 e := &r.entries 214 e.mtx.RLock() 215 entry = e.findRunning(flt) 216 e.mtx.RUnlock() 217 return 218 } 219 220 // NOTE: relies on the find() to walk in the newer --> older order 221 func GetLatest(flt Flt) Renewable { 222 entry := dreg.entries.find(flt) 223 return entry 224 } 225 226 // AbortAllBuckets aborts all xactions that run with any of the provided bcks. 227 // It not only stops the "bucket xactions" but possibly "task xactions" which 228 // are running on given bucket. 229 230 func AbortAllBuckets(err error, bcks ...*meta.Bck) { 231 dreg.abort(&abortArgs{bcks: bcks, err: err}) 232 } 233 234 // AbortAll waits until abort of all xactions is finished 235 // Every abort is done asynchronously 236 func AbortAll(err error, scope ...int) { 237 dreg.abort(&abortArgs{scope: scope, err: err}) 238 } 239 240 func AbortKind(err error, kind string) { 241 dreg.abort(&abortArgs{kind: kind, err: err}) 242 } 243 244 func AbortByNewReb(err error) { dreg.abort(&abortArgs{err: err, newreb: true}) } 245 246 func DoAbort(flt Flt, err error) { 247 switch { 248 case flt.ID != "": 249 xctn, err := dreg.getXact(flt.ID) 250 if xctn == nil || err != nil { 251 return 252 } 253 debug.Assertf(flt.Kind == "" || xctn.Kind() == flt.Kind, "wrong xaction kind: %s vs %q", xctn.Cname(), flt.Kind) 254 xctn.Abort(err) 255 case flt.Kind != "" && flt.Bck != nil: 256 dreg.abort(&abortArgs{kind: flt.Kind, bcks: []*meta.Bck{flt.Bck}, err: err}) 257 case flt.Kind != "": 258 debug.Assert(xact.IsValidKind(flt.Kind), flt.Kind) 259 AbortKind(err, flt.Kind) 260 case flt.Bck != nil: 261 AbortAllBuckets(err, flt.Bck) 262 default: 263 AbortAll(err) 264 } 265 } 266 267 func GetSnap(flt Flt) ([]*core.Snap, error) { 268 var onlyRunning bool 269 if flt.OnlyRunning != nil { 270 onlyRunning = *flt.OnlyRunning 271 } 272 if flt.ID != "" { 273 xctn, err := dreg.getXact(flt.ID) 274 if err != nil { 275 return nil, err 276 } 277 if xctn != nil { 278 if onlyRunning && xctn.Finished() { 279 return nil, cmn.NewErrXactNotFoundError("[only-running vs " + xctn.String() + "]") 280 } 281 if flt.Kind != "" && xctn.Kind() != flt.Kind { 282 return nil, cmn.NewErrXactNotFoundError("[kind=" + flt.Kind + " vs " + xctn.String() + "]") 283 } 284 return []*core.Snap{xctn.Snap()}, nil 285 } 286 if onlyRunning || flt.Kind != apc.ActRebalance { 287 return nil, cmn.NewErrXactNotFoundError("ID=" + flt.ID) 288 } 289 // not running rebalance: include all finished (but not aborted) ones 290 // with ID at ot _after_ the specified 291 return dreg.matchingXactsStats(func(xctn core.Xact) bool { 292 cmp := xact.CompareRebIDs(xctn.ID(), flt.ID) 293 return cmp >= 0 && xctn.Finished() && !xctn.IsAborted() 294 }), nil 295 } 296 if flt.Bck != nil || flt.Kind != "" { 297 // Error checks 298 if flt.Kind != "" && !xact.IsValidKind(flt.Kind) { 299 return nil, cmn.NewErrXactNotFoundError(flt.Kind) 300 } 301 if flt.Bck != nil && !flt.Bck.HasProvider() { 302 return nil, fmt.Errorf("xaction %q: unknown provider for bucket %s", flt.Kind, flt.Bck.Name) 303 } 304 305 if onlyRunning { 306 matching := make([]*core.Snap, 0, 10) 307 if flt.Kind == "" { 308 dreg.entries.mtx.RLock() 309 for kind := range xact.Table { 310 entry := dreg.entries.findRunning(Flt{Kind: kind, Bck: flt.Bck}) 311 if entry != nil { 312 matching = append(matching, entry.Get().Snap()) 313 } 314 } 315 dreg.entries.mtx.RUnlock() 316 } else { 317 entry := dreg.getRunning(Flt{Kind: flt.Kind, Bck: flt.Bck}) 318 if entry != nil { 319 matching = append(matching, entry.Get().Snap()) 320 } 321 } 322 return matching, nil 323 } 324 return dreg.matchingXactsStats(flt.Matches), nil 325 } 326 return dreg.matchingXactsStats(flt.Matches), nil 327 } 328 329 func (r *registry) abort(args *abortArgs) { 330 r.entries.forEach(args.do) 331 } 332 333 func (args *abortArgs) do(entry Renewable) bool { 334 xctn := entry.Get() 335 if xctn.Finished() { 336 return true 337 } 338 339 var abort bool 340 switch { 341 case args.newreb: 342 debug.Assertf(args.scope == nil && args.kind == "", "scope %v, kind %q", args.scope, args.kind) 343 _, dtor, err := xact.GetDescriptor(xctn.Kind()) 344 debug.AssertNoErr(err) 345 if dtor.AbortRebRes { 346 abort = true 347 } 348 case len(args.bcks) > 0: 349 debug.Assertf(args.scope == nil, "scope %v", args.scope) 350 for _, bck := range args.bcks { 351 if xctn.Bck() != nil && bck.Equal(xctn.Bck(), true /*sameID*/, true /*same backend*/) { 352 abort = true 353 break 354 } 355 } 356 if abort && args.kind != "" { 357 abort = args.kind == xctn.Kind() 358 } 359 case args.kind != "": 360 debug.Assertf(args.scope == nil && len(args.bcks) == 0, "scope %v, bcks %v", args.scope, args.bcks) 361 abort = args.kind == xctn.Kind() 362 default: 363 abort = args.scope == nil || xact.IsSameScope(xctn.Kind(), args.scope...) 364 } 365 366 if abort { 367 xctn.Abort(args.err) 368 } 369 return true 370 } 371 372 func (r *registry) matchingXactsStats(match func(xctn core.Xact) bool) []*core.Snap { 373 matchingEntries := make([]Renewable, 0, 20) 374 r.entries.forEach(func(entry Renewable) bool { 375 if !match(entry.Get()) { 376 return true 377 } 378 matchingEntries = append(matchingEntries, entry) 379 return true 380 }) 381 // TODO: we cannot do this inside `forEach` because - nested locks 382 sts := make([]*core.Snap, 0, len(matchingEntries)) 383 for _, entry := range matchingEntries { 384 sts = append(sts, entry.Get().Snap()) 385 } 386 return sts 387 } 388 389 func (r *registry) incFinished() { r.finDelta.Inc() } 390 391 func (r *registry) hkPruneActive() time.Duration { 392 if r.finDelta.Swap(0) == 0 { 393 return hk.PruneActiveIval 394 } 395 e := &r.entries 396 e.mtx.Lock() 397 l := len(e.active) 398 for i := 0; i < l; i++ { 399 entry := e.active[i] 400 if !entry.Get().Finished() { 401 continue 402 } 403 copy(e.active[i:], e.active[i+1:]) 404 i-- 405 l-- 406 e.active = e.active[:l] 407 } 408 e.mtx.Unlock() 409 return hk.PruneActiveIval 410 } 411 412 func (r *registry) hkDelOld() time.Duration { 413 var ( 414 toRemove []string 415 numNonLso int 416 now = time.Now() 417 ) 418 419 r.entries.mtx.RLock() 420 l := len(r.entries.all) 421 422 // first, cleanup list-objects: walk older to newer while counting non-lso 423 for i := range l { 424 xctn := r.entries.all[i].Get() 425 if xctn.Kind() != apc.ActList { 426 numNonLso++ 427 continue 428 } 429 if xctn.Finished() { 430 if sinceFin := now.Sub(xctn.EndTime()); sinceFin >= hk.OldAgeLso { 431 toRemove = append(toRemove, xctn.ID()) 432 } 433 } 434 } 435 // all the rest: older to newer, while keeping at least `keepOldThreshold` 436 if numNonLso > keepOldThreshold { 437 var cnt int 438 for i := range l { 439 xctn := r.entries.all[i].Get() 440 if xctn.Kind() == apc.ActList { 441 continue 442 } 443 if xctn.Finished() { 444 if sinceFin := now.Sub(xctn.EndTime()); sinceFin >= hk.OldAgeX { 445 toRemove = append(toRemove, xctn.ID()) 446 cnt++ 447 if numNonLso-cnt <= keepOldThreshold { 448 break 449 } 450 } 451 } 452 } 453 } 454 r.entries.mtx.RUnlock() 455 456 if len(toRemove) == 0 { 457 return hk.DelOldIval 458 } 459 460 // cleanup 461 r.entries.mtx.Lock() 462 for _, id := range toRemove { 463 r.entries.del(id) 464 } 465 r.entries.mtx.Unlock() 466 return hk.DelOldIval 467 } 468 469 func (r *registry) renewByID(entry Renewable, bck *meta.Bck) (rns RenewRes) { 470 flt := Flt{ID: entry.UUID(), Kind: entry.Kind(), Bck: bck} 471 rns = r._renewFlt(entry, flt) 472 rns.beingRenewed() 473 return 474 } 475 476 func (r *registry) renew(entry Renewable, bck *meta.Bck, buckets ...*meta.Bck) (rns RenewRes) { 477 flt := Flt{Kind: entry.Kind(), Bck: bck, Buckets: buckets} 478 rns = r._renewFlt(entry, flt) 479 rns.beingRenewed() 480 return 481 } 482 483 func (r *registry) _renewFlt(entry Renewable, flt Flt) (rns RenewRes) { 484 // first, try to reuse under rlock 485 r.renewMtx.RLock() 486 if prevEntry := r.getRunning(flt); prevEntry != nil { 487 xprev := prevEntry.Get() 488 if usePrev(xprev, entry, flt) { 489 r.renewMtx.RUnlock() 490 return RenewRes{Entry: prevEntry, UUID: xprev.ID()} 491 } 492 if wpr, err := entry.WhenPrevIsRunning(prevEntry); wpr == WprUse || err != nil { 493 r.renewMtx.RUnlock() 494 if cmn.IsErrXactUsePrev(err) { 495 if wpr != WprUse { 496 nlog.Errorf("%v - not starting a new one of the same kind", err) 497 } 498 } 499 xctn := prevEntry.Get() 500 return RenewRes{Entry: prevEntry, Err: err, UUID: xctn.ID()} 501 } 502 } 503 r.renewMtx.RUnlock() 504 505 // second 506 r.renewMtx.Lock() 507 rns = r.renewLocked(entry, flt) 508 r.renewMtx.Unlock() 509 return 510 } 511 512 // reusing current (aka "previous") xaction: default policies 513 func usePrev(xprev core.Xact, nentry Renewable, flt Flt) bool { 514 pkind, nkind := xprev.Kind(), nentry.Kind() 515 debug.Assertf(pkind == nkind && pkind != "", "%s != %s", pkind, nkind) 516 pdtor, ndtor := xact.Table[pkind], xact.Table[nkind] 517 debug.Assert(pdtor.Scope == ndtor.Scope) 518 519 // same ID 520 if xprev.ID() != "" && xprev.ID() == nentry.UUID() { 521 return true // yes, use prev 522 } 523 if _, ok := xprev.(xact.Demand); !ok { 524 return false // upon return call xaction-specific WhenPrevIsRunning() 525 } 526 // 527 // on-demand 528 // 529 if pdtor.Scope != xact.ScopeB { 530 return true 531 } 532 bck := flt.Bck 533 debug.Assert(!bck.IsEmpty()) 534 if !bck.Equal(xprev.Bck(), true, true) { 535 return false 536 } 537 // on-demand (from-bucket, to-bucket) 538 from, to := xprev.FromTo() 539 if len(flt.Buckets) == 2 && from != nil && to != nil { 540 for _, bck := range flt.Buckets { 541 if !bck.Equal(from, true, true) && !bck.Equal(to, true, true) { 542 return false 543 } 544 } 545 } 546 return true 547 } 548 549 func (r *registry) renewLocked(entry Renewable, flt Flt) (rns RenewRes) { 550 var ( 551 xprev core.Xact 552 wpr WPR 553 err error 554 ) 555 if prevEntry := r.getRunning(flt); prevEntry != nil { 556 xprev = prevEntry.Get() 557 if usePrev(xprev, entry, flt) { 558 return RenewRes{Entry: prevEntry, UUID: xprev.ID()} 559 } 560 wpr, err = entry.WhenPrevIsRunning(prevEntry) 561 if wpr == WprUse || err != nil { 562 return RenewRes{Entry: prevEntry, Err: err, UUID: xprev.ID()} 563 } 564 debug.Assert(wpr == WprAbort || wpr == WprKeepAndStartNew) 565 if wpr == WprAbort { 566 xprev.Abort(cmn.ErrXactRenewAbort) 567 time.Sleep(waitPrevAborted) 568 } 569 } 570 if err = entry.Start(); err != nil { 571 return RenewRes{Err: err} 572 } 573 r.entries.add(entry) 574 return RenewRes{Entry: entry} 575 } 576 577 ////////////////////// 578 // registry entries // 579 ////////////////////// 580 581 // NOTE: the caller must take rlock 582 func (e *entries) findRunning(flt Flt) Renewable { 583 onl := true 584 flt.OnlyRunning = &onl 585 for _, entry := range e.active { 586 if flt.Matches(entry.Get()) { 587 return entry 588 } 589 } 590 return nil 591 } 592 593 // internal use, special case: Flt{Kind: kind}; NOTE: the caller must take rlock 594 func (e *entries) findRunningKind(kind string) Renewable { 595 for _, entry := range e.active { 596 if entry.Kind() != kind { 597 continue 598 } 599 xctn := entry.Get() 600 if xctn.Running() { 601 return entry 602 } 603 } 604 return nil 605 } 606 607 func (e *entries) find(flt Flt) (entry Renewable) { 608 e.mtx.RLock() 609 entry = e.findUnlocked(flt) 610 e.mtx.RUnlock() 611 return 612 } 613 614 func (e *entries) findUnlocked(flt Flt) Renewable { 615 if flt.OnlyRunning != nil && *flt.OnlyRunning { 616 return e.findRunning(flt) 617 } 618 // walk in reverse as there is a greater chance 619 // the one we are looking for is at the end 620 for idx := len(e.all) - 1; idx >= 0; idx-- { 621 entry := e.all[idx] 622 if flt.Matches(entry.Get()) { 623 return entry 624 } 625 } 626 return nil 627 } 628 629 func (e *entries) forEach(matcher func(entry Renewable) bool) { 630 e.mtx.RLock() 631 defer e.mtx.RUnlock() 632 for _, entry := range e.all { 633 if !matcher(entry) { 634 return 635 } 636 } 637 } 638 639 // NOTE: is called under lock 640 func (e *entries) del(id string) { 641 for idx, entry := range e.all { 642 xctn := entry.Get() 643 if xctn.ID() == id { 644 debug.Assert(xctn.Finished(), xctn.String()) 645 nlen := len(e.all) - 1 646 e.all[idx] = e.all[nlen] 647 e.all = e.all[:nlen] 648 break 649 } 650 } 651 for idx, entry := range e.active { 652 xctn := entry.Get() 653 if xctn.ID() == id { 654 if !xctn.Finished() { 655 nlog.Errorln("Warning: premature HK call to del-old", xctn.String()) 656 break 657 } 658 nlen := len(e.active) - 1 659 e.active[idx] = e.active[nlen] 660 e.active = e.active[:nlen] 661 break 662 } 663 } 664 } 665 666 func (e *entries) add(entry Renewable) { 667 e.mtx.Lock() 668 e.active = append(e.active, entry) 669 e.all = append(e.all, entry) 670 e.mtx.Unlock() 671 672 // grow 673 if cap(e.roActive) < len(e.active) { 674 e.roActive = make([]Renewable, 0, len(e.active)+len(e.active)>>1) 675 } 676 } 677 678 // LimitedCoexistence checks whether a given xaction that is about to start can, in fact, "coexist" 679 // with those that are currently running. It's a piece of logic designed to centralize all decision-making 680 // of that sort. Further comments below. 681 682 func LimitedCoexistence(tsi *meta.Snode, bck *meta.Bck, action string, otherBck ...*meta.Bck) (err error) { 683 if cmn.Rom.Features().IsSet(feat.IgnoreLimitedCoexistence) { 684 return 685 } 686 const sleep = time.Second 687 for i := time.Duration(0); i <= waitLimitedCoex; i += sleep { 688 if err = dreg.limco(tsi, bck, action, otherBck...); err == nil { 689 break 690 } 691 time.Sleep(sleep) 692 } 693 return 694 } 695 696 // - assorted admin-requested actions, in turn, trigger global rebalance 697 // e.g.: if copy-bucket or ETL is currently running we cannot start 698 // transitioning storage targets to maintenance 699 // - all supported xactions define "limited coexistence" via their respecive 700 // descriptors in xact.Table 701 func (r *registry) limco(tsi *meta.Snode, bck *meta.Bck, action string, otherBck ...*meta.Bck) error { 702 var ( 703 nd *xact.Descriptor // the one that wants to run 704 admin bool // admin-requested action that'd generate protential conflict 705 ) 706 switch action { 707 case apc.ActStartMaintenance, apc.ActStopMaintenance, apc.ActShutdownNode, apc.ActDecommissionNode: 708 nd = &xact.Descriptor{} 709 admin = true 710 default: 711 d, ok := xact.Table[action] 712 if !ok { 713 return nil 714 } 715 nd = &d 716 } 717 var locked bool 718 for kind, d := range xact.Table { 719 // rebalance-vs-rebalance and resilver-vs-resilver sort it out between themselves 720 // (by preempting) 721 conflict := (d.ConflictRebRes && admin) || 722 (d.Rebalance && nd.ConflictRebRes) || (d.Resilver && nd.ConflictRebRes) 723 if !conflict { 724 continue 725 } 726 727 // potential conflict becomes very real if the 'kind' is actually running 728 if !locked { 729 r.entries.mtx.RLock() 730 locked = true 731 defer r.entries.mtx.RUnlock() 732 } 733 entry := r.entries.findRunningKind(kind) 734 if entry == nil { 735 continue 736 } 737 738 // conflict confirmed 739 var b string 740 if bck != nil { 741 b = bck.String() 742 } 743 return cmn.NewErrLimitedCoexistence(tsi.String(), entry.Get().String(), action, b) 744 } 745 746 // finally, bucket rename (apc.ActMoveBck) is a special case - 747 // incompatible with any ConflictRebRes type operation _on the same_ bucket 748 if action != apc.ActMoveBck { 749 return nil 750 } 751 bck1, bck2 := bck, otherBck[0] 752 for _, entry := range r.entries.active { 753 xctn := entry.Get() 754 if !xctn.Running() { 755 continue 756 } 757 d, ok := xact.Table[xctn.Kind()] 758 debug.Assert(ok, xctn.Kind()) 759 if !d.ConflictRebRes { 760 continue 761 } 762 from, to := xctn.FromTo() 763 if _eqAny(bck1, bck2, from, to) { 764 detail := bck1.String() + " => " + bck2.String() 765 return cmn.NewErrLimitedCoexistence(tsi.String(), entry.Get().String(), action, detail) 766 } 767 } 768 return nil 769 } 770 771 func _eqAny(bck1, bck2, from, to *meta.Bck) (eq bool) { 772 if from != nil { 773 if bck1.Equal(from, false, true) || bck2.Equal(from, false, true) { 774 return true 775 } 776 } 777 if to != nil { 778 eq = bck1.Equal(to, false, true) || bck2.Equal(to, false, true) 779 } 780 return 781 } 782 783 /////////////// 784 // RenewBase // 785 /////////////// 786 787 func (r *RenewBase) Bucket() *meta.Bck { return r.Bck } 788 func (r *RenewBase) UUID() string { return r.Args.UUID } 789 790 func (r *RenewBase) Str(kind string) string { 791 prefix := kind 792 if r.Bck != nil { 793 prefix += "@" + r.Bck.String() 794 } 795 return fmt.Sprintf("%s, ID=%q", prefix, r.UUID()) 796 } 797 798 ////////////// 799 // RenewRes // 800 ////////////// 801 802 func (rns *RenewRes) IsRunning() bool { 803 if rns.UUID == "" { 804 return false 805 } 806 return rns.Entry.Get().Running() 807 } 808 809 // make sure existing on-demand is active to prevent it from (idle) expiration 810 // (see demand.go hkcb()) 811 func (rns *RenewRes) beingRenewed() { 812 if rns.Err != nil || !rns.IsRunning() { 813 return 814 } 815 xctn := rns.Entry.Get() 816 if xdmnd, ok := xctn.(xact.Demand); ok { 817 xdmnd.IncPending() 818 xdmnd.DecPending() 819 } 820 } 821 822 ///////// 823 // Flt // 824 ///////// 825 826 func (flt *Flt) String() string { 827 msg := xact.QueryMsg{OnlyRunning: flt.OnlyRunning, Bck: flt.Bck.Clone(), ID: flt.ID, Kind: flt.Kind} 828 return msg.String() 829 } 830 831 func (flt Flt) Matches(xctn core.Xact) (yes bool) { 832 debug.Assert(xact.IsValidKind(xctn.Kind()), xctn.String()) 833 // running? 834 if flt.OnlyRunning != nil { 835 if *flt.OnlyRunning != xctn.Running() { 836 return false 837 } 838 } 839 // same ID? 840 if flt.ID != "" { 841 debug.Assert(cos.IsValidUUID(flt.ID) || xact.IsValidRebID(flt.ID), flt.ID) 842 if yes = xctn.ID() == flt.ID; yes { 843 debug.Assert(xctn.Kind() == flt.Kind, xctn.String()+" vs same ID "+flt.String()) 844 } 845 return 846 } 847 // kind? 848 if flt.Kind != "" { 849 debug.Assert(xact.IsValidKind(flt.Kind), flt.Kind) 850 if xctn.Kind() != flt.Kind { 851 return false 852 } 853 } 854 // bucket? 855 if xact.Table[xctn.Kind()].Scope != xact.ScopeB { 856 return true // non single-bucket x 857 } 858 if flt.Bck == nil { 859 debug.Assert(len(flt.Buckets) == 0) 860 return true // the filter's not filtering out 861 } 862 if len(flt.Buckets) > 0 { 863 debug.Assert(len(flt.Buckets) == 2) 864 from, to := xctn.FromTo() 865 if from != nil { // XactArch special case 866 debug.Assert(to != nil) 867 return from.Equal(flt.Buckets[0], false, false) && to.Equal(flt.Buckets[1], false, false) 868 } 869 } 870 871 return xctn.Bck().Equal(flt.Bck, true, true) 872 }