github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/nomad/state/schema.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "sync" 6 7 memdb "github.com/hashicorp/go-memdb" 8 9 "github.com/hashicorp/nomad/nomad/structs" 10 ) 11 12 var ( 13 schemaFactories SchemaFactories 14 factoriesLock sync.Mutex 15 ) 16 17 // SchemaFactory is the factory method for returning a TableSchema 18 type SchemaFactory func() *memdb.TableSchema 19 type SchemaFactories []SchemaFactory 20 21 // RegisterSchemaFactories is used to register a table schema. 22 func RegisterSchemaFactories(factories ...SchemaFactory) { 23 factoriesLock.Lock() 24 defer factoriesLock.Unlock() 25 schemaFactories = append(schemaFactories, factories...) 26 } 27 28 func GetFactories() SchemaFactories { 29 return schemaFactories 30 } 31 32 func init() { 33 // Register all schemas 34 RegisterSchemaFactories([]SchemaFactory{ 35 indexTableSchema, 36 nodeTableSchema, 37 jobTableSchema, 38 jobSummarySchema, 39 jobVersionSchema, 40 deploymentSchema, 41 periodicLaunchTableSchema, 42 evalTableSchema, 43 allocTableSchema, 44 vaultAccessorTableSchema, 45 siTokenAccessorTableSchema, 46 aclPolicyTableSchema, 47 aclTokenTableSchema, 48 autopilotConfigTableSchema, 49 schedulerConfigTableSchema, 50 clusterMetaTableSchema, 51 csiVolumeTableSchema, 52 csiPluginTableSchema, 53 scalingPolicyTableSchema, 54 scalingEventTableSchema, 55 }...) 56 } 57 58 // stateStoreSchema is used to return the schema for the state store 59 func stateStoreSchema() *memdb.DBSchema { 60 // Create the root DB schema 61 db := &memdb.DBSchema{ 62 Tables: make(map[string]*memdb.TableSchema), 63 } 64 65 // Add each of the tables 66 for _, schemaFn := range GetFactories() { 67 schema := schemaFn() 68 if _, ok := db.Tables[schema.Name]; ok { 69 panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) 70 } 71 db.Tables[schema.Name] = schema 72 } 73 return db 74 } 75 76 // indexTableSchema is used for tracking the most recent index used for each table. 77 func indexTableSchema() *memdb.TableSchema { 78 return &memdb.TableSchema{ 79 Name: "index", 80 Indexes: map[string]*memdb.IndexSchema{ 81 "id": { 82 Name: "id", 83 AllowMissing: false, 84 Unique: true, 85 Indexer: &memdb.StringFieldIndex{ 86 Field: "Key", 87 Lowercase: true, 88 }, 89 }, 90 }, 91 } 92 } 93 94 // nodeTableSchema returns the MemDB schema for the nodes table. 95 // This table is used to store all the client nodes that are registered. 96 func nodeTableSchema() *memdb.TableSchema { 97 return &memdb.TableSchema{ 98 Name: "nodes", 99 Indexes: map[string]*memdb.IndexSchema{ 100 // Primary index is used for node management 101 // and simple direct lookup. ID is required to be 102 // unique. 103 "id": { 104 Name: "id", 105 AllowMissing: false, 106 Unique: true, 107 Indexer: &memdb.UUIDFieldIndex{ 108 Field: "ID", 109 }, 110 }, 111 "token": { 112 Name: "token", 113 AllowMissing: true, 114 Unique: false, 115 Indexer: &memdb.UUIDFieldIndex{ 116 Field: "Token", 117 }, 118 }, 119 "secret_id": { 120 Name: "secret_id", 121 AllowMissing: false, 122 Unique: true, 123 Indexer: &memdb.UUIDFieldIndex{ 124 Field: "SecretID", 125 }, 126 }, 127 }, 128 } 129 } 130 131 // jobTableSchema returns the MemDB schema for the jobs table. 132 // This table is used to store all the jobs that have been submitted. 133 func jobTableSchema() *memdb.TableSchema { 134 return &memdb.TableSchema{ 135 Name: "jobs", 136 Indexes: map[string]*memdb.IndexSchema{ 137 // Primary index is used for job management 138 // and simple direct lookup. ID is required to be 139 // unique within a namespace. 140 "id": { 141 Name: "id", 142 AllowMissing: false, 143 Unique: true, 144 145 // Use a compound index so the tuple of (Namespace, ID) is 146 // uniquely identifying 147 Indexer: &memdb.CompoundIndex{ 148 Indexes: []memdb.Indexer{ 149 &memdb.StringFieldIndex{ 150 Field: "Namespace", 151 }, 152 153 &memdb.StringFieldIndex{ 154 Field: "ID", 155 }, 156 }, 157 }, 158 }, 159 "type": { 160 Name: "type", 161 AllowMissing: false, 162 Unique: false, 163 Indexer: &memdb.StringFieldIndex{ 164 Field: "Type", 165 Lowercase: false, 166 }, 167 }, 168 "gc": { 169 Name: "gc", 170 AllowMissing: false, 171 Unique: false, 172 Indexer: &memdb.ConditionalIndex{ 173 Conditional: jobIsGCable, 174 }, 175 }, 176 "periodic": { 177 Name: "periodic", 178 AllowMissing: false, 179 Unique: false, 180 Indexer: &memdb.ConditionalIndex{ 181 Conditional: jobIsPeriodic, 182 }, 183 }, 184 }, 185 } 186 } 187 188 // jobSummarySchema returns the memdb schema for the job summary table 189 func jobSummarySchema() *memdb.TableSchema { 190 return &memdb.TableSchema{ 191 Name: "job_summary", 192 Indexes: map[string]*memdb.IndexSchema{ 193 "id": { 194 Name: "id", 195 AllowMissing: false, 196 Unique: true, 197 198 // Use a compound index so the tuple of (Namespace, JobID) is 199 // uniquely identifying 200 Indexer: &memdb.CompoundIndex{ 201 Indexes: []memdb.Indexer{ 202 &memdb.StringFieldIndex{ 203 Field: "Namespace", 204 }, 205 206 &memdb.StringFieldIndex{ 207 Field: "JobID", 208 }, 209 }, 210 }, 211 }, 212 }, 213 } 214 } 215 216 // jobVersionSchema returns the memdb schema for the job version table which 217 // keeps a historical view of job versions. 218 func jobVersionSchema() *memdb.TableSchema { 219 return &memdb.TableSchema{ 220 Name: "job_version", 221 Indexes: map[string]*memdb.IndexSchema{ 222 "id": { 223 Name: "id", 224 AllowMissing: false, 225 Unique: true, 226 227 // Use a compound index so the tuple of (Namespace, ID, Version) is 228 // uniquely identifying 229 Indexer: &memdb.CompoundIndex{ 230 Indexes: []memdb.Indexer{ 231 &memdb.StringFieldIndex{ 232 Field: "Namespace", 233 }, 234 235 &memdb.StringFieldIndex{ 236 Field: "ID", 237 Lowercase: true, 238 }, 239 240 &memdb.UintFieldIndex{ 241 Field: "Version", 242 }, 243 }, 244 }, 245 }, 246 }, 247 } 248 } 249 250 // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index 251 // on whether a job is eligible for garbage collection. 252 func jobIsGCable(obj interface{}) (bool, error) { 253 j, ok := obj.(*structs.Job) 254 if !ok { 255 return false, fmt.Errorf("Unexpected type: %v", obj) 256 } 257 258 // If the job is periodic or parameterized it is only garbage collectable if 259 // it is stopped. 260 periodic := j.Periodic != nil && j.Periodic.Enabled 261 parameterized := j.IsParameterized() 262 if periodic || parameterized { 263 return j.Stop, nil 264 } 265 266 // If the job isn't dead it isn't eligible 267 if j.Status != structs.JobStatusDead { 268 return false, nil 269 } 270 271 // Any job that is stopped is eligible for garbage collection 272 if j.Stop { 273 return true, nil 274 } 275 276 // Otherwise, only batch jobs are eligible because they complete on their 277 // own without a user stopping them. 278 if j.Type != structs.JobTypeBatch { 279 return false, nil 280 } 281 282 return true, nil 283 } 284 285 // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index 286 // on whether a job is periodic. 287 func jobIsPeriodic(obj interface{}) (bool, error) { 288 j, ok := obj.(*structs.Job) 289 if !ok { 290 return false, fmt.Errorf("Unexpected type: %v", obj) 291 } 292 293 if j.Periodic != nil && j.Periodic.Enabled == true { 294 return true, nil 295 } 296 297 return false, nil 298 } 299 300 // deploymentSchema returns the MemDB schema tracking a job's deployments 301 func deploymentSchema() *memdb.TableSchema { 302 return &memdb.TableSchema{ 303 Name: "deployment", 304 Indexes: map[string]*memdb.IndexSchema{ 305 "id": { 306 Name: "id", 307 AllowMissing: false, 308 Unique: true, 309 Indexer: &memdb.UUIDFieldIndex{ 310 Field: "ID", 311 }, 312 }, 313 314 "namespace": { 315 Name: "namespace", 316 AllowMissing: false, 317 Unique: false, 318 Indexer: &memdb.StringFieldIndex{ 319 Field: "Namespace", 320 }, 321 }, 322 323 // Job index is used to lookup deployments by job 324 "job": { 325 Name: "job", 326 AllowMissing: false, 327 Unique: false, 328 329 // Use a compound index so the tuple of (Namespace, JobID) is 330 // uniquely identifying 331 Indexer: &memdb.CompoundIndex{ 332 Indexes: []memdb.Indexer{ 333 &memdb.StringFieldIndex{ 334 Field: "Namespace", 335 }, 336 337 &memdb.StringFieldIndex{ 338 Field: "JobID", 339 }, 340 }, 341 }, 342 }, 343 }, 344 } 345 } 346 347 // periodicLaunchTableSchema returns the MemDB schema tracking the most recent 348 // launch time for a periodic job. 349 func periodicLaunchTableSchema() *memdb.TableSchema { 350 return &memdb.TableSchema{ 351 Name: "periodic_launch", 352 Indexes: map[string]*memdb.IndexSchema{ 353 // Primary index is used for job management 354 // and simple direct lookup. ID is required to be 355 // unique. 356 "id": { 357 Name: "id", 358 AllowMissing: false, 359 Unique: true, 360 361 // Use a compound index so the tuple of (Namespace, JobID) is 362 // uniquely identifying 363 Indexer: &memdb.CompoundIndex{ 364 Indexes: []memdb.Indexer{ 365 &memdb.StringFieldIndex{ 366 Field: "Namespace", 367 }, 368 369 &memdb.StringFieldIndex{ 370 Field: "ID", 371 }, 372 }, 373 }, 374 }, 375 }, 376 } 377 } 378 379 // evalTableSchema returns the MemDB schema for the eval table. 380 // This table is used to store all the evaluations that are pending 381 // or recently completed. 382 func evalTableSchema() *memdb.TableSchema { 383 return &memdb.TableSchema{ 384 Name: "evals", 385 Indexes: map[string]*memdb.IndexSchema{ 386 // Primary index is used for direct lookup. 387 "id": { 388 Name: "id", 389 AllowMissing: false, 390 Unique: true, 391 Indexer: &memdb.UUIDFieldIndex{ 392 Field: "ID", 393 }, 394 }, 395 396 "namespace": { 397 Name: "namespace", 398 AllowMissing: false, 399 Unique: false, 400 Indexer: &memdb.StringFieldIndex{ 401 Field: "Namespace", 402 }, 403 }, 404 405 // Job index is used to lookup allocations by job 406 "job": { 407 Name: "job", 408 AllowMissing: false, 409 Unique: false, 410 Indexer: &memdb.CompoundIndex{ 411 Indexes: []memdb.Indexer{ 412 &memdb.StringFieldIndex{ 413 Field: "Namespace", 414 }, 415 416 &memdb.StringFieldIndex{ 417 Field: "JobID", 418 Lowercase: true, 419 }, 420 421 &memdb.StringFieldIndex{ 422 Field: "Status", 423 Lowercase: true, 424 }, 425 }, 426 }, 427 }, 428 }, 429 } 430 } 431 432 // allocTableSchema returns the MemDB schema for the allocation table. 433 // This table is used to store all the task allocations between task groups 434 // and nodes. 435 func allocTableSchema() *memdb.TableSchema { 436 return &memdb.TableSchema{ 437 Name: "allocs", 438 Indexes: map[string]*memdb.IndexSchema{ 439 // Primary index is a UUID 440 "id": { 441 Name: "id", 442 AllowMissing: false, 443 Unique: true, 444 Indexer: &memdb.UUIDFieldIndex{ 445 Field: "ID", 446 }, 447 }, 448 449 "namespace": { 450 Name: "namespace", 451 AllowMissing: false, 452 Unique: false, 453 Indexer: &memdb.StringFieldIndex{ 454 Field: "Namespace", 455 }, 456 }, 457 458 // Node index is used to lookup allocations by node 459 "node": { 460 Name: "node", 461 AllowMissing: true, // Missing is allow for failed allocations 462 Unique: false, 463 Indexer: &memdb.CompoundIndex{ 464 Indexes: []memdb.Indexer{ 465 &memdb.StringFieldIndex{ 466 Field: "NodeID", 467 Lowercase: true, 468 }, 469 470 // Conditional indexer on if allocation is terminal 471 &memdb.ConditionalIndex{ 472 Conditional: func(obj interface{}) (bool, error) { 473 // Cast to allocation 474 alloc, ok := obj.(*structs.Allocation) 475 if !ok { 476 return false, fmt.Errorf("wrong type, got %t should be Allocation", obj) 477 } 478 479 // Check if the allocation is terminal 480 return alloc.TerminalStatus(), nil 481 }, 482 }, 483 }, 484 }, 485 }, 486 487 // Job index is used to lookup allocations by job 488 "job": { 489 Name: "job", 490 AllowMissing: false, 491 Unique: false, 492 493 Indexer: &memdb.CompoundIndex{ 494 Indexes: []memdb.Indexer{ 495 &memdb.StringFieldIndex{ 496 Field: "Namespace", 497 }, 498 499 &memdb.StringFieldIndex{ 500 Field: "JobID", 501 }, 502 }, 503 }, 504 }, 505 506 // Eval index is used to lookup allocations by eval 507 "eval": { 508 Name: "eval", 509 AllowMissing: false, 510 Unique: false, 511 Indexer: &memdb.UUIDFieldIndex{ 512 Field: "EvalID", 513 }, 514 }, 515 516 // Deployment index is used to lookup allocations by deployment 517 "deployment": { 518 Name: "deployment", 519 AllowMissing: true, 520 Unique: false, 521 Indexer: &memdb.UUIDFieldIndex{ 522 Field: "DeploymentID", 523 }, 524 }, 525 }, 526 } 527 } 528 529 // vaultAccessorTableSchema returns the MemDB schema for the Vault Accessor 530 // Table. This table tracks Vault accessors for tokens created on behalf of 531 // allocations required Vault tokens. 532 func vaultAccessorTableSchema() *memdb.TableSchema { 533 return &memdb.TableSchema{ 534 Name: "vault_accessors", 535 Indexes: map[string]*memdb.IndexSchema{ 536 // The primary index is the accessor id 537 "id": { 538 Name: "id", 539 AllowMissing: false, 540 Unique: true, 541 Indexer: &memdb.StringFieldIndex{ 542 Field: "Accessor", 543 }, 544 }, 545 546 "alloc_id": { 547 Name: "alloc_id", 548 AllowMissing: false, 549 Unique: false, 550 Indexer: &memdb.StringFieldIndex{ 551 Field: "AllocID", 552 }, 553 }, 554 555 "node_id": { 556 Name: "node_id", 557 AllowMissing: false, 558 Unique: false, 559 Indexer: &memdb.StringFieldIndex{ 560 Field: "NodeID", 561 }, 562 }, 563 }, 564 } 565 } 566 567 // siTokenAccessorTableSchema returns the MemDB schema for the Service Identity 568 // token accessor table. This table tracks accessors for tokens created on behalf 569 // of allocations with Consul connect enabled tasks that need SI tokens. 570 func siTokenAccessorTableSchema() *memdb.TableSchema { 571 return &memdb.TableSchema{ 572 Name: siTokenAccessorTable, 573 Indexes: map[string]*memdb.IndexSchema{ 574 // The primary index is the accessor id 575 "id": { 576 Name: "id", 577 AllowMissing: false, 578 Unique: true, 579 Indexer: &memdb.StringFieldIndex{ 580 Field: "AccessorID", 581 }, 582 }, 583 584 "alloc_id": { 585 Name: "alloc_id", 586 AllowMissing: false, 587 Unique: false, 588 Indexer: &memdb.StringFieldIndex{ 589 Field: "AllocID", 590 }, 591 }, 592 593 "node_id": { 594 Name: "node_id", 595 AllowMissing: false, 596 Unique: false, 597 Indexer: &memdb.StringFieldIndex{ 598 Field: "NodeID", 599 }, 600 }, 601 }, 602 } 603 } 604 605 // aclPolicyTableSchema returns the MemDB schema for the policy table. 606 // This table is used to store the policies which are referenced by tokens 607 func aclPolicyTableSchema() *memdb.TableSchema { 608 return &memdb.TableSchema{ 609 Name: "acl_policy", 610 Indexes: map[string]*memdb.IndexSchema{ 611 "id": { 612 Name: "id", 613 AllowMissing: false, 614 Unique: true, 615 Indexer: &memdb.StringFieldIndex{ 616 Field: "Name", 617 }, 618 }, 619 }, 620 } 621 } 622 623 // aclTokenTableSchema returns the MemDB schema for the tokens table. 624 // This table is used to store the bearer tokens which are used to authenticate 625 func aclTokenTableSchema() *memdb.TableSchema { 626 return &memdb.TableSchema{ 627 Name: "acl_token", 628 Indexes: map[string]*memdb.IndexSchema{ 629 "id": { 630 Name: "id", 631 AllowMissing: false, 632 Unique: true, 633 Indexer: &memdb.UUIDFieldIndex{ 634 Field: "AccessorID", 635 }, 636 }, 637 "secret": { 638 Name: "secret", 639 AllowMissing: false, 640 Unique: true, 641 Indexer: &memdb.UUIDFieldIndex{ 642 Field: "SecretID", 643 }, 644 }, 645 "global": { 646 Name: "global", 647 AllowMissing: false, 648 Unique: false, 649 Indexer: &memdb.FieldSetIndex{ 650 Field: "Global", 651 }, 652 }, 653 }, 654 } 655 } 656 657 // singletonRecord can be used to describe tables which should contain only 1 entry. 658 // Example uses include storing node config or cluster metadata blobs. 659 var singletonRecord = &memdb.ConditionalIndex{ 660 Conditional: func(interface{}) (bool, error) { return true, nil }, 661 } 662 663 // schedulerConfigTableSchema returns the MemDB schema for the scheduler config table. 664 // This table is used to store configuration options for the scheduler 665 func schedulerConfigTableSchema() *memdb.TableSchema { 666 return &memdb.TableSchema{ 667 Name: "scheduler_config", 668 Indexes: map[string]*memdb.IndexSchema{ 669 "id": { 670 Name: "id", 671 AllowMissing: true, 672 Unique: true, 673 Indexer: singletonRecord, // we store only 1 scheduler config 674 }, 675 }, 676 } 677 } 678 679 // clusterMetaTableSchema returns the MemDB schema for the scheduler config table. 680 func clusterMetaTableSchema() *memdb.TableSchema { 681 return &memdb.TableSchema{ 682 Name: "cluster_meta", 683 Indexes: map[string]*memdb.IndexSchema{ 684 "id": { 685 Name: "id", 686 AllowMissing: false, 687 Unique: true, 688 Indexer: singletonRecord, // we store only 1 cluster metadata 689 }, 690 }, 691 } 692 } 693 694 // CSIVolumes are identified by id globally, and searchable by driver 695 func csiVolumeTableSchema() *memdb.TableSchema { 696 return &memdb.TableSchema{ 697 Name: "csi_volumes", 698 Indexes: map[string]*memdb.IndexSchema{ 699 "id": { 700 Name: "id", 701 AllowMissing: false, 702 Unique: true, 703 Indexer: &memdb.CompoundIndex{ 704 Indexes: []memdb.Indexer{ 705 &memdb.StringFieldIndex{ 706 Field: "Namespace", 707 }, 708 &memdb.StringFieldIndex{ 709 Field: "ID", 710 }, 711 }, 712 }, 713 }, 714 "plugin_id": { 715 Name: "plugin_id", 716 AllowMissing: false, 717 Unique: false, 718 Indexer: &memdb.StringFieldIndex{ 719 Field: "PluginID", 720 }, 721 }, 722 }, 723 } 724 } 725 726 // CSIPlugins are identified by id globally, and searchable by driver 727 func csiPluginTableSchema() *memdb.TableSchema { 728 return &memdb.TableSchema{ 729 Name: "csi_plugins", 730 Indexes: map[string]*memdb.IndexSchema{ 731 "id": { 732 Name: "id", 733 AllowMissing: false, 734 Unique: true, 735 Indexer: &memdb.StringFieldIndex{ 736 Field: "ID", 737 }, 738 }, 739 }, 740 } 741 } 742 743 // StringFieldIndex is used to extract a field from an object 744 // using reflection and builds an index on that field. 745 type ScalingPolicyTargetFieldIndex struct { 746 Field string 747 } 748 749 // FromObject is used to extract an index value from an 750 // object or to indicate that the index value is missing. 751 func (s *ScalingPolicyTargetFieldIndex) FromObject(obj interface{}) (bool, []byte, error) { 752 policy, ok := obj.(*structs.ScalingPolicy) 753 if !ok { 754 return false, nil, fmt.Errorf("object %#v is not a ScalingPolicy", obj) 755 } 756 757 if policy.Target == nil { 758 return false, nil, nil 759 } 760 761 val, ok := policy.Target[s.Field] 762 if !ok { 763 return false, nil, nil 764 } 765 766 // Add the null character as a terminator 767 val += "\x00" 768 return true, []byte(val), nil 769 } 770 771 // FromArgs is used to build an exact index lookup based on arguments 772 func (s *ScalingPolicyTargetFieldIndex) FromArgs(args ...interface{}) ([]byte, error) { 773 if len(args) != 1 { 774 return nil, fmt.Errorf("must provide only a single argument") 775 } 776 arg, ok := args[0].(string) 777 if !ok { 778 return nil, fmt.Errorf("argument must be a string: %#v", args[0]) 779 } 780 // Add the null character as a terminator 781 arg += "\x00" 782 return []byte(arg), nil 783 } 784 785 // PrefixFromArgs returns a prefix that should be used for scanning based on the arguments 786 func (s *ScalingPolicyTargetFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { 787 val, err := s.FromArgs(args...) 788 if err != nil { 789 return nil, err 790 } 791 792 // Strip the null terminator, the rest is a prefix 793 n := len(val) 794 if n > 0 { 795 return val[:n-1], nil 796 } 797 return val, nil 798 } 799 800 // scalingPolicyTableSchema returns the MemDB schema for the policy table. 801 func scalingPolicyTableSchema() *memdb.TableSchema { 802 return &memdb.TableSchema{ 803 Name: "scaling_policy", 804 Indexes: map[string]*memdb.IndexSchema{ 805 // Primary index is used for simple direct lookup. 806 "id": { 807 Name: "id", 808 AllowMissing: false, 809 Unique: true, 810 811 // UUID is uniquely identifying 812 Indexer: &memdb.StringFieldIndex{ 813 Field: "ID", 814 }, 815 }, 816 // Target index is used for listing by namespace or job, or looking up a specific target. 817 // A given task group can have only a single scaling policies, so this is guaranteed to be unique. 818 "target": { 819 Name: "target", 820 AllowMissing: false, 821 Unique: true, 822 823 // Use a compound index so the tuple of (Namespace, Job, Group) is 824 // uniquely identifying 825 Indexer: &memdb.CompoundIndex{ 826 Indexes: []memdb.Indexer{ 827 &ScalingPolicyTargetFieldIndex{ 828 Field: "Namespace", 829 }, 830 831 &ScalingPolicyTargetFieldIndex{ 832 Field: "Job", 833 }, 834 835 &ScalingPolicyTargetFieldIndex{ 836 Field: "Group", 837 }, 838 }, 839 }, 840 }, 841 // Used to filter by enabled 842 "enabled": { 843 Name: "enabled", 844 AllowMissing: false, 845 Unique: false, 846 Indexer: &memdb.FieldSetIndex{ 847 Field: "Enabled", 848 }, 849 }, 850 }, 851 } 852 } 853 854 // scalingEventTableSchema returns the memdb schema for job scaling events 855 func scalingEventTableSchema() *memdb.TableSchema { 856 return &memdb.TableSchema{ 857 Name: "scaling_event", 858 Indexes: map[string]*memdb.IndexSchema{ 859 "id": { 860 Name: "id", 861 AllowMissing: false, 862 Unique: true, 863 864 // Use a compound index so the tuple of (Namespace, JobID) is 865 // uniquely identifying 866 Indexer: &memdb.CompoundIndex{ 867 Indexes: []memdb.Indexer{ 868 &memdb.StringFieldIndex{ 869 Field: "Namespace", 870 }, 871 872 &memdb.StringFieldIndex{ 873 Field: "JobID", 874 }, 875 }, 876 }, 877 }, 878 879 // TODO: need to figure out whether we want to index these or the jobs or ... 880 // "error": { 881 // Name: "error", 882 // AllowMissing: false, 883 // Unique: false, 884 // Indexer: &memdb.FieldSetIndex{ 885 // Field: "Error", 886 // }, 887 // }, 888 }, 889 } 890 }