github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/gopkg.in/mgo.v2/txn/flusher.go (about) 1 package txn 2 3 import ( 4 "fmt" 5 6 "gopkg.in/mgo.v2" 7 "gopkg.in/mgo.v2/bson" 8 ) 9 10 func flush(r *Runner, t *transaction) error { 11 f := &flusher{ 12 Runner: r, 13 goal: t, 14 goalKeys: make(map[docKey]bool), 15 queue: make(map[docKey][]token), 16 debugId: debugPrefix(), 17 } 18 for _, dkey := range f.goal.docKeys() { 19 f.goalKeys[dkey] = true 20 } 21 return f.run() 22 } 23 24 type flusher struct { 25 *Runner 26 goal *transaction 27 goalKeys map[docKey]bool 28 queue map[docKey][]token 29 debugId string 30 } 31 32 func (f *flusher) run() (err error) { 33 if chaosEnabled { 34 defer f.handleChaos(&err) 35 } 36 37 f.debugf("Processing %s", f.goal) 38 seen := make(map[bson.ObjectId]*transaction) 39 if err := f.recurse(f.goal, seen); err != nil { 40 return err 41 } 42 if f.goal.done() { 43 return nil 44 } 45 46 // Sparse workloads will generally be managed entirely by recurse. 47 // Getting here means one or more transactions have dependencies 48 // and perhaps cycles. 49 50 // Build successors data for Tarjan's sort. Must consider 51 // that entries in txn-queue are not necessarily valid. 52 successors := make(map[bson.ObjectId][]bson.ObjectId) 53 ready := true 54 for _, dqueue := range f.queue { 55 NextPair: 56 for i := 0; i < len(dqueue); i++ { 57 pred := dqueue[i] 58 predid := pred.id() 59 predt := seen[predid] 60 if predt == nil || predt.Nonce != pred.nonce() { 61 continue 62 } 63 predsuccids, ok := successors[predid] 64 if !ok { 65 successors[predid] = nil 66 } 67 68 for j := i + 1; j < len(dqueue); j++ { 69 succ := dqueue[j] 70 succid := succ.id() 71 succt := seen[succid] 72 if succt == nil || succt.Nonce != succ.nonce() { 73 continue 74 } 75 if _, ok := successors[succid]; !ok { 76 successors[succid] = nil 77 } 78 79 // Found a valid pred/succ pair. 80 i = j - 1 81 for _, predsuccid := range predsuccids { 82 if predsuccid == succid { 83 continue NextPair 84 } 85 } 86 successors[predid] = append(predsuccids, succid) 87 if succid == f.goal.Id { 88 // There are still pre-requisites to handle. 89 ready = false 90 } 91 continue NextPair 92 } 93 } 94 } 95 f.debugf("Queues: %v", f.queue) 96 f.debugf("Successors: %v", successors) 97 if ready { 98 f.debugf("Goal %s has no real pre-requisites", f.goal) 99 return f.advance(f.goal, nil, true) 100 } 101 102 // Robert Tarjan's algorithm for detecting strongly-connected 103 // components is used for topological sorting and detecting 104 // cycles at once. The order in which transactions are applied 105 // in commonly affected documents must be a global agreement. 106 sorted := tarjanSort(successors) 107 if debugEnabled { 108 f.debugf("Tarjan output: %v", sorted) 109 } 110 pull := make(map[bson.ObjectId]*transaction) 111 for i := len(sorted) - 1; i >= 0; i-- { 112 scc := sorted[i] 113 f.debugf("Flushing %v", scc) 114 if len(scc) == 1 { 115 pull[scc[0]] = seen[scc[0]] 116 } 117 for _, id := range scc { 118 if err := f.advance(seen[id], pull, true); err != nil { 119 return err 120 } 121 } 122 if len(scc) > 1 { 123 for _, id := range scc { 124 pull[id] = seen[id] 125 } 126 } 127 } 128 return nil 129 } 130 131 func (f *flusher) recurse(t *transaction, seen map[bson.ObjectId]*transaction) error { 132 seen[t.Id] = t 133 err := f.advance(t, nil, false) 134 if err != errPreReqs { 135 return err 136 } 137 for _, dkey := range t.docKeys() { 138 for _, dtt := range f.queue[dkey] { 139 id := dtt.id() 140 if seen[id] != nil { 141 continue 142 } 143 qt, err := f.load(id) 144 if err != nil { 145 return err 146 } 147 err = f.recurse(qt, seen) 148 if err != nil { 149 return err 150 } 151 } 152 } 153 return nil 154 } 155 156 func (f *flusher) advance(t *transaction, pull map[bson.ObjectId]*transaction, force bool) error { 157 for { 158 switch t.State { 159 case tpreparing, tprepared: 160 revnos, err := f.prepare(t, force) 161 if err != nil { 162 return err 163 } 164 if t.State != tprepared { 165 continue 166 } 167 if err = f.assert(t, revnos, pull); err != nil { 168 return err 169 } 170 if t.State != tprepared { 171 continue 172 } 173 if err = f.checkpoint(t, revnos); err != nil { 174 return err 175 } 176 case tapplying: 177 return f.apply(t, pull) 178 case taborting: 179 return f.abortOrReload(t, nil, pull) 180 case tapplied, taborted: 181 return nil 182 default: 183 panic(fmt.Errorf("transaction in unknown state: %q", t.State)) 184 } 185 } 186 panic("unreachable") 187 } 188 189 type stash string 190 191 const ( 192 stashStable stash = "" 193 stashInsert stash = "insert" 194 stashRemove stash = "remove" 195 ) 196 197 type txnInfo struct { 198 Queue []token `bson:"txn-queue"` 199 Revno int64 `bson:"txn-revno,omitempty"` 200 Insert bson.ObjectId `bson:"txn-insert,omitempty"` 201 Remove bson.ObjectId `bson:"txn-remove,omitempty"` 202 } 203 204 type stashState string 205 206 const ( 207 stashNew stashState = "" 208 stashInserting stashState = "inserting" 209 ) 210 211 var txnFields = bson.D{{"txn-queue", 1}, {"txn-revno", 1}, {"txn-remove", 1}, {"txn-insert", 1}} 212 213 var errPreReqs = fmt.Errorf("transaction has pre-requisites and force is false") 214 215 // prepare injects t's id onto txn-queue for all affected documents 216 // and collects the current txn-queue and txn-revno values during 217 // the process. If the prepared txn-queue indicates that there are 218 // pre-requisite transactions to be applied and the force parameter 219 // is false, errPreReqs will be returned. Otherwise, the current 220 // tip revision numbers for all the documents are returned. 221 func (f *flusher) prepare(t *transaction, force bool) (revnos []int64, err error) { 222 if t.State != tpreparing { 223 return f.rescan(t, force) 224 } 225 f.debugf("Preparing %s", t) 226 227 // dkeys being sorted means stable iteration across all runners. This 228 // isn't strictly required, but reduces the chances of cycles. 229 dkeys := t.docKeys() 230 231 revno := make(map[docKey]int64) 232 info := txnInfo{} 233 tt := tokenFor(t) 234 NextDoc: 235 for _, dkey := range dkeys { 236 change := mgo.Change{ 237 Update: bson.D{{"$addToSet", bson.D{{"txn-queue", tt}}}}, 238 ReturnNew: true, 239 } 240 c := f.tc.Database.C(dkey.C) 241 cquery := c.FindId(dkey.Id).Select(txnFields) 242 243 RetryDoc: 244 change.Upsert = false 245 chaos("") 246 if _, err := cquery.Apply(change, &info); err == nil { 247 if info.Remove == "" { 248 // Fast path, unless workload is insert/remove heavy. 249 revno[dkey] = info.Revno 250 f.queue[dkey] = info.Queue 251 f.debugf("[A] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue) 252 continue NextDoc 253 } else { 254 // Handle remove in progress before preparing it. 255 if err := f.loadAndApply(info.Remove); err != nil { 256 return nil, err 257 } 258 goto RetryDoc 259 } 260 } else if err != mgo.ErrNotFound { 261 return nil, err 262 } 263 264 // Document missing. Use stash collection. 265 change.Upsert = true 266 chaos("") 267 _, err := f.sc.FindId(dkey).Apply(change, &info) 268 if err != nil { 269 return nil, err 270 } 271 if info.Insert != "" { 272 // Handle insert in progress before preparing it. 273 if err := f.loadAndApply(info.Insert); err != nil { 274 return nil, err 275 } 276 goto RetryDoc 277 } 278 279 // Must confirm stash is still in use and is the same one 280 // prepared, since applying a remove overwrites the stash. 281 docFound := false 282 stashFound := false 283 if err = c.FindId(dkey.Id).Select(txnFields).One(&info); err == nil { 284 docFound = true 285 } else if err != mgo.ErrNotFound { 286 return nil, err 287 } else if err = f.sc.FindId(dkey).One(&info); err == nil { 288 stashFound = true 289 if info.Revno == 0 { 290 // Missing revno in the stash only happens when it 291 // has been upserted, in which case it defaults to -1. 292 // Txn-inserted documents get revno -1 while in the stash 293 // for the first time, and -revno-1 == 2 when they go live. 294 info.Revno = -1 295 } 296 } else if err != mgo.ErrNotFound { 297 return nil, err 298 } 299 300 if docFound && info.Remove == "" || stashFound && info.Insert == "" { 301 for _, dtt := range info.Queue { 302 if dtt != tt { 303 continue 304 } 305 // Found tt properly prepared. 306 if stashFound { 307 f.debugf("[B] Prepared document %v on stash with revno %d and queue: %v", dkey, info.Revno, info.Queue) 308 } else { 309 f.debugf("[B] Prepared document %v with revno %d and queue: %v", dkey, info.Revno, info.Queue) 310 } 311 revno[dkey] = info.Revno 312 f.queue[dkey] = info.Queue 313 continue NextDoc 314 } 315 } 316 317 // The stash wasn't valid and tt got overwritten. Try again. 318 f.unstashToken(tt, dkey) 319 goto RetryDoc 320 } 321 322 // Save the prepared nonce onto t. 323 nonce := tt.nonce() 324 qdoc := bson.D{{"_id", t.Id}, {"s", tpreparing}} 325 udoc := bson.D{{"$set", bson.D{{"s", tprepared}, {"n", nonce}}}} 326 chaos("set-prepared") 327 err = f.tc.Update(qdoc, udoc) 328 if err == nil { 329 t.State = tprepared 330 t.Nonce = nonce 331 } else if err == mgo.ErrNotFound { 332 f.debugf("Can't save nonce of %s: LOST RACE", tt) 333 if err := f.reload(t); err != nil { 334 return nil, err 335 } else if t.State == tpreparing { 336 panic("can't save nonce yet transaction is still preparing") 337 } else if t.State != tprepared { 338 return t.Revnos, nil 339 } 340 tt = t.token() 341 } else if err != nil { 342 return nil, err 343 } 344 345 prereqs, found := f.hasPreReqs(tt, dkeys) 346 if !found { 347 // Must only happen when reloading above. 348 return f.rescan(t, force) 349 } else if prereqs && !force { 350 f.debugf("Prepared queue with %s [has prereqs & not forced].", tt) 351 return nil, errPreReqs 352 } 353 revnos = assembledRevnos(t.Ops, revno) 354 if !prereqs { 355 f.debugf("Prepared queue with %s [no prereqs]. Revnos: %v", tt, revnos) 356 } else { 357 f.debugf("Prepared queue with %s [forced] Revnos: %v", tt, revnos) 358 } 359 return revnos, nil 360 } 361 362 func (f *flusher) unstashToken(tt token, dkey docKey) error { 363 qdoc := bson.D{{"_id", dkey}, {"txn-queue", tt}} 364 udoc := bson.D{{"$pull", bson.D{{"txn-queue", tt}}}} 365 chaos("") 366 if err := f.sc.Update(qdoc, udoc); err == nil { 367 chaos("") 368 err = f.sc.Remove(bson.D{{"_id", dkey}, {"txn-queue", bson.D{}}}) 369 } else if err != mgo.ErrNotFound { 370 return err 371 } 372 return nil 373 } 374 375 func (f *flusher) rescan(t *transaction, force bool) (revnos []int64, err error) { 376 f.debugf("Rescanning %s", t) 377 if t.State != tprepared { 378 panic(fmt.Errorf("rescanning transaction in invalid state: %q", t.State)) 379 } 380 381 // dkeys being sorted means stable iteration across all 382 // runners. This isn't strictly required, but reduces the chances 383 // of cycles. 384 dkeys := t.docKeys() 385 386 tt := t.token() 387 if !force { 388 prereqs, found := f.hasPreReqs(tt, dkeys) 389 if found && prereqs { 390 // Its state is already known. 391 return nil, errPreReqs 392 } 393 } 394 395 revno := make(map[docKey]int64) 396 info := txnInfo{} 397 for _, dkey := range dkeys { 398 const retries = 3 399 retry := -1 400 401 RetryDoc: 402 retry++ 403 c := f.tc.Database.C(dkey.C) 404 if err := c.FindId(dkey.Id).Select(txnFields).One(&info); err == mgo.ErrNotFound { 405 // Document is missing. Look in stash. 406 chaos("") 407 if err := f.sc.FindId(dkey).One(&info); err == mgo.ErrNotFound { 408 // Stash also doesn't exist. Maybe someone applied it. 409 if err := f.reload(t); err != nil { 410 return nil, err 411 } else if t.State != tprepared { 412 return t.Revnos, err 413 } 414 // Not applying either. 415 if retry < retries { 416 // Retry since there might be an insert/remove race. 417 goto RetryDoc 418 } 419 // Neither the doc nor the stash seem to exist. 420 return nil, fmt.Errorf("cannot find document %v for applying transaction %s", dkey, t) 421 } else if err != nil { 422 return nil, err 423 } 424 // Stash found. 425 if info.Insert != "" { 426 // Handle insert in progress before assuming ordering is good. 427 if err := f.loadAndApply(info.Insert); err != nil { 428 return nil, err 429 } 430 goto RetryDoc 431 } 432 if info.Revno == 0 { 433 // Missing revno in the stash means -1. 434 info.Revno = -1 435 } 436 } else if err != nil { 437 return nil, err 438 } else if info.Remove != "" { 439 // Handle remove in progress before assuming ordering is good. 440 if err := f.loadAndApply(info.Remove); err != nil { 441 return nil, err 442 } 443 goto RetryDoc 444 } 445 revno[dkey] = info.Revno 446 447 found := false 448 for _, id := range info.Queue { 449 if id == tt { 450 found = true 451 break 452 } 453 } 454 f.queue[dkey] = info.Queue 455 if !found { 456 // Rescanned transaction id was not in the queue. This could mean one 457 // of three things: 458 // 1) The transaction was applied and popped by someone else. This is 459 // the common case. 460 // 2) We've read an out-of-date queue from the stash. This can happen 461 // when someone else was paused for a long while preparing another 462 // transaction for this document, and improperly upserted to the 463 // stash when unpaused (after someone else inserted the document). 464 // This is rare but possible. 465 // 3) There's an actual bug somewhere, or outside interference. Worst 466 // possible case. 467 f.debugf("Rescanned document %v misses %s in queue: %v", dkey, tt, info.Queue) 468 err := f.reload(t) 469 if t.State == tpreparing || t.State == tprepared { 470 if retry < retries { 471 // Case 2. 472 goto RetryDoc 473 } 474 // Case 3. 475 return nil, fmt.Errorf("cannot find transaction %s in queue for document %v", t, dkey) 476 } 477 // Case 1. 478 return t.Revnos, err 479 } 480 } 481 482 prereqs, found := f.hasPreReqs(tt, dkeys) 483 if !found { 484 panic("rescanning loop guarantees that this can't happen") 485 } else if prereqs && !force { 486 f.debugf("Rescanned queue with %s: has prereqs, not forced", tt) 487 return nil, errPreReqs 488 } 489 revnos = assembledRevnos(t.Ops, revno) 490 if !prereqs { 491 f.debugf("Rescanned queue with %s: no prereqs, revnos: %v", tt, revnos) 492 } else { 493 f.debugf("Rescanned queue with %s: has prereqs, forced, revnos: %v", tt, revnos) 494 } 495 return revnos, nil 496 } 497 498 func assembledRevnos(ops []Op, revno map[docKey]int64) []int64 { 499 revnos := make([]int64, len(ops)) 500 for i, op := range ops { 501 dkey := op.docKey() 502 revnos[i] = revno[dkey] 503 drevno := revno[dkey] 504 switch { 505 case op.Insert != nil && drevno < 0: 506 revno[dkey] = -drevno + 1 507 case op.Update != nil && drevno >= 0: 508 revno[dkey] = drevno + 1 509 case op.Remove && drevno >= 0: 510 revno[dkey] = -drevno - 1 511 } 512 } 513 return revnos 514 } 515 516 func (f *flusher) hasPreReqs(tt token, dkeys docKeys) (prereqs, found bool) { 517 found = true 518 NextDoc: 519 for _, dkey := range dkeys { 520 for _, dtt := range f.queue[dkey] { 521 if dtt == tt { 522 continue NextDoc 523 } else if dtt.id() != tt.id() { 524 prereqs = true 525 } 526 } 527 found = false 528 } 529 return 530 } 531 532 func (f *flusher) reload(t *transaction) error { 533 var newt transaction 534 query := f.tc.FindId(t.Id) 535 query.Select(bson.D{{"s", 1}, {"n", 1}, {"r", 1}}) 536 if err := query.One(&newt); err != nil { 537 return fmt.Errorf("failed to reload transaction: %v", err) 538 } 539 t.State = newt.State 540 t.Nonce = newt.Nonce 541 t.Revnos = newt.Revnos 542 f.debugf("Reloaded %s: %q", t, t.State) 543 return nil 544 } 545 546 func (f *flusher) loadAndApply(id bson.ObjectId) error { 547 t, err := f.load(id) 548 if err != nil { 549 return err 550 } 551 return f.advance(t, nil, true) 552 } 553 554 // assert verifies that all assertions in t match the content that t 555 // will be applied upon. If an assertion fails, the transaction state 556 // is changed to aborted. 557 func (f *flusher) assert(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) error { 558 f.debugf("Asserting %s with revnos %v", t, revnos) 559 if t.State != tprepared { 560 panic(fmt.Errorf("asserting transaction in invalid state: %q", t.State)) 561 } 562 qdoc := make(bson.D, 3) 563 revno := make(map[docKey]int64) 564 for i, op := range t.Ops { 565 dkey := op.docKey() 566 if _, ok := revno[dkey]; !ok { 567 revno[dkey] = revnos[i] 568 } 569 if op.Assert == nil { 570 continue 571 } 572 if op.Assert == DocMissing { 573 if revnos[i] >= 0 { 574 return f.abortOrReload(t, revnos, pull) 575 } 576 continue 577 } 578 if op.Insert != nil { 579 return fmt.Errorf("Insert can only Assert txn.DocMissing", op.Assert) 580 } 581 // if revnos[i] < 0 { abort }? 582 583 qdoc = append(qdoc[:0], bson.DocElem{"_id", op.Id}) 584 if op.Assert != DocMissing { 585 var revnoq interface{} 586 if n := revno[dkey]; n == 0 { 587 revnoq = bson.D{{"$exists", false}} 588 } else { 589 revnoq = n 590 } 591 // XXX Add tt to the query here, once we're sure it's all working. 592 // Not having it increases the chances of breaking on bad logic. 593 qdoc = append(qdoc, bson.DocElem{"txn-revno", revnoq}) 594 if op.Assert != DocExists { 595 qdoc = append(qdoc, bson.DocElem{"$or", []interface{}{op.Assert}}) 596 } 597 } 598 599 c := f.tc.Database.C(op.C) 600 if err := c.Find(qdoc).Select(bson.D{{"_id", 1}}).One(nil); err == mgo.ErrNotFound { 601 // Assertion failed or someone else started applying. 602 return f.abortOrReload(t, revnos, pull) 603 } else if err != nil { 604 return err 605 } 606 } 607 f.debugf("Asserting %s succeeded", t) 608 return nil 609 } 610 611 func (f *flusher) abortOrReload(t *transaction, revnos []int64, pull map[bson.ObjectId]*transaction) (err error) { 612 f.debugf("Aborting or reloading %s (was %q)", t, t.State) 613 if t.State == tprepared { 614 qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}} 615 udoc := bson.D{{"$set", bson.D{{"s", taborting}}}} 616 chaos("set-aborting") 617 if err = f.tc.Update(qdoc, udoc); err == nil { 618 t.State = taborting 619 } else if err == mgo.ErrNotFound { 620 if err = f.reload(t); err != nil || t.State != taborting { 621 f.debugf("Won't abort %s. Reloaded state: %q", t, t.State) 622 return err 623 } 624 } else { 625 return err 626 } 627 } else if t.State != taborting { 628 panic(fmt.Errorf("aborting transaction in invalid state: %q", t.State)) 629 } 630 631 if len(revnos) > 0 { 632 if pull == nil { 633 pull = map[bson.ObjectId]*transaction{t.Id: t} 634 } 635 seen := make(map[docKey]bool) 636 for i, op := range t.Ops { 637 dkey := op.docKey() 638 if seen[op.docKey()] { 639 continue 640 } 641 seen[dkey] = true 642 643 pullAll := tokensToPull(f.queue[dkey], pull, "") 644 if len(pullAll) == 0 { 645 continue 646 } 647 udoc := bson.D{{"$pullAll", bson.D{{"txn-queue", pullAll}}}} 648 chaos("") 649 if revnos[i] < 0 { 650 err = f.sc.UpdateId(dkey, udoc) 651 } else { 652 c := f.tc.Database.C(dkey.C) 653 err = c.UpdateId(dkey.Id, udoc) 654 } 655 if err != nil && err != mgo.ErrNotFound { 656 return err 657 } 658 } 659 } 660 udoc := bson.D{{"$set", bson.D{{"s", taborted}}}} 661 chaos("set-aborted") 662 if err := f.tc.UpdateId(t.Id, udoc); err != nil && err != mgo.ErrNotFound { 663 return err 664 } 665 t.State = taborted 666 f.debugf("Aborted %s", t) 667 return nil 668 } 669 670 func (f *flusher) checkpoint(t *transaction, revnos []int64) error { 671 var debugRevnos map[docKey][]int64 672 if debugEnabled { 673 debugRevnos = make(map[docKey][]int64) 674 for i, op := range t.Ops { 675 dkey := op.docKey() 676 debugRevnos[dkey] = append(debugRevnos[dkey], revnos[i]) 677 } 678 f.debugf("Ready to apply %s. Saving revnos %v", t, debugRevnos) 679 } 680 681 // Save in t the txn-revno values the transaction must run on. 682 qdoc := bson.D{{"_id", t.Id}, {"s", tprepared}} 683 udoc := bson.D{{"$set", bson.D{{"s", tapplying}, {"r", revnos}}}} 684 chaos("set-applying") 685 err := f.tc.Update(qdoc, udoc) 686 if err == nil { 687 t.State = tapplying 688 t.Revnos = revnos 689 f.debugf("Ready to apply %s. Saving revnos %v: DONE", t, debugRevnos) 690 } else if err == mgo.ErrNotFound { 691 f.debugf("Ready to apply %s. Saving revnos %v: LOST RACE", t, debugRevnos) 692 return f.reload(t) 693 } 694 return nil 695 } 696 697 func (f *flusher) apply(t *transaction, pull map[bson.ObjectId]*transaction) error { 698 f.debugf("Applying transaction %s", t) 699 if t.State != tapplying { 700 panic(fmt.Errorf("applying transaction in invalid state: %q", t.State)) 701 } 702 if pull == nil { 703 pull = map[bson.ObjectId]*transaction{t.Id: t} 704 } 705 706 logRevnos := append([]int64(nil), t.Revnos...) 707 logDoc := bson.D{{"_id", t.Id}} 708 709 tt := tokenFor(t) 710 for i := range t.Ops { 711 op := &t.Ops[i] 712 dkey := op.docKey() 713 dqueue := f.queue[dkey] 714 revno := t.Revnos[i] 715 716 var opName string 717 if debugEnabled { 718 opName = op.name() 719 f.debugf("Applying %s op %d (%s) on %v with txn-revno %d", t, i, opName, dkey, revno) 720 } 721 722 c := f.tc.Database.C(op.C) 723 724 qdoc := bson.D{{"_id", dkey.Id}, {"txn-revno", revno}, {"txn-queue", tt}} 725 if op.Insert != nil { 726 qdoc[0].Value = dkey 727 if revno == -1 { 728 qdoc[1].Value = bson.D{{"$exists", false}} 729 } 730 } else if revno == 0 { 731 // There's no document with revno 0. The only way to see it is 732 // when an existent document participates in a transaction the 733 // first time. Txn-inserted documents get revno -1 while in the 734 // stash for the first time, and -revno-1 == 2 when they go live. 735 qdoc[1].Value = bson.D{{"$exists", false}} 736 } 737 738 pullAll := tokensToPull(dqueue, pull, tt) 739 740 var d bson.D 741 var outcome string 742 var err error 743 switch { 744 case op.Update != nil: 745 if revno < 0 { 746 err = mgo.ErrNotFound 747 f.debugf("Won't try to apply update op; negative revision means the document is missing or stashed") 748 } else { 749 newRevno := revno + 1 750 logRevnos[i] = newRevno 751 if d, err = objToDoc(op.Update); err != nil { 752 return err 753 } 754 if d, err = addToDoc(d, "$pullAll", bson.D{{"txn-queue", pullAll}}); err != nil { 755 return err 756 } 757 if d, err = addToDoc(d, "$set", bson.D{{"txn-revno", newRevno}}); err != nil { 758 return err 759 } 760 chaos("") 761 err = c.Update(qdoc, d) 762 } 763 case op.Remove: 764 if revno < 0 { 765 err = mgo.ErrNotFound 766 } else { 767 newRevno := -revno - 1 768 logRevnos[i] = newRevno 769 nonce := newNonce() 770 stash := txnInfo{} 771 change := mgo.Change{ 772 Update: bson.D{{"$push", bson.D{{"n", nonce}}}}, 773 Upsert: true, 774 ReturnNew: true, 775 } 776 if _, err = f.sc.FindId(dkey).Apply(change, &stash); err != nil { 777 return err 778 } 779 change = mgo.Change{ 780 Update: bson.D{{"$set", bson.D{{"txn-remove", t.Id}}}}, 781 ReturnNew: true, 782 } 783 var info txnInfo 784 if _, err = c.Find(qdoc).Apply(change, &info); err == nil { 785 // The document still exists so the stash previously 786 // observed was either out of date or necessarily 787 // contained the token being applied. 788 f.debugf("Marked document %v to be removed on revno %d with queue: %v", dkey, info.Revno, info.Queue) 789 updated := false 790 if !hasToken(stash.Queue, tt) { 791 var set, unset bson.D 792 if revno == 0 { 793 // Missing revno in stash means -1. 794 set = bson.D{{"txn-queue", info.Queue}} 795 unset = bson.D{{"n", 1}, {"txn-revno", 1}} 796 } else { 797 set = bson.D{{"txn-queue", info.Queue}, {"txn-revno", newRevno}} 798 unset = bson.D{{"n", 1}} 799 } 800 qdoc := bson.D{{"_id", dkey}, {"n", nonce}} 801 udoc := bson.D{{"$set", set}, {"$unset", unset}} 802 if err = f.sc.Update(qdoc, udoc); err == nil { 803 updated = true 804 } else if err != mgo.ErrNotFound { 805 return err 806 } 807 } 808 if updated { 809 f.debugf("Updated stash for document %v with revno %d and queue: %v", dkey, newRevno, info.Queue) 810 } else { 811 f.debugf("Stash for document %v was up-to-date", dkey) 812 } 813 err = c.Remove(qdoc) 814 } 815 } 816 case op.Insert != nil: 817 if revno >= 0 { 818 err = mgo.ErrNotFound 819 } else { 820 newRevno := -revno + 1 821 logRevnos[i] = newRevno 822 if d, err = objToDoc(op.Insert); err != nil { 823 return err 824 } 825 change := mgo.Change{ 826 Update: bson.D{{"$set", bson.D{{"txn-insert", t.Id}}}}, 827 ReturnNew: true, 828 } 829 chaos("") 830 var info txnInfo 831 if _, err = f.sc.Find(qdoc).Apply(change, &info); err == nil { 832 f.debugf("Stash for document %v has revno %d and queue: %v", dkey, info.Revno, info.Queue) 833 d = setInDoc(d, bson.D{{"_id", op.Id}, {"txn-revno", newRevno}, {"txn-queue", info.Queue}}) 834 // Unlikely yet unfortunate race in here if this gets seriously 835 // delayed. If someone inserts+removes meanwhile, this will 836 // reinsert, and there's no way to avoid that while keeping the 837 // collection clean or compromising sharding. applyOps can solve 838 // the former, but it can't shard (SERVER-1439). 839 chaos("insert") 840 err = c.Insert(d) 841 if err == nil || mgo.IsDup(err) { 842 if err == nil { 843 f.debugf("New document %v inserted with revno %d and queue: %v", dkey, info.Revno, info.Queue) 844 } else { 845 f.debugf("Document %v already existed", dkey) 846 } 847 chaos("") 848 if err = f.sc.Remove(qdoc); err == nil { 849 f.debugf("Stash for document %v removed", dkey) 850 } 851 } 852 } 853 } 854 case op.Assert != nil: 855 // Pure assertion. No changes to apply. 856 } 857 if err == nil { 858 outcome = "DONE" 859 } else if err == mgo.ErrNotFound || mgo.IsDup(err) { 860 outcome = "MISS" 861 err = nil 862 } else { 863 outcome = err.Error() 864 } 865 if debugEnabled { 866 f.debugf("Applying %s op %d (%s) on %v with txn-revno %d: %s", t, i, opName, dkey, revno, outcome) 867 } 868 if err != nil { 869 return err 870 } 871 872 if f.lc != nil && op.isChange() { 873 // Add change to the log document. 874 var dr bson.D 875 for li := range logDoc { 876 elem := &logDoc[li] 877 if elem.Name == op.C { 878 dr = elem.Value.(bson.D) 879 break 880 } 881 } 882 if dr == nil { 883 logDoc = append(logDoc, bson.DocElem{op.C, bson.D{{"d", []interface{}{}}, {"r", []int64{}}}}) 884 dr = logDoc[len(logDoc)-1].Value.(bson.D) 885 } 886 dr[0].Value = append(dr[0].Value.([]interface{}), op.Id) 887 dr[1].Value = append(dr[1].Value.([]int64), logRevnos[i]) 888 } 889 } 890 t.State = tapplied 891 892 if f.lc != nil { 893 // Insert log document into the changelog collection. 894 f.debugf("Inserting %s into change log", t) 895 err := f.lc.Insert(logDoc) 896 if err != nil && !mgo.IsDup(err) { 897 return err 898 } 899 } 900 901 // It's been applied, so errors are ignored here. It's fine for someone 902 // else to win the race and mark it as applied, and it's also fine for 903 // it to remain pending until a later point when someone will perceive 904 // it has been applied and mark it at such. 905 f.debugf("Marking %s as applied", t) 906 chaos("set-applied") 907 f.tc.Update(bson.D{{"_id", t.Id}, {"s", tapplying}}, bson.D{{"$set", bson.D{{"s", tapplied}}}}) 908 return nil 909 } 910 911 func tokensToPull(dqueue []token, pull map[bson.ObjectId]*transaction, dontPull token) []token { 912 var result []token 913 for j := len(dqueue) - 1; j >= 0; j-- { 914 dtt := dqueue[j] 915 if dtt == dontPull { 916 continue 917 } 918 if _, ok := pull[dtt.id()]; ok { 919 // It was handled before and this is a leftover invalid 920 // nonce in the queue. Cherry-pick it out. 921 result = append(result, dtt) 922 } 923 } 924 return result 925 } 926 927 func objToDoc(obj interface{}) (d bson.D, err error) { 928 data, err := bson.Marshal(obj) 929 if err != nil { 930 return nil, err 931 } 932 err = bson.Unmarshal(data, &d) 933 if err != nil { 934 return nil, err 935 } 936 return d, err 937 } 938 939 func addToDoc(doc bson.D, key string, add bson.D) (bson.D, error) { 940 for i := range doc { 941 elem := &doc[i] 942 if elem.Name != key { 943 continue 944 } 945 if old, ok := elem.Value.(bson.D); ok { 946 elem.Value = append(old, add...) 947 return doc, nil 948 } else { 949 return nil, fmt.Errorf("invalid %q value in change document: %#v", key, elem.Value) 950 } 951 } 952 return append(doc, bson.DocElem{key, add}), nil 953 } 954 955 func setInDoc(doc bson.D, set bson.D) bson.D { 956 dlen := len(doc) 957 NextS: 958 for s := range set { 959 sname := set[s].Name 960 for d := 0; d < dlen; d++ { 961 if doc[d].Name == sname { 962 doc[d].Value = set[s].Value 963 continue NextS 964 } 965 } 966 doc = append(doc, set[s]) 967 } 968 return doc 969 } 970 971 func hasToken(tokens []token, tt token) bool { 972 for _, ttt := range tokens { 973 if ttt == tt { 974 return true 975 } 976 } 977 return false 978 } 979 980 func (f *flusher) debugf(format string, args ...interface{}) { 981 if !debugEnabled { 982 return 983 } 984 debugf(f.debugId+format, args...) 985 }