github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/nomad/state/schema.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-memdb"
     7  	"github.com/hashicorp/nomad/nomad/structs"
     8  )
     9  
    10  // stateStoreSchema is used to return the schema for the state store
    11  func stateStoreSchema() *memdb.DBSchema {
    12  	// Create the root DB schema
    13  	db := &memdb.DBSchema{
    14  		Tables: make(map[string]*memdb.TableSchema),
    15  	}
    16  
    17  	// Collect all the schemas that are needed
    18  	schemas := []func() *memdb.TableSchema{
    19  		indexTableSchema,
    20  		nodeTableSchema,
    21  		jobTableSchema,
    22  		periodicLaunchTableSchema,
    23  		evalTableSchema,
    24  		allocTableSchema,
    25  	}
    26  
    27  	// Add each of the tables
    28  	for _, schemaFn := range schemas {
    29  		schema := schemaFn()
    30  		if _, ok := db.Tables[schema.Name]; ok {
    31  			panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
    32  		}
    33  		db.Tables[schema.Name] = schema
    34  	}
    35  	return db
    36  }
    37  
    38  // indexTableSchema is used for
    39  func indexTableSchema() *memdb.TableSchema {
    40  	return &memdb.TableSchema{
    41  		Name: "index",
    42  		Indexes: map[string]*memdb.IndexSchema{
    43  			"id": &memdb.IndexSchema{
    44  				Name:         "id",
    45  				AllowMissing: false,
    46  				Unique:       true,
    47  				Indexer: &memdb.StringFieldIndex{
    48  					Field:     "Key",
    49  					Lowercase: true,
    50  				},
    51  			},
    52  		},
    53  	}
    54  }
    55  
    56  // nodeTableSchema returns the MemDB schema for the nodes table.
    57  // This table is used to store all the client nodes that are registered.
    58  func nodeTableSchema() *memdb.TableSchema {
    59  	return &memdb.TableSchema{
    60  		Name: "nodes",
    61  		Indexes: map[string]*memdb.IndexSchema{
    62  			// Primary index is used for node management
    63  			// and simple direct lookup. ID is required to be
    64  			// unique.
    65  			"id": &memdb.IndexSchema{
    66  				Name:         "id",
    67  				AllowMissing: false,
    68  				Unique:       true,
    69  				Indexer: &memdb.UUIDFieldIndex{
    70  					Field: "ID",
    71  				},
    72  			},
    73  		},
    74  	}
    75  }
    76  
    77  // jobTableSchema returns the MemDB schema for the jobs table.
    78  // This table is used to store all the jobs that have been submitted.
    79  func jobTableSchema() *memdb.TableSchema {
    80  	return &memdb.TableSchema{
    81  		Name: "jobs",
    82  		Indexes: map[string]*memdb.IndexSchema{
    83  			// Primary index is used for job management
    84  			// and simple direct lookup. ID is required to be
    85  			// unique.
    86  			"id": &memdb.IndexSchema{
    87  				Name:         "id",
    88  				AllowMissing: false,
    89  				Unique:       true,
    90  				Indexer: &memdb.StringFieldIndex{
    91  					Field:     "ID",
    92  					Lowercase: true,
    93  				},
    94  			},
    95  			"type": &memdb.IndexSchema{
    96  				Name:         "type",
    97  				AllowMissing: false,
    98  				Unique:       false,
    99  				Indexer: &memdb.StringFieldIndex{
   100  					Field:     "Type",
   101  					Lowercase: false,
   102  				},
   103  			},
   104  			"gc": &memdb.IndexSchema{
   105  				Name:         "gc",
   106  				AllowMissing: false,
   107  				Unique:       false,
   108  				Indexer: &memdb.ConditionalIndex{
   109  					Conditional: jobIsGCable,
   110  				},
   111  			},
   112  			"periodic": &memdb.IndexSchema{
   113  				Name:         "periodic",
   114  				AllowMissing: false,
   115  				Unique:       false,
   116  				Indexer: &memdb.ConditionalIndex{
   117  					Conditional: jobIsPeriodic,
   118  				},
   119  			},
   120  		},
   121  	}
   122  }
   123  
   124  // jobIsGCable satisfies the ConditionalIndexFunc interface and creates an index
   125  // on whether a job is eligible for garbage collection.
   126  func jobIsGCable(obj interface{}) (bool, error) {
   127  	j, ok := obj.(*structs.Job)
   128  	if !ok {
   129  		return false, fmt.Errorf("Unexpected type: %v", obj)
   130  	}
   131  
   132  	// The job is GCable if it is batch and it is not periodic
   133  	periodic := j.Periodic != nil && j.Periodic.Enabled
   134  	gcable := j.Type == structs.JobTypeBatch && !periodic
   135  	return gcable, nil
   136  }
   137  
   138  // jobIsPeriodic satisfies the ConditionalIndexFunc interface and creates an index
   139  // on whether a job is periodic.
   140  func jobIsPeriodic(obj interface{}) (bool, error) {
   141  	j, ok := obj.(*structs.Job)
   142  	if !ok {
   143  		return false, fmt.Errorf("Unexpected type: %v", obj)
   144  	}
   145  
   146  	if j.Periodic != nil && j.Periodic.Enabled == true {
   147  		return true, nil
   148  	}
   149  
   150  	return false, nil
   151  }
   152  
   153  // periodicLaunchTableSchema returns the MemDB schema tracking the most recent
   154  // launch time for a perioidic job.
   155  func periodicLaunchTableSchema() *memdb.TableSchema {
   156  	return &memdb.TableSchema{
   157  		Name: "periodic_launch",
   158  		Indexes: map[string]*memdb.IndexSchema{
   159  			// Primary index is used for job management
   160  			// and simple direct lookup. ID is required to be
   161  			// unique.
   162  			"id": &memdb.IndexSchema{
   163  				Name:         "id",
   164  				AllowMissing: false,
   165  				Unique:       true,
   166  				Indexer: &memdb.StringFieldIndex{
   167  					Field:     "ID",
   168  					Lowercase: true,
   169  				},
   170  			},
   171  		},
   172  	}
   173  }
   174  
   175  // evalTableSchema returns the MemDB schema for the eval table.
   176  // This table is used to store all the evaluations that are pending
   177  // or recently completed.
   178  func evalTableSchema() *memdb.TableSchema {
   179  	return &memdb.TableSchema{
   180  		Name: "evals",
   181  		Indexes: map[string]*memdb.IndexSchema{
   182  			// Primary index is used for direct lookup.
   183  			"id": &memdb.IndexSchema{
   184  				Name:         "id",
   185  				AllowMissing: false,
   186  				Unique:       true,
   187  				Indexer: &memdb.UUIDFieldIndex{
   188  					Field: "ID",
   189  				},
   190  			},
   191  
   192  			// Job index is used to lookup allocations by job
   193  			"job": &memdb.IndexSchema{
   194  				Name:         "job",
   195  				AllowMissing: false,
   196  				Unique:       false,
   197  				Indexer: &memdb.StringFieldIndex{
   198  					Field:     "JobID",
   199  					Lowercase: true,
   200  				},
   201  			},
   202  		},
   203  	}
   204  }
   205  
   206  // allocTableSchema returns the MemDB schema for the allocation table.
   207  // This table is used to store all the task allocations between task groups
   208  // and nodes.
   209  func allocTableSchema() *memdb.TableSchema {
   210  	return &memdb.TableSchema{
   211  		Name: "allocs",
   212  		Indexes: map[string]*memdb.IndexSchema{
   213  			// Primary index is a UUID
   214  			"id": &memdb.IndexSchema{
   215  				Name:         "id",
   216  				AllowMissing: false,
   217  				Unique:       true,
   218  				Indexer: &memdb.UUIDFieldIndex{
   219  					Field: "ID",
   220  				},
   221  			},
   222  
   223  			// Node index is used to lookup allocations by node
   224  			"node": &memdb.IndexSchema{
   225  				Name:         "node",
   226  				AllowMissing: true, // Missing is allow for failed allocations
   227  				Unique:       false,
   228  				Indexer: &memdb.CompoundIndex{
   229  					Indexes: []memdb.Indexer{
   230  						&memdb.StringFieldIndex{
   231  							Field:     "NodeID",
   232  							Lowercase: true,
   233  						},
   234  
   235  						// Conditional indexer on if allocation is terminal
   236  						&memdb.ConditionalIndex{
   237  							Conditional: func(obj interface{}) (bool, error) {
   238  								// Cast to allocation
   239  								alloc, ok := obj.(*structs.Allocation)
   240  								if !ok {
   241  									return false, fmt.Errorf("wrong type, got %t should be Allocation", obj)
   242  								}
   243  
   244  								// Check if the allocation is terminal
   245  								return alloc.TerminalStatus(), nil
   246  							},
   247  						},
   248  					},
   249  				},
   250  			},
   251  
   252  			// Job index is used to lookup allocations by job
   253  			"job": &memdb.IndexSchema{
   254  				Name:         "job",
   255  				AllowMissing: false,
   256  				Unique:       false,
   257  				Indexer: &memdb.StringFieldIndex{
   258  					Field:     "JobID",
   259  					Lowercase: true,
   260  				},
   261  			},
   262  
   263  			// Eval index is used to lookup allocations by eval
   264  			"eval": &memdb.IndexSchema{
   265  				Name:         "eval",
   266  				AllowMissing: false,
   267  				Unique:       false,
   268  				Indexer: &memdb.UUIDFieldIndex{
   269  					Field: "EvalID",
   270  				},
   271  			},
   272  		},
   273  	}
   274  }