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 }