github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/nomad/state/state_store.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 8 "github.com/hashicorp/go-memdb" 9 "github.com/hashicorp/nomad/nomad/structs" 10 ) 11 12 // IndexEntry is used with the "index" table 13 // for managing the latest Raft index affecting a table. 14 type IndexEntry struct { 15 Key string 16 Value uint64 17 } 18 19 // The StateStore is responsible for maintaining all the Nomad 20 // state. It is manipulated by the FSM which maintains consistency 21 // through the use of Raft. The goals of the StateStore are to provide 22 // high concurrency for read operations without blocking writes, and 23 // to provide write availability in the face of reads. EVERY object 24 // returned as a result of a read against the state store should be 25 // considered a constant and NEVER modified in place. 26 type StateStore struct { 27 logger *log.Logger 28 db *memdb.MemDB 29 30 // abandonCh is used to signal watchers that this state store has been 31 // abandoned (usually during a restore). This is only ever closed. 32 abandonCh chan struct{} 33 } 34 35 // NewStateStore is used to create a new state store 36 func NewStateStore(logOutput io.Writer) (*StateStore, error) { 37 // Create the MemDB 38 db, err := memdb.NewMemDB(stateStoreSchema()) 39 if err != nil { 40 return nil, fmt.Errorf("state store setup failed: %v", err) 41 } 42 43 // Create the state store 44 s := &StateStore{ 45 logger: log.New(logOutput, "", log.LstdFlags), 46 db: db, 47 abandonCh: make(chan struct{}), 48 } 49 return s, nil 50 } 51 52 // Snapshot is used to create a point in time snapshot. Because 53 // we use MemDB, we just need to snapshot the state of the underlying 54 // database. 55 func (s *StateStore) Snapshot() (*StateSnapshot, error) { 56 snap := &StateSnapshot{ 57 StateStore: StateStore{ 58 logger: s.logger, 59 db: s.db.Snapshot(), 60 }, 61 } 62 return snap, nil 63 } 64 65 // Restore is used to optimize the efficiency of rebuilding 66 // state by minimizing the number of transactions and checking 67 // overhead. 68 func (s *StateStore) Restore() (*StateRestore, error) { 69 txn := s.db.Txn(true) 70 r := &StateRestore{ 71 txn: txn, 72 } 73 return r, nil 74 } 75 76 // AbandonCh returns a channel you can wait on to know if the state store was 77 // abandoned. 78 func (s *StateStore) AbandonCh() <-chan struct{} { 79 return s.abandonCh 80 } 81 82 // Abandon is used to signal that the given state store has been abandoned. 83 // Calling this more than one time will panic. 84 func (s *StateStore) Abandon() { 85 close(s.abandonCh) 86 } 87 88 // UpsertJobSummary upserts a job summary into the state store. 89 func (s *StateStore) UpsertJobSummary(index uint64, jobSummary *structs.JobSummary) error { 90 txn := s.db.Txn(true) 91 defer txn.Abort() 92 93 // Update the index 94 if err := txn.Insert("job_summary", jobSummary); err != nil { 95 return err 96 } 97 98 // Update the indexes table for job summary 99 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 100 return fmt.Errorf("index update failed: %v", err) 101 } 102 103 txn.Commit() 104 return nil 105 } 106 107 // DeleteJobSummary deletes the job summary with the given ID. This is for 108 // testing purposes only. 109 func (s *StateStore) DeleteJobSummary(index uint64, id string) error { 110 txn := s.db.Txn(true) 111 defer txn.Abort() 112 113 // Delete the job summary 114 if _, err := txn.DeleteAll("job_summary", "id", id); err != nil { 115 return fmt.Errorf("deleting job summary failed: %v", err) 116 } 117 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 118 return fmt.Errorf("index update failed: %v", err) 119 } 120 txn.Commit() 121 return nil 122 } 123 124 // UpsertNode is used to register a node or update a node definition 125 // This is assumed to be triggered by the client, so we retain the value 126 // of drain which is set by the scheduler. 127 func (s *StateStore) UpsertNode(index uint64, node *structs.Node) error { 128 txn := s.db.Txn(true) 129 defer txn.Abort() 130 131 // Check if the node already exists 132 existing, err := txn.First("nodes", "id", node.ID) 133 if err != nil { 134 return fmt.Errorf("node lookup failed: %v", err) 135 } 136 137 // Setup the indexes correctly 138 if existing != nil { 139 exist := existing.(*structs.Node) 140 node.CreateIndex = exist.CreateIndex 141 node.ModifyIndex = index 142 node.Drain = exist.Drain // Retain the drain mode 143 } else { 144 node.CreateIndex = index 145 node.ModifyIndex = index 146 } 147 148 // Insert the node 149 if err := txn.Insert("nodes", node); err != nil { 150 return fmt.Errorf("node insert failed: %v", err) 151 } 152 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 153 return fmt.Errorf("index update failed: %v", err) 154 } 155 156 txn.Commit() 157 return nil 158 } 159 160 // DeleteNode is used to deregister a node 161 func (s *StateStore) DeleteNode(index uint64, nodeID string) error { 162 txn := s.db.Txn(true) 163 defer txn.Abort() 164 165 // Lookup the node 166 existing, err := txn.First("nodes", "id", nodeID) 167 if err != nil { 168 return fmt.Errorf("node lookup failed: %v", err) 169 } 170 if existing == nil { 171 return fmt.Errorf("node not found") 172 } 173 174 // Delete the node 175 if err := txn.Delete("nodes", existing); err != nil { 176 return fmt.Errorf("node delete failed: %v", err) 177 } 178 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 179 return fmt.Errorf("index update failed: %v", err) 180 } 181 182 txn.Commit() 183 return nil 184 } 185 186 // UpdateNodeStatus is used to update the status of a node 187 func (s *StateStore) UpdateNodeStatus(index uint64, nodeID, status string) error { 188 txn := s.db.Txn(true) 189 defer txn.Abort() 190 191 // Lookup the node 192 existing, err := txn.First("nodes", "id", nodeID) 193 if err != nil { 194 return fmt.Errorf("node lookup failed: %v", err) 195 } 196 if existing == nil { 197 return fmt.Errorf("node not found") 198 } 199 200 // Copy the existing node 201 existingNode := existing.(*structs.Node) 202 copyNode := new(structs.Node) 203 *copyNode = *existingNode 204 205 // Update the status in the copy 206 copyNode.Status = status 207 copyNode.ModifyIndex = index 208 209 // Insert the node 210 if err := txn.Insert("nodes", copyNode); err != nil { 211 return fmt.Errorf("node update failed: %v", err) 212 } 213 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 214 return fmt.Errorf("index update failed: %v", err) 215 } 216 217 txn.Commit() 218 return nil 219 } 220 221 // UpdateNodeDrain is used to update the drain of a node 222 func (s *StateStore) UpdateNodeDrain(index uint64, nodeID string, drain bool) error { 223 txn := s.db.Txn(true) 224 defer txn.Abort() 225 226 // Lookup the node 227 existing, err := txn.First("nodes", "id", nodeID) 228 if err != nil { 229 return fmt.Errorf("node lookup failed: %v", err) 230 } 231 if existing == nil { 232 return fmt.Errorf("node not found") 233 } 234 235 // Copy the existing node 236 existingNode := existing.(*structs.Node) 237 copyNode := new(structs.Node) 238 *copyNode = *existingNode 239 240 // Update the drain in the copy 241 copyNode.Drain = drain 242 copyNode.ModifyIndex = index 243 244 // Insert the node 245 if err := txn.Insert("nodes", copyNode); err != nil { 246 return fmt.Errorf("node update failed: %v", err) 247 } 248 if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { 249 return fmt.Errorf("index update failed: %v", err) 250 } 251 252 txn.Commit() 253 return nil 254 } 255 256 // NodeByID is used to lookup a node by ID 257 func (s *StateStore) NodeByID(ws memdb.WatchSet, nodeID string) (*structs.Node, error) { 258 txn := s.db.Txn(false) 259 260 watchCh, existing, err := txn.FirstWatch("nodes", "id", nodeID) 261 if err != nil { 262 return nil, fmt.Errorf("node lookup failed: %v", err) 263 } 264 ws.Add(watchCh) 265 266 if existing != nil { 267 return existing.(*structs.Node), nil 268 } 269 return nil, nil 270 } 271 272 // NodesByIDPrefix is used to lookup nodes by prefix 273 func (s *StateStore) NodesByIDPrefix(ws memdb.WatchSet, nodeID string) (memdb.ResultIterator, error) { 274 txn := s.db.Txn(false) 275 276 iter, err := txn.Get("nodes", "id_prefix", nodeID) 277 if err != nil { 278 return nil, fmt.Errorf("node lookup failed: %v", err) 279 } 280 ws.Add(iter.WatchCh()) 281 282 return iter, nil 283 } 284 285 // Nodes returns an iterator over all the nodes 286 func (s *StateStore) Nodes(ws memdb.WatchSet) (memdb.ResultIterator, error) { 287 txn := s.db.Txn(false) 288 289 // Walk the entire nodes table 290 iter, err := txn.Get("nodes", "id") 291 if err != nil { 292 return nil, err 293 } 294 ws.Add(iter.WatchCh()) 295 return iter, nil 296 } 297 298 // UpsertJob is used to register a job or update a job definition 299 func (s *StateStore) UpsertJob(index uint64, job *structs.Job) error { 300 txn := s.db.Txn(true) 301 defer txn.Abort() 302 303 // Check if the job already exists 304 existing, err := txn.First("jobs", "id", job.ID) 305 if err != nil { 306 return fmt.Errorf("job lookup failed: %v", err) 307 } 308 309 // Setup the indexes correctly 310 if existing != nil { 311 job.CreateIndex = existing.(*structs.Job).CreateIndex 312 job.ModifyIndex = index 313 job.JobModifyIndex = index 314 315 // Compute the job status 316 var err error 317 job.Status, err = s.getJobStatus(txn, job, false) 318 if err != nil { 319 return fmt.Errorf("setting job status for %q failed: %v", job.ID, err) 320 } 321 } else { 322 job.CreateIndex = index 323 job.ModifyIndex = index 324 job.JobModifyIndex = index 325 326 if err := s.setJobStatus(index, txn, job, false, ""); err != nil { 327 return fmt.Errorf("setting job status for %q failed: %v", job.ID, err) 328 } 329 330 // Have to get the job again since it could have been updated 331 updated, err := txn.First("jobs", "id", job.ID) 332 if err != nil { 333 return fmt.Errorf("job lookup failed: %v", err) 334 } 335 if updated != nil { 336 job = updated.(*structs.Job) 337 } 338 } 339 340 if err := s.updateSummaryWithJob(index, job, txn); err != nil { 341 return fmt.Errorf("unable to create job summary: %v", err) 342 } 343 344 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 345 // COMPAT 0.4.1 -> 0.5 346 s.addEphemeralDiskToTaskGroups(job) 347 348 // Insert the job 349 if err := txn.Insert("jobs", job); err != nil { 350 return fmt.Errorf("job insert failed: %v", err) 351 } 352 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 353 return fmt.Errorf("index update failed: %v", err) 354 } 355 356 txn.Commit() 357 return nil 358 } 359 360 // DeleteJob is used to deregister a job 361 func (s *StateStore) DeleteJob(index uint64, jobID string) error { 362 txn := s.db.Txn(true) 363 defer txn.Abort() 364 365 // Lookup the node 366 existing, err := txn.First("jobs", "id", jobID) 367 if err != nil { 368 return fmt.Errorf("job lookup failed: %v", err) 369 } 370 if existing == nil { 371 return fmt.Errorf("job not found") 372 } 373 374 // Check if we should update a parent job summary 375 job := existing.(*structs.Job) 376 if job.ParentID != "" { 377 summaryRaw, err := txn.First("job_summary", "id", job.ParentID) 378 if err != nil { 379 return fmt.Errorf("unable to retrieve summary for parent job: %v", err) 380 } 381 382 // Only continue if the summary exists. It could not exist if the parent 383 // job was removed 384 if summaryRaw != nil { 385 existing := summaryRaw.(*structs.JobSummary) 386 pSummary := existing.Copy() 387 if pSummary.Children != nil { 388 389 modified := false 390 switch job.Status { 391 case structs.JobStatusPending: 392 pSummary.Children.Pending-- 393 pSummary.Children.Dead++ 394 modified = true 395 case structs.JobStatusRunning: 396 pSummary.Children.Running-- 397 pSummary.Children.Dead++ 398 modified = true 399 case structs.JobStatusDead: 400 default: 401 return fmt.Errorf("unknown old job status %q", job.Status) 402 } 403 404 if modified { 405 // Update the modify index 406 pSummary.ModifyIndex = index 407 408 // Insert the summary 409 if err := txn.Insert("job_summary", pSummary); err != nil { 410 return fmt.Errorf("job summary insert failed: %v", err) 411 } 412 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 413 return fmt.Errorf("index update failed: %v", err) 414 } 415 } 416 } 417 } 418 } 419 420 // Delete the job 421 if err := txn.Delete("jobs", existing); err != nil { 422 return fmt.Errorf("job delete failed: %v", err) 423 } 424 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 425 return fmt.Errorf("index update failed: %v", err) 426 } 427 428 // Delete the job summary 429 if _, err = txn.DeleteAll("job_summary", "id", jobID); err != nil { 430 return fmt.Errorf("deleing job summary failed: %v", err) 431 } 432 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 433 return fmt.Errorf("index update failed: %v", err) 434 } 435 436 txn.Commit() 437 return nil 438 } 439 440 // JobByID is used to lookup a job by its ID 441 func (s *StateStore) JobByID(ws memdb.WatchSet, id string) (*structs.Job, error) { 442 txn := s.db.Txn(false) 443 444 watchCh, existing, err := txn.FirstWatch("jobs", "id", id) 445 if err != nil { 446 return nil, fmt.Errorf("job lookup failed: %v", err) 447 } 448 ws.Add(watchCh) 449 450 if existing != nil { 451 return existing.(*structs.Job), nil 452 } 453 return nil, nil 454 } 455 456 // JobsByIDPrefix is used to lookup a job by prefix 457 func (s *StateStore) JobsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) { 458 txn := s.db.Txn(false) 459 460 iter, err := txn.Get("jobs", "id_prefix", id) 461 if err != nil { 462 return nil, fmt.Errorf("job lookup failed: %v", err) 463 } 464 465 ws.Add(iter.WatchCh()) 466 467 return iter, nil 468 } 469 470 // Jobs returns an iterator over all the jobs 471 func (s *StateStore) Jobs(ws memdb.WatchSet) (memdb.ResultIterator, error) { 472 txn := s.db.Txn(false) 473 474 // Walk the entire jobs table 475 iter, err := txn.Get("jobs", "id") 476 if err != nil { 477 return nil, err 478 } 479 480 ws.Add(iter.WatchCh()) 481 482 return iter, nil 483 } 484 485 // JobsByPeriodic returns an iterator over all the periodic or non-periodic jobs. 486 func (s *StateStore) JobsByPeriodic(ws memdb.WatchSet, periodic bool) (memdb.ResultIterator, error) { 487 txn := s.db.Txn(false) 488 489 iter, err := txn.Get("jobs", "periodic", periodic) 490 if err != nil { 491 return nil, err 492 } 493 494 ws.Add(iter.WatchCh()) 495 496 return iter, nil 497 } 498 499 // JobsByScheduler returns an iterator over all the jobs with the specific 500 // scheduler type. 501 func (s *StateStore) JobsByScheduler(ws memdb.WatchSet, schedulerType string) (memdb.ResultIterator, error) { 502 txn := s.db.Txn(false) 503 504 // Return an iterator for jobs with the specific type. 505 iter, err := txn.Get("jobs", "type", schedulerType) 506 if err != nil { 507 return nil, err 508 } 509 510 ws.Add(iter.WatchCh()) 511 512 return iter, nil 513 } 514 515 // JobsByGC returns an iterator over all jobs eligible or uneligible for garbage 516 // collection. 517 func (s *StateStore) JobsByGC(ws memdb.WatchSet, gc bool) (memdb.ResultIterator, error) { 518 txn := s.db.Txn(false) 519 520 iter, err := txn.Get("jobs", "gc", gc) 521 if err != nil { 522 return nil, err 523 } 524 525 ws.Add(iter.WatchCh()) 526 527 return iter, nil 528 } 529 530 // JobSummary returns a job summary object which matches a specific id. 531 func (s *StateStore) JobSummaryByID(ws memdb.WatchSet, jobID string) (*structs.JobSummary, error) { 532 txn := s.db.Txn(false) 533 534 watchCh, existing, err := txn.FirstWatch("job_summary", "id", jobID) 535 if err != nil { 536 return nil, err 537 } 538 539 ws.Add(watchCh) 540 541 if existing != nil { 542 summary := existing.(*structs.JobSummary) 543 return summary, nil 544 } 545 546 return nil, nil 547 } 548 549 // JobSummaries walks the entire job summary table and returns all the job 550 // summary objects 551 func (s *StateStore) JobSummaries(ws memdb.WatchSet) (memdb.ResultIterator, error) { 552 txn := s.db.Txn(false) 553 554 iter, err := txn.Get("job_summary", "id") 555 if err != nil { 556 return nil, err 557 } 558 559 ws.Add(iter.WatchCh()) 560 561 return iter, nil 562 } 563 564 // JobSummaryByPrefix is used to look up Job Summary by id prefix 565 func (s *StateStore) JobSummaryByPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) { 566 txn := s.db.Txn(false) 567 568 iter, err := txn.Get("job_summary", "id_prefix", id) 569 if err != nil { 570 return nil, fmt.Errorf("eval lookup failed: %v", err) 571 } 572 573 ws.Add(iter.WatchCh()) 574 575 return iter, nil 576 } 577 578 // UpsertPeriodicLaunch is used to register a launch or update it. 579 func (s *StateStore) UpsertPeriodicLaunch(index uint64, launch *structs.PeriodicLaunch) error { 580 txn := s.db.Txn(true) 581 defer txn.Abort() 582 583 // Check if the job already exists 584 existing, err := txn.First("periodic_launch", "id", launch.ID) 585 if err != nil { 586 return fmt.Errorf("periodic launch lookup failed: %v", err) 587 } 588 589 // Setup the indexes correctly 590 if existing != nil { 591 launch.CreateIndex = existing.(*structs.PeriodicLaunch).CreateIndex 592 launch.ModifyIndex = index 593 } else { 594 launch.CreateIndex = index 595 launch.ModifyIndex = index 596 } 597 598 // Insert the job 599 if err := txn.Insert("periodic_launch", launch); err != nil { 600 return fmt.Errorf("launch insert failed: %v", err) 601 } 602 if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil { 603 return fmt.Errorf("index update failed: %v", err) 604 } 605 606 txn.Commit() 607 return nil 608 } 609 610 // DeletePeriodicLaunch is used to delete the periodic launch 611 func (s *StateStore) DeletePeriodicLaunch(index uint64, jobID string) error { 612 txn := s.db.Txn(true) 613 defer txn.Abort() 614 615 // Lookup the launch 616 existing, err := txn.First("periodic_launch", "id", jobID) 617 if err != nil { 618 return fmt.Errorf("launch lookup failed: %v", err) 619 } 620 if existing == nil { 621 return fmt.Errorf("launch not found") 622 } 623 624 // Delete the launch 625 if err := txn.Delete("periodic_launch", existing); err != nil { 626 return fmt.Errorf("launch delete failed: %v", err) 627 } 628 if err := txn.Insert("index", &IndexEntry{"periodic_launch", index}); err != nil { 629 return fmt.Errorf("index update failed: %v", err) 630 } 631 632 txn.Commit() 633 return nil 634 } 635 636 // PeriodicLaunchByID is used to lookup a periodic launch by the periodic job 637 // ID. 638 func (s *StateStore) PeriodicLaunchByID(ws memdb.WatchSet, id string) (*structs.PeriodicLaunch, error) { 639 txn := s.db.Txn(false) 640 641 watchCh, existing, err := txn.FirstWatch("periodic_launch", "id", id) 642 if err != nil { 643 return nil, fmt.Errorf("periodic launch lookup failed: %v", err) 644 } 645 646 ws.Add(watchCh) 647 648 if existing != nil { 649 return existing.(*structs.PeriodicLaunch), nil 650 } 651 return nil, nil 652 } 653 654 // PeriodicLaunches returns an iterator over all the periodic launches 655 func (s *StateStore) PeriodicLaunches(ws memdb.WatchSet) (memdb.ResultIterator, error) { 656 txn := s.db.Txn(false) 657 658 // Walk the entire table 659 iter, err := txn.Get("periodic_launch", "id") 660 if err != nil { 661 return nil, err 662 } 663 664 ws.Add(iter.WatchCh()) 665 666 return iter, nil 667 } 668 669 // UpsertEvals is used to upsert a set of evaluations 670 func (s *StateStore) UpsertEvals(index uint64, evals []*structs.Evaluation) error { 671 txn := s.db.Txn(true) 672 defer txn.Abort() 673 674 // Do a nested upsert 675 jobs := make(map[string]string, len(evals)) 676 for _, eval := range evals { 677 if err := s.nestedUpsertEval(txn, index, eval); err != nil { 678 return err 679 } 680 681 jobs[eval.JobID] = "" 682 } 683 684 // Set the job's status 685 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 686 return fmt.Errorf("setting job status failed: %v", err) 687 } 688 689 txn.Commit() 690 return nil 691 } 692 693 // nestedUpsertEvaluation is used to nest an evaluation upsert within a transaction 694 func (s *StateStore) nestedUpsertEval(txn *memdb.Txn, index uint64, eval *structs.Evaluation) error { 695 // Lookup the evaluation 696 existing, err := txn.First("evals", "id", eval.ID) 697 if err != nil { 698 return fmt.Errorf("eval lookup failed: %v", err) 699 } 700 701 // Update the indexes 702 if existing != nil { 703 eval.CreateIndex = existing.(*structs.Evaluation).CreateIndex 704 eval.ModifyIndex = index 705 } else { 706 eval.CreateIndex = index 707 eval.ModifyIndex = index 708 } 709 710 // Update the job summary 711 summaryRaw, err := txn.First("job_summary", "id", eval.JobID) 712 if err != nil { 713 return fmt.Errorf("job summary lookup failed: %v", err) 714 } 715 if summaryRaw != nil { 716 js := summaryRaw.(*structs.JobSummary).Copy() 717 hasSummaryChanged := false 718 for tg, num := range eval.QueuedAllocations { 719 if summary, ok := js.Summary[tg]; ok { 720 if summary.Queued != num { 721 summary.Queued = num 722 js.Summary[tg] = summary 723 hasSummaryChanged = true 724 } 725 } else { 726 s.logger.Printf("[ERR] state_store: unable to update queued for job %q and task group %q", eval.JobID, tg) 727 } 728 } 729 730 // Insert the job summary 731 if hasSummaryChanged { 732 js.ModifyIndex = index 733 if err := txn.Insert("job_summary", js); err != nil { 734 return fmt.Errorf("job summary insert failed: %v", err) 735 } 736 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 737 return fmt.Errorf("index update failed: %v", err) 738 } 739 } 740 } 741 742 // Check if the job has any blocked evaluations and cancel them 743 if eval.Status == structs.EvalStatusComplete && len(eval.FailedTGAllocs) == 0 { 744 // Get the blocked evaluation for a job if it exists 745 iter, err := txn.Get("evals", "job", eval.JobID, structs.EvalStatusBlocked) 746 if err != nil { 747 return fmt.Errorf("failed to get blocked evals for job %q: %v", eval.JobID, err) 748 } 749 750 var blocked []*structs.Evaluation 751 for { 752 raw := iter.Next() 753 if raw == nil { 754 break 755 } 756 blocked = append(blocked, raw.(*structs.Evaluation)) 757 } 758 759 // Go through and update the evals 760 for _, eval := range blocked { 761 newEval := eval.Copy() 762 newEval.Status = structs.EvalStatusCancelled 763 newEval.StatusDescription = fmt.Sprintf("evaluation %q successful", newEval.ID) 764 newEval.ModifyIndex = index 765 if err := txn.Insert("evals", newEval); err != nil { 766 return fmt.Errorf("eval insert failed: %v", err) 767 } 768 } 769 } 770 771 // Insert the eval 772 if err := txn.Insert("evals", eval); err != nil { 773 return fmt.Errorf("eval insert failed: %v", err) 774 } 775 if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil { 776 return fmt.Errorf("index update failed: %v", err) 777 } 778 return nil 779 } 780 781 // DeleteEval is used to delete an evaluation 782 func (s *StateStore) DeleteEval(index uint64, evals []string, allocs []string) error { 783 txn := s.db.Txn(true) 784 defer txn.Abort() 785 786 jobs := make(map[string]string, len(evals)) 787 for _, eval := range evals { 788 existing, err := txn.First("evals", "id", eval) 789 if err != nil { 790 return fmt.Errorf("eval lookup failed: %v", err) 791 } 792 if existing == nil { 793 continue 794 } 795 if err := txn.Delete("evals", existing); err != nil { 796 return fmt.Errorf("eval delete failed: %v", err) 797 } 798 jobID := existing.(*structs.Evaluation).JobID 799 jobs[jobID] = "" 800 } 801 802 for _, alloc := range allocs { 803 existing, err := txn.First("allocs", "id", alloc) 804 if err != nil { 805 return fmt.Errorf("alloc lookup failed: %v", err) 806 } 807 if existing == nil { 808 continue 809 } 810 if err := txn.Delete("allocs", existing); err != nil { 811 return fmt.Errorf("alloc delete failed: %v", err) 812 } 813 } 814 815 // Update the indexes 816 if err := txn.Insert("index", &IndexEntry{"evals", index}); err != nil { 817 return fmt.Errorf("index update failed: %v", err) 818 } 819 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 820 return fmt.Errorf("index update failed: %v", err) 821 } 822 823 // Set the job's status 824 if err := s.setJobStatuses(index, txn, jobs, true); err != nil { 825 return fmt.Errorf("setting job status failed: %v", err) 826 } 827 828 txn.Commit() 829 return nil 830 } 831 832 // EvalByID is used to lookup an eval by its ID 833 func (s *StateStore) EvalByID(ws memdb.WatchSet, id string) (*structs.Evaluation, error) { 834 txn := s.db.Txn(false) 835 836 watchCh, existing, err := txn.FirstWatch("evals", "id", id) 837 if err != nil { 838 return nil, fmt.Errorf("eval lookup failed: %v", err) 839 } 840 841 ws.Add(watchCh) 842 843 if existing != nil { 844 return existing.(*structs.Evaluation), nil 845 } 846 return nil, nil 847 } 848 849 // EvalsByIDPrefix is used to lookup evaluations by prefix 850 func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) { 851 txn := s.db.Txn(false) 852 853 iter, err := txn.Get("evals", "id_prefix", id) 854 if err != nil { 855 return nil, fmt.Errorf("eval lookup failed: %v", err) 856 } 857 858 ws.Add(iter.WatchCh()) 859 860 return iter, nil 861 } 862 863 // EvalsByJob returns all the evaluations by job id 864 func (s *StateStore) EvalsByJob(ws memdb.WatchSet, jobID string) ([]*structs.Evaluation, error) { 865 txn := s.db.Txn(false) 866 867 // Get an iterator over the node allocations 868 iter, err := txn.Get("evals", "job_prefix", jobID) 869 if err != nil { 870 return nil, err 871 } 872 873 ws.Add(iter.WatchCh()) 874 875 var out []*structs.Evaluation 876 for { 877 raw := iter.Next() 878 if raw == nil { 879 break 880 } 881 882 e := raw.(*structs.Evaluation) 883 884 // Filter non-exact matches 885 if e.JobID != jobID { 886 continue 887 } 888 889 out = append(out, e) 890 } 891 return out, nil 892 } 893 894 // Evals returns an iterator over all the evaluations 895 func (s *StateStore) Evals(ws memdb.WatchSet) (memdb.ResultIterator, error) { 896 txn := s.db.Txn(false) 897 898 // Walk the entire table 899 iter, err := txn.Get("evals", "id") 900 if err != nil { 901 return nil, err 902 } 903 904 ws.Add(iter.WatchCh()) 905 906 return iter, nil 907 } 908 909 // UpdateAllocsFromClient is used to update an allocation based on input 910 // from a client. While the schedulers are the authority on the allocation for 911 // most things, some updates are authoritative from the client. Specifically, 912 // the desired state comes from the schedulers, while the actual state comes 913 // from clients. 914 func (s *StateStore) UpdateAllocsFromClient(index uint64, allocs []*structs.Allocation) error { 915 txn := s.db.Txn(true) 916 defer txn.Abort() 917 918 // Handle each of the updated allocations 919 for _, alloc := range allocs { 920 if err := s.nestedUpdateAllocFromClient(txn, index, alloc); err != nil { 921 return err 922 } 923 } 924 925 // Update the indexes 926 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 927 return fmt.Errorf("index update failed: %v", err) 928 } 929 930 txn.Commit() 931 return nil 932 } 933 934 // nestedUpdateAllocFromClient is used to nest an update of an allocation with client status 935 func (s *StateStore) nestedUpdateAllocFromClient(txn *memdb.Txn, index uint64, alloc *structs.Allocation) error { 936 // Look for existing alloc 937 existing, err := txn.First("allocs", "id", alloc.ID) 938 if err != nil { 939 return fmt.Errorf("alloc lookup failed: %v", err) 940 } 941 942 // Nothing to do if this does not exist 943 if existing == nil { 944 return nil 945 } 946 exist := existing.(*structs.Allocation) 947 948 // Copy everything from the existing allocation 949 copyAlloc := exist.Copy() 950 951 // Pull in anything the client is the authority on 952 copyAlloc.ClientStatus = alloc.ClientStatus 953 copyAlloc.ClientDescription = alloc.ClientDescription 954 copyAlloc.TaskStates = alloc.TaskStates 955 956 // Update the modify index 957 copyAlloc.ModifyIndex = index 958 959 if err := s.updateSummaryWithAlloc(index, copyAlloc, exist, txn); err != nil { 960 return fmt.Errorf("error updating job summary: %v", err) 961 } 962 963 // Update the allocation 964 if err := txn.Insert("allocs", copyAlloc); err != nil { 965 return fmt.Errorf("alloc insert failed: %v", err) 966 } 967 968 // Set the job's status 969 forceStatus := "" 970 if !copyAlloc.TerminalStatus() { 971 forceStatus = structs.JobStatusRunning 972 } 973 jobs := map[string]string{exist.JobID: forceStatus} 974 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 975 return fmt.Errorf("setting job status failed: %v", err) 976 } 977 return nil 978 } 979 980 // UpsertAllocs is used to evict a set of allocations 981 // and allocate new ones at the same time. 982 func (s *StateStore) UpsertAllocs(index uint64, allocs []*structs.Allocation) error { 983 txn := s.db.Txn(true) 984 defer txn.Abort() 985 986 // Handle the allocations 987 jobs := make(map[string]string, 1) 988 for _, alloc := range allocs { 989 existing, err := txn.First("allocs", "id", alloc.ID) 990 if err != nil { 991 return fmt.Errorf("alloc lookup failed: %v", err) 992 } 993 exist, _ := existing.(*structs.Allocation) 994 995 if exist == nil { 996 alloc.CreateIndex = index 997 alloc.ModifyIndex = index 998 alloc.AllocModifyIndex = index 999 } else { 1000 alloc.CreateIndex = exist.CreateIndex 1001 alloc.ModifyIndex = index 1002 alloc.AllocModifyIndex = index 1003 1004 // If the scheduler is marking this allocation as lost we do not 1005 // want to reuse the status of the existing allocation. 1006 if alloc.ClientStatus != structs.AllocClientStatusLost { 1007 alloc.ClientStatus = exist.ClientStatus 1008 alloc.ClientDescription = exist.ClientDescription 1009 } 1010 1011 // The job has been denormalized so re-attach the original job 1012 if alloc.Job == nil { 1013 alloc.Job = exist.Job 1014 } 1015 } 1016 1017 if err := s.updateSummaryWithAlloc(index, alloc, exist, txn); err != nil { 1018 return fmt.Errorf("error updating job summary: %v", err) 1019 } 1020 1021 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 1022 // COMPAT 0.4.1 -> 0.5 1023 if alloc.Job != nil { 1024 s.addEphemeralDiskToTaskGroups(alloc.Job) 1025 } 1026 1027 if err := txn.Insert("allocs", alloc); err != nil { 1028 return fmt.Errorf("alloc insert failed: %v", err) 1029 } 1030 1031 // If the allocation is running, force the job to running status. 1032 forceStatus := "" 1033 if !alloc.TerminalStatus() { 1034 forceStatus = structs.JobStatusRunning 1035 } 1036 jobs[alloc.JobID] = forceStatus 1037 } 1038 1039 // Update the indexes 1040 if err := txn.Insert("index", &IndexEntry{"allocs", index}); err != nil { 1041 return fmt.Errorf("index update failed: %v", err) 1042 } 1043 1044 // Set the job's status 1045 if err := s.setJobStatuses(index, txn, jobs, false); err != nil { 1046 return fmt.Errorf("setting job status failed: %v", err) 1047 } 1048 1049 txn.Commit() 1050 return nil 1051 } 1052 1053 // AllocByID is used to lookup an allocation by its ID 1054 func (s *StateStore) AllocByID(ws memdb.WatchSet, id string) (*structs.Allocation, error) { 1055 txn := s.db.Txn(false) 1056 1057 watchCh, existing, err := txn.FirstWatch("allocs", "id", id) 1058 if err != nil { 1059 return nil, fmt.Errorf("alloc lookup failed: %v", err) 1060 } 1061 1062 ws.Add(watchCh) 1063 1064 if existing != nil { 1065 return existing.(*structs.Allocation), nil 1066 } 1067 return nil, nil 1068 } 1069 1070 // AllocsByIDPrefix is used to lookup allocs by prefix 1071 func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, id string) (memdb.ResultIterator, error) { 1072 txn := s.db.Txn(false) 1073 1074 iter, err := txn.Get("allocs", "id_prefix", id) 1075 if err != nil { 1076 return nil, fmt.Errorf("alloc lookup failed: %v", err) 1077 } 1078 1079 ws.Add(iter.WatchCh()) 1080 1081 return iter, nil 1082 } 1083 1084 // AllocsByNode returns all the allocations by node 1085 func (s *StateStore) AllocsByNode(ws memdb.WatchSet, node string) ([]*structs.Allocation, error) { 1086 txn := s.db.Txn(false) 1087 1088 // Get an iterator over the node allocations, using only the 1089 // node prefix which ignores the terminal status 1090 iter, err := txn.Get("allocs", "node_prefix", node) 1091 if err != nil { 1092 return nil, err 1093 } 1094 1095 ws.Add(iter.WatchCh()) 1096 1097 var out []*structs.Allocation 1098 for { 1099 raw := iter.Next() 1100 if raw == nil { 1101 break 1102 } 1103 out = append(out, raw.(*structs.Allocation)) 1104 } 1105 return out, nil 1106 } 1107 1108 // AllocsByNode returns all the allocations by node and terminal status 1109 func (s *StateStore) AllocsByNodeTerminal(ws memdb.WatchSet, node string, terminal bool) ([]*structs.Allocation, error) { 1110 txn := s.db.Txn(false) 1111 1112 // Get an iterator over the node allocations 1113 iter, err := txn.Get("allocs", "node", node, terminal) 1114 if err != nil { 1115 return nil, err 1116 } 1117 1118 ws.Add(iter.WatchCh()) 1119 1120 var out []*structs.Allocation 1121 for { 1122 raw := iter.Next() 1123 if raw == nil { 1124 break 1125 } 1126 out = append(out, raw.(*structs.Allocation)) 1127 } 1128 return out, nil 1129 } 1130 1131 // AllocsByJob returns all the allocations by job id 1132 func (s *StateStore) AllocsByJob(ws memdb.WatchSet, jobID string, all bool) ([]*structs.Allocation, error) { 1133 txn := s.db.Txn(false) 1134 1135 // Get the job 1136 var job *structs.Job 1137 rawJob, err := txn.First("jobs", "id", jobID) 1138 if err != nil { 1139 return nil, err 1140 } 1141 if rawJob != nil { 1142 job = rawJob.(*structs.Job) 1143 } 1144 1145 // Get an iterator over the node allocations 1146 iter, err := txn.Get("allocs", "job", jobID) 1147 if err != nil { 1148 return nil, err 1149 } 1150 1151 ws.Add(iter.WatchCh()) 1152 1153 var out []*structs.Allocation 1154 for { 1155 raw := iter.Next() 1156 if raw == nil { 1157 break 1158 } 1159 1160 alloc := raw.(*structs.Allocation) 1161 // If the allocation belongs to a job with the same ID but a different 1162 // create index and we are not getting all the allocations whose Jobs 1163 // matches the same Job ID then we skip it 1164 if !all && job != nil && alloc.Job.CreateIndex != job.CreateIndex { 1165 continue 1166 } 1167 out = append(out, raw.(*structs.Allocation)) 1168 } 1169 return out, nil 1170 } 1171 1172 // AllocsByEval returns all the allocations by eval id 1173 func (s *StateStore) AllocsByEval(ws memdb.WatchSet, evalID string) ([]*structs.Allocation, error) { 1174 txn := s.db.Txn(false) 1175 1176 // Get an iterator over the eval allocations 1177 iter, err := txn.Get("allocs", "eval", evalID) 1178 if err != nil { 1179 return nil, err 1180 } 1181 1182 ws.Add(iter.WatchCh()) 1183 1184 var out []*structs.Allocation 1185 for { 1186 raw := iter.Next() 1187 if raw == nil { 1188 break 1189 } 1190 out = append(out, raw.(*structs.Allocation)) 1191 } 1192 return out, nil 1193 } 1194 1195 // Allocs returns an iterator over all the evaluations 1196 func (s *StateStore) Allocs(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1197 txn := s.db.Txn(false) 1198 1199 // Walk the entire table 1200 iter, err := txn.Get("allocs", "id") 1201 if err != nil { 1202 return nil, err 1203 } 1204 1205 ws.Add(iter.WatchCh()) 1206 1207 return iter, nil 1208 } 1209 1210 // UpsertVaultAccessors is used to register a set of Vault Accessors 1211 func (s *StateStore) UpsertVaultAccessor(index uint64, accessors []*structs.VaultAccessor) error { 1212 txn := s.db.Txn(true) 1213 defer txn.Abort() 1214 1215 for _, accessor := range accessors { 1216 // Set the create index 1217 accessor.CreateIndex = index 1218 1219 // Insert the accessor 1220 if err := txn.Insert("vault_accessors", accessor); err != nil { 1221 return fmt.Errorf("accessor insert failed: %v", err) 1222 } 1223 } 1224 1225 if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil { 1226 return fmt.Errorf("index update failed: %v", err) 1227 } 1228 1229 txn.Commit() 1230 return nil 1231 } 1232 1233 // DeleteVaultAccessors is used to delete a set of Vault Accessors 1234 func (s *StateStore) DeleteVaultAccessors(index uint64, accessors []*structs.VaultAccessor) error { 1235 txn := s.db.Txn(true) 1236 defer txn.Abort() 1237 1238 // Lookup the accessor 1239 for _, accessor := range accessors { 1240 // Delete the accessor 1241 if err := txn.Delete("vault_accessors", accessor); err != nil { 1242 return fmt.Errorf("accessor delete failed: %v", err) 1243 } 1244 } 1245 1246 if err := txn.Insert("index", &IndexEntry{"vault_accessors", index}); err != nil { 1247 return fmt.Errorf("index update failed: %v", err) 1248 } 1249 1250 txn.Commit() 1251 return nil 1252 } 1253 1254 // VaultAccessor returns the given Vault accessor 1255 func (s *StateStore) VaultAccessor(ws memdb.WatchSet, accessor string) (*structs.VaultAccessor, error) { 1256 txn := s.db.Txn(false) 1257 1258 watchCh, existing, err := txn.FirstWatch("vault_accessors", "id", accessor) 1259 if err != nil { 1260 return nil, fmt.Errorf("accessor lookup failed: %v", err) 1261 } 1262 1263 ws.Add(watchCh) 1264 1265 if existing != nil { 1266 return existing.(*structs.VaultAccessor), nil 1267 } 1268 1269 return nil, nil 1270 } 1271 1272 // VaultAccessors returns an iterator of Vault accessors. 1273 func (s *StateStore) VaultAccessors(ws memdb.WatchSet) (memdb.ResultIterator, error) { 1274 txn := s.db.Txn(false) 1275 1276 iter, err := txn.Get("vault_accessors", "id") 1277 if err != nil { 1278 return nil, err 1279 } 1280 1281 ws.Add(iter.WatchCh()) 1282 1283 return iter, nil 1284 } 1285 1286 // VaultAccessorsByAlloc returns all the Vault accessors by alloc id 1287 func (s *StateStore) VaultAccessorsByAlloc(ws memdb.WatchSet, allocID string) ([]*structs.VaultAccessor, error) { 1288 txn := s.db.Txn(false) 1289 1290 // Get an iterator over the accessors 1291 iter, err := txn.Get("vault_accessors", "alloc_id", allocID) 1292 if err != nil { 1293 return nil, err 1294 } 1295 1296 ws.Add(iter.WatchCh()) 1297 1298 var out []*structs.VaultAccessor 1299 for { 1300 raw := iter.Next() 1301 if raw == nil { 1302 break 1303 } 1304 out = append(out, raw.(*structs.VaultAccessor)) 1305 } 1306 return out, nil 1307 } 1308 1309 // VaultAccessorsByNode returns all the Vault accessors by node id 1310 func (s *StateStore) VaultAccessorsByNode(ws memdb.WatchSet, nodeID string) ([]*structs.VaultAccessor, error) { 1311 txn := s.db.Txn(false) 1312 1313 // Get an iterator over the accessors 1314 iter, err := txn.Get("vault_accessors", "node_id", nodeID) 1315 if err != nil { 1316 return nil, err 1317 } 1318 1319 ws.Add(iter.WatchCh()) 1320 1321 var out []*structs.VaultAccessor 1322 for { 1323 raw := iter.Next() 1324 if raw == nil { 1325 break 1326 } 1327 out = append(out, raw.(*structs.VaultAccessor)) 1328 } 1329 return out, nil 1330 } 1331 1332 // LastIndex returns the greatest index value for all indexes 1333 func (s *StateStore) LatestIndex() (uint64, error) { 1334 indexes, err := s.Indexes() 1335 if err != nil { 1336 return 0, err 1337 } 1338 1339 var max uint64 = 0 1340 for { 1341 raw := indexes.Next() 1342 if raw == nil { 1343 break 1344 } 1345 1346 // Prepare the request struct 1347 idx := raw.(*IndexEntry) 1348 1349 // Determine the max 1350 if idx.Value > max { 1351 max = idx.Value 1352 } 1353 } 1354 1355 return max, nil 1356 } 1357 1358 // Index finds the matching index value 1359 func (s *StateStore) Index(name string) (uint64, error) { 1360 txn := s.db.Txn(false) 1361 1362 // Lookup the first matching index 1363 out, err := txn.First("index", "id", name) 1364 if err != nil { 1365 return 0, err 1366 } 1367 if out == nil { 1368 return 0, nil 1369 } 1370 return out.(*IndexEntry).Value, nil 1371 } 1372 1373 // RemoveIndex is a helper method to remove an index for testing purposes 1374 func (s *StateStore) RemoveIndex(name string) error { 1375 txn := s.db.Txn(true) 1376 defer txn.Abort() 1377 1378 if _, err := txn.DeleteAll("index", "id", name); err != nil { 1379 return err 1380 } 1381 1382 txn.Commit() 1383 return nil 1384 } 1385 1386 // Indexes returns an iterator over all the indexes 1387 func (s *StateStore) Indexes() (memdb.ResultIterator, error) { 1388 txn := s.db.Txn(false) 1389 1390 // Walk the entire nodes table 1391 iter, err := txn.Get("index", "id") 1392 if err != nil { 1393 return nil, err 1394 } 1395 return iter, nil 1396 } 1397 1398 // ReconcileJobSummaries re-creates summaries for all jobs present in the state 1399 // store 1400 func (s *StateStore) ReconcileJobSummaries(index uint64) error { 1401 txn := s.db.Txn(true) 1402 defer txn.Abort() 1403 1404 // Get all the jobs 1405 iter, err := txn.Get("jobs", "id") 1406 if err != nil { 1407 return err 1408 } 1409 for { 1410 rawJob := iter.Next() 1411 if rawJob == nil { 1412 break 1413 } 1414 job := rawJob.(*structs.Job) 1415 1416 // Create a job summary for the job 1417 summary := &structs.JobSummary{ 1418 JobID: job.ID, 1419 Summary: make(map[string]structs.TaskGroupSummary), 1420 } 1421 for _, tg := range job.TaskGroups { 1422 summary.Summary[tg.Name] = structs.TaskGroupSummary{} 1423 } 1424 1425 // Find all the allocations for the jobs 1426 iterAllocs, err := txn.Get("allocs", "job", job.ID) 1427 if err != nil { 1428 return err 1429 } 1430 1431 // Calculate the summary for the job 1432 for { 1433 rawAlloc := iterAllocs.Next() 1434 if rawAlloc == nil { 1435 break 1436 } 1437 alloc := rawAlloc.(*structs.Allocation) 1438 1439 // Ignore the allocation if it doesn't belong to the currently 1440 // registered job. The allocation is checked because of issue #2304 1441 if alloc.Job == nil || alloc.Job.CreateIndex != job.CreateIndex { 1442 continue 1443 } 1444 1445 tg := summary.Summary[alloc.TaskGroup] 1446 switch alloc.ClientStatus { 1447 case structs.AllocClientStatusFailed: 1448 tg.Failed += 1 1449 case structs.AllocClientStatusLost: 1450 tg.Lost += 1 1451 case structs.AllocClientStatusComplete: 1452 tg.Complete += 1 1453 case structs.AllocClientStatusRunning: 1454 tg.Running += 1 1455 case structs.AllocClientStatusPending: 1456 tg.Starting += 1 1457 default: 1458 s.logger.Printf("[ERR] state_store: invalid client status: %v in allocation %q", alloc.ClientStatus, alloc.ID) 1459 } 1460 summary.Summary[alloc.TaskGroup] = tg 1461 } 1462 1463 // Set the create index of the summary same as the job's create index 1464 // and the modify index to the current index 1465 summary.CreateIndex = job.CreateIndex 1466 summary.ModifyIndex = index 1467 1468 // Insert the job summary 1469 if err := txn.Insert("job_summary", summary); err != nil { 1470 return fmt.Errorf("error inserting job summary: %v", err) 1471 } 1472 } 1473 1474 // Update the indexes table for job summary 1475 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1476 return fmt.Errorf("index update failed: %v", err) 1477 } 1478 txn.Commit() 1479 return nil 1480 } 1481 1482 // setJobStatuses is a helper for calling setJobStatus on multiple jobs by ID. 1483 // It takes a map of job IDs to an optional forceStatus string. It returns an 1484 // error if the job doesn't exist or setJobStatus fails. 1485 func (s *StateStore) setJobStatuses(index uint64, txn *memdb.Txn, 1486 jobs map[string]string, evalDelete bool) error { 1487 for job, forceStatus := range jobs { 1488 existing, err := txn.First("jobs", "id", job) 1489 if err != nil { 1490 return fmt.Errorf("job lookup failed: %v", err) 1491 } 1492 1493 if existing == nil { 1494 continue 1495 } 1496 1497 if err := s.setJobStatus(index, txn, existing.(*structs.Job), evalDelete, forceStatus); err != nil { 1498 return err 1499 } 1500 } 1501 1502 return nil 1503 } 1504 1505 // setJobStatus sets the status of the job by looking up associated evaluations 1506 // and allocations. evalDelete should be set to true if setJobStatus is being 1507 // called because an evaluation is being deleted (potentially because of garbage 1508 // collection). If forceStatus is non-empty, the job's status will be set to the 1509 // passed status. 1510 func (s *StateStore) setJobStatus(index uint64, txn *memdb.Txn, 1511 job *structs.Job, evalDelete bool, forceStatus string) error { 1512 1513 // Capture the current status so we can check if there is a change 1514 oldStatus := job.Status 1515 if index == job.CreateIndex { 1516 oldStatus = "" 1517 } 1518 newStatus := forceStatus 1519 1520 // If forceStatus is not set, compute the jobs status. 1521 if forceStatus == "" { 1522 var err error 1523 newStatus, err = s.getJobStatus(txn, job, evalDelete) 1524 if err != nil { 1525 return err 1526 } 1527 } 1528 1529 // Fast-path if nothing has changed. 1530 if oldStatus == newStatus { 1531 return nil 1532 } 1533 1534 // Copy and update the existing job 1535 updated := job.Copy() 1536 updated.Status = newStatus 1537 updated.ModifyIndex = index 1538 1539 // Insert the job 1540 if err := txn.Insert("jobs", updated); err != nil { 1541 return fmt.Errorf("job insert failed: %v", err) 1542 } 1543 if err := txn.Insert("index", &IndexEntry{"jobs", index}); err != nil { 1544 return fmt.Errorf("index update failed: %v", err) 1545 } 1546 1547 // Update the children summary 1548 if updated.ParentID != "" { 1549 // Try to update the summary of the parent job summary 1550 summaryRaw, err := txn.First("job_summary", "id", updated.ParentID) 1551 if err != nil { 1552 return fmt.Errorf("unable to retrieve summary for parent job: %v", err) 1553 } 1554 1555 // Only continue if the summary exists. It could not exist if the parent 1556 // job was removed 1557 if summaryRaw != nil { 1558 existing := summaryRaw.(*structs.JobSummary) 1559 pSummary := existing.Copy() 1560 if pSummary.Children == nil { 1561 pSummary.Children = new(structs.JobChildrenSummary) 1562 } 1563 1564 // Determine the transistion and update the correct fields 1565 children := pSummary.Children 1566 1567 // Decrement old status 1568 if oldStatus != "" { 1569 switch oldStatus { 1570 case structs.JobStatusPending: 1571 children.Pending-- 1572 case structs.JobStatusRunning: 1573 children.Running-- 1574 case structs.JobStatusDead: 1575 children.Dead-- 1576 default: 1577 return fmt.Errorf("unknown old job status %q", oldStatus) 1578 } 1579 } 1580 1581 // Increment new status 1582 switch newStatus { 1583 case structs.JobStatusPending: 1584 children.Pending++ 1585 case structs.JobStatusRunning: 1586 children.Running++ 1587 case structs.JobStatusDead: 1588 children.Dead++ 1589 default: 1590 return fmt.Errorf("unknown new job status %q", newStatus) 1591 } 1592 1593 // Update the index 1594 pSummary.ModifyIndex = index 1595 1596 // Insert the summary 1597 if err := txn.Insert("job_summary", pSummary); err != nil { 1598 return fmt.Errorf("job summary insert failed: %v", err) 1599 } 1600 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1601 return fmt.Errorf("index update failed: %v", err) 1602 } 1603 } 1604 } 1605 1606 return nil 1607 } 1608 1609 func (s *StateStore) getJobStatus(txn *memdb.Txn, job *structs.Job, evalDelete bool) (string, error) { 1610 allocs, err := txn.Get("allocs", "job", job.ID) 1611 if err != nil { 1612 return "", err 1613 } 1614 1615 // If there is a non-terminal allocation, the job is running. 1616 hasAlloc := false 1617 for alloc := allocs.Next(); alloc != nil; alloc = allocs.Next() { 1618 hasAlloc = true 1619 if !alloc.(*structs.Allocation).TerminalStatus() { 1620 return structs.JobStatusRunning, nil 1621 } 1622 } 1623 1624 evals, err := txn.Get("evals", "job_prefix", job.ID) 1625 if err != nil { 1626 return "", err 1627 } 1628 1629 hasEval := false 1630 for raw := evals.Next(); raw != nil; raw = evals.Next() { 1631 e := raw.(*structs.Evaluation) 1632 1633 // Filter non-exact matches 1634 if e.JobID != job.ID { 1635 continue 1636 } 1637 1638 hasEval = true 1639 if !e.TerminalStatus() { 1640 return structs.JobStatusPending, nil 1641 } 1642 } 1643 1644 // The job is dead if all the allocations and evals are terminal or if there 1645 // are no evals because of garbage collection. 1646 if evalDelete || hasEval || hasAlloc { 1647 return structs.JobStatusDead, nil 1648 } 1649 1650 // If there are no allocations or evaluations it is a new job. If the 1651 // job is periodic or is a parameterized job, we mark it as running as 1652 // it will never have an allocation/evaluation against it. 1653 if job.IsPeriodic() || job.IsParameterized() { 1654 return structs.JobStatusRunning, nil 1655 } 1656 return structs.JobStatusPending, nil 1657 } 1658 1659 // updateSummaryWithJob creates or updates job summaries when new jobs are 1660 // upserted or existing ones are updated 1661 func (s *StateStore) updateSummaryWithJob(index uint64, job *structs.Job, 1662 txn *memdb.Txn) error { 1663 1664 // Update the job summary 1665 summaryRaw, err := txn.First("job_summary", "id", job.ID) 1666 if err != nil { 1667 return fmt.Errorf("job summary lookup failed: %v", err) 1668 } 1669 1670 // Get the summary or create if necessary 1671 var summary *structs.JobSummary 1672 hasSummaryChanged := false 1673 if summaryRaw != nil { 1674 summary = summaryRaw.(*structs.JobSummary).Copy() 1675 } else { 1676 summary = &structs.JobSummary{ 1677 JobID: job.ID, 1678 Summary: make(map[string]structs.TaskGroupSummary), 1679 Children: new(structs.JobChildrenSummary), 1680 CreateIndex: index, 1681 } 1682 hasSummaryChanged = true 1683 } 1684 1685 for _, tg := range job.TaskGroups { 1686 if _, ok := summary.Summary[tg.Name]; !ok { 1687 newSummary := structs.TaskGroupSummary{ 1688 Complete: 0, 1689 Failed: 0, 1690 Running: 0, 1691 Starting: 0, 1692 } 1693 summary.Summary[tg.Name] = newSummary 1694 hasSummaryChanged = true 1695 } 1696 } 1697 1698 // The job summary has changed, so update the modify index. 1699 if hasSummaryChanged { 1700 summary.ModifyIndex = index 1701 1702 // Update the indexes table for job summary 1703 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1704 return fmt.Errorf("index update failed: %v", err) 1705 } 1706 if err := txn.Insert("job_summary", summary); err != nil { 1707 return err 1708 } 1709 } 1710 1711 return nil 1712 } 1713 1714 // updateSummaryWithAlloc updates the job summary when allocations are updated 1715 // or inserted 1716 func (s *StateStore) updateSummaryWithAlloc(index uint64, alloc *structs.Allocation, 1717 existingAlloc *structs.Allocation, txn *memdb.Txn) error { 1718 1719 // We don't have to update the summary if the job is missing 1720 if alloc.Job == nil { 1721 return nil 1722 } 1723 1724 summaryRaw, err := txn.First("job_summary", "id", alloc.JobID) 1725 if err != nil { 1726 return fmt.Errorf("unable to lookup job summary for job id %q: %v", alloc.JobID, err) 1727 } 1728 1729 if summaryRaw == nil { 1730 // Check if the job is de-registered 1731 rawJob, err := txn.First("jobs", "id", alloc.JobID) 1732 if err != nil { 1733 return fmt.Errorf("unable to query job: %v", err) 1734 } 1735 1736 // If the job is de-registered then we skip updating it's summary 1737 if rawJob == nil { 1738 return nil 1739 } 1740 1741 return fmt.Errorf("job summary for job %q is not present", alloc.JobID) 1742 } 1743 1744 // Get a copy of the existing summary 1745 jobSummary := summaryRaw.(*structs.JobSummary).Copy() 1746 1747 // Not updating the job summary because the allocation doesn't belong to the 1748 // currently registered job 1749 if jobSummary.CreateIndex != alloc.Job.CreateIndex { 1750 return nil 1751 } 1752 1753 tgSummary, ok := jobSummary.Summary[alloc.TaskGroup] 1754 if !ok { 1755 return fmt.Errorf("unable to find task group in the job summary: %v", alloc.TaskGroup) 1756 } 1757 1758 summaryChanged := false 1759 if existingAlloc == nil { 1760 switch alloc.DesiredStatus { 1761 case structs.AllocDesiredStatusStop, structs.AllocDesiredStatusEvict: 1762 s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v", 1763 alloc.ID, alloc.DesiredStatus) 1764 } 1765 switch alloc.ClientStatus { 1766 case structs.AllocClientStatusPending: 1767 tgSummary.Starting += 1 1768 if tgSummary.Queued > 0 { 1769 tgSummary.Queued -= 1 1770 } 1771 summaryChanged = true 1772 case structs.AllocClientStatusRunning, structs.AllocClientStatusFailed, 1773 structs.AllocClientStatusComplete: 1774 s.logger.Printf("[ERR] state_store: new allocation inserted into state store with id: %v and state: %v", 1775 alloc.ID, alloc.ClientStatus) 1776 } 1777 } else if existingAlloc.ClientStatus != alloc.ClientStatus { 1778 // Incrementing the client of the bin of the current state 1779 switch alloc.ClientStatus { 1780 case structs.AllocClientStatusRunning: 1781 tgSummary.Running += 1 1782 case structs.AllocClientStatusFailed: 1783 tgSummary.Failed += 1 1784 case structs.AllocClientStatusPending: 1785 tgSummary.Starting += 1 1786 case structs.AllocClientStatusComplete: 1787 tgSummary.Complete += 1 1788 case structs.AllocClientStatusLost: 1789 tgSummary.Lost += 1 1790 } 1791 1792 // Decrementing the count of the bin of the last state 1793 switch existingAlloc.ClientStatus { 1794 case structs.AllocClientStatusRunning: 1795 tgSummary.Running -= 1 1796 case structs.AllocClientStatusPending: 1797 tgSummary.Starting -= 1 1798 case structs.AllocClientStatusLost: 1799 tgSummary.Lost -= 1 1800 case structs.AllocClientStatusFailed, structs.AllocClientStatusComplete: 1801 default: 1802 s.logger.Printf("[ERR] state_store: invalid old state of allocation with id: %v, and state: %v", 1803 existingAlloc.ID, existingAlloc.ClientStatus) 1804 } 1805 summaryChanged = true 1806 } 1807 jobSummary.Summary[alloc.TaskGroup] = tgSummary 1808 1809 if summaryChanged { 1810 jobSummary.ModifyIndex = index 1811 1812 // Update the indexes table for job summary 1813 if err := txn.Insert("index", &IndexEntry{"job_summary", index}); err != nil { 1814 return fmt.Errorf("index update failed: %v", err) 1815 } 1816 1817 if err := txn.Insert("job_summary", jobSummary); err != nil { 1818 return fmt.Errorf("updating job summary failed: %v", err) 1819 } 1820 } 1821 1822 return nil 1823 } 1824 1825 // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups 1826 func (s *StateStore) addEphemeralDiskToTaskGroups(job *structs.Job) { 1827 for _, tg := range job.TaskGroups { 1828 var diskMB int 1829 for _, task := range tg.Tasks { 1830 if task.Resources != nil { 1831 diskMB += task.Resources.DiskMB 1832 task.Resources.DiskMB = 0 1833 } 1834 } 1835 if tg.EphemeralDisk != nil { 1836 continue 1837 } 1838 tg.EphemeralDisk = &structs.EphemeralDisk{ 1839 SizeMB: diskMB, 1840 } 1841 } 1842 } 1843 1844 // StateSnapshot is used to provide a point-in-time snapshot 1845 type StateSnapshot struct { 1846 StateStore 1847 } 1848 1849 // StateRestore is used to optimize the performance when 1850 // restoring state by only using a single large transaction 1851 // instead of thousands of sub transactions 1852 type StateRestore struct { 1853 txn *memdb.Txn 1854 } 1855 1856 // Abort is used to abort the restore operation 1857 func (s *StateRestore) Abort() { 1858 s.txn.Abort() 1859 } 1860 1861 // Commit is used to commit the restore operation 1862 func (s *StateRestore) Commit() { 1863 s.txn.Commit() 1864 } 1865 1866 // NodeRestore is used to restore a node 1867 func (r *StateRestore) NodeRestore(node *structs.Node) error { 1868 if err := r.txn.Insert("nodes", node); err != nil { 1869 return fmt.Errorf("node insert failed: %v", err) 1870 } 1871 return nil 1872 } 1873 1874 // JobRestore is used to restore a job 1875 func (r *StateRestore) JobRestore(job *structs.Job) error { 1876 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 1877 // COMPAT 0.4.1 -> 0.5 1878 r.addEphemeralDiskToTaskGroups(job) 1879 1880 if err := r.txn.Insert("jobs", job); err != nil { 1881 return fmt.Errorf("job insert failed: %v", err) 1882 } 1883 return nil 1884 } 1885 1886 // EvalRestore is used to restore an evaluation 1887 func (r *StateRestore) EvalRestore(eval *structs.Evaluation) error { 1888 if err := r.txn.Insert("evals", eval); err != nil { 1889 return fmt.Errorf("eval insert failed: %v", err) 1890 } 1891 return nil 1892 } 1893 1894 // AllocRestore is used to restore an allocation 1895 func (r *StateRestore) AllocRestore(alloc *structs.Allocation) error { 1896 // Set the shared resources if it's not present 1897 // COMPAT 0.4.1 -> 0.5 1898 if alloc.SharedResources == nil { 1899 alloc.SharedResources = &structs.Resources{ 1900 DiskMB: alloc.Resources.DiskMB, 1901 } 1902 } 1903 1904 // Create the EphemeralDisk if it's nil by adding up DiskMB from task resources. 1905 if alloc.Job != nil { 1906 r.addEphemeralDiskToTaskGroups(alloc.Job) 1907 } 1908 1909 if err := r.txn.Insert("allocs", alloc); err != nil { 1910 return fmt.Errorf("alloc insert failed: %v", err) 1911 } 1912 return nil 1913 } 1914 1915 // IndexRestore is used to restore an index 1916 func (r *StateRestore) IndexRestore(idx *IndexEntry) error { 1917 if err := r.txn.Insert("index", idx); err != nil { 1918 return fmt.Errorf("index insert failed: %v", err) 1919 } 1920 return nil 1921 } 1922 1923 // PeriodicLaunchRestore is used to restore a periodic launch. 1924 func (r *StateRestore) PeriodicLaunchRestore(launch *structs.PeriodicLaunch) error { 1925 if err := r.txn.Insert("periodic_launch", launch); err != nil { 1926 return fmt.Errorf("periodic launch insert failed: %v", err) 1927 } 1928 return nil 1929 } 1930 1931 // JobSummaryRestore is used to restore a job summary 1932 func (r *StateRestore) JobSummaryRestore(jobSummary *structs.JobSummary) error { 1933 if err := r.txn.Insert("job_summary", jobSummary); err != nil { 1934 return fmt.Errorf("job summary insert failed: %v", err) 1935 } 1936 return nil 1937 } 1938 1939 // VaultAccessorRestore is used to restore a vault accessor 1940 func (r *StateRestore) VaultAccessorRestore(accessor *structs.VaultAccessor) error { 1941 if err := r.txn.Insert("vault_accessors", accessor); err != nil { 1942 return fmt.Errorf("vault accessor insert failed: %v", err) 1943 } 1944 return nil 1945 } 1946 1947 // addEphemeralDiskToTaskGroups adds missing EphemeralDisk objects to TaskGroups 1948 func (r *StateRestore) addEphemeralDiskToTaskGroups(job *structs.Job) { 1949 for _, tg := range job.TaskGroups { 1950 if tg.EphemeralDisk != nil { 1951 continue 1952 } 1953 var sizeMB int 1954 for _, task := range tg.Tasks { 1955 if task.Resources != nil { 1956 sizeMB += task.Resources.DiskMB 1957 task.Resources.DiskMB = 0 1958 } 1959 } 1960 tg.EphemeralDisk = &structs.EphemeralDisk{ 1961 SizeMB: sizeMB, 1962 } 1963 } 1964 }