go.temporal.io/server@v1.23.0/common/config/persistence.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package config 26 27 import ( 28 "errors" 29 "fmt" 30 "reflect" 31 "strings" 32 33 "github.com/gocql/gocql" 34 "go.temporal.io/server/common/persistence/visibility/store/elasticsearch/client" 35 ) 36 37 const ( 38 // StoreTypeSQL refers to sql based storage as persistence store 39 StoreTypeSQL = "sql" 40 // StoreTypeNoSQL refers to nosql based storage as persistence store 41 StoreTypeNoSQL = "nosql" 42 ) 43 44 // DefaultStoreType returns the storeType for the default persistence store 45 func (c *Persistence) DefaultStoreType() string { 46 if c.DataStores[c.DefaultStore].SQL != nil { 47 return StoreTypeSQL 48 } 49 return StoreTypeNoSQL 50 } 51 52 // Validate validates the persistence config 53 func (c *Persistence) Validate() error { 54 stores := []string{c.DefaultStore} 55 if c.VisibilityStore != "" { 56 stores = append(stores, c.VisibilityStore) 57 } 58 if c.SecondaryVisibilityStore != "" { 59 stores = append(stores, c.SecondaryVisibilityStore) 60 } 61 if c.AdvancedVisibilityStore != "" { 62 stores = append(stores, c.AdvancedVisibilityStore) 63 } 64 65 // There are 3 config keys: 66 // - visibilityStore: can set any data store 67 // - secondaryVisibilityStore: can set any data store 68 // - advancedVisibilityStore: can only set elasticsearch data store 69 // If visibilityStore is set, then it's always the primary. 70 // If secondaryVisibilityStore is set, it's always the secondary. 71 // 72 // Valid dual visibility combinations (order: primary, secondary): 73 // - visibilityStore (standard), secondaryVisibilityStore (any) 74 // - visibilityStore (standard), advancedVisibilityStore (es) 75 // - visibilityStore (advanced sql), secondaryVisibilityStore (advanced sql) 76 // - visibilityStore (es), visibilityStore (es) [via elasticsearch.indices config] 77 // - advancedVisibilityStore (es), advancedVisibilityStore (es) [via elasticsearch.indices config] 78 // 79 // Invalid dual visibility combinations: 80 // - visibilityStore (advanced sql), secondaryVisibilityStore (standard, es) 81 // - visibilityStore (advanced sql), advancedVisibilityStore (es) 82 // - visibilityStore (es), secondaryVisibilityStore (any) 83 // - visibilityStore (es), advancedVisibilityStore (es) 84 // - advancedVisibilityStore (es), secondaryVisibilityStore (any) 85 // 86 // The validation for dual visibility pair (advanced sql, advanced sql) is in visibility factory 87 // due to circular dependency. This will be better after standard visibility is removed. 88 89 if c.VisibilityStore == "" && c.AdvancedVisibilityStore == "" { 90 return errors.New("persistence config: visibilityStore must be specified") 91 } 92 if c.SecondaryVisibilityStore != "" && c.AdvancedVisibilityStore != "" { 93 return errors.New( 94 "persistence config: cannot specify both secondaryVisibilityStore and " + 95 "advancedVisibilityStore", 96 ) 97 } 98 if c.AdvancedVisibilityStore != "" && c.DataStores[c.AdvancedVisibilityStore].Elasticsearch == nil { 99 return fmt.Errorf( 100 "persistence config: advanced visibility datastore %q: missing elasticsearch config", 101 c.AdvancedVisibilityStore, 102 ) 103 } 104 if c.DataStores[c.VisibilityStore].Elasticsearch != nil && 105 (c.SecondaryVisibilityStore != "" || c.AdvancedVisibilityStore != "") { 106 return errors.New( 107 "persistence config: cannot set secondaryVisibilityStore or advancedVisibilityStore " + 108 "when visibilityStore is setting elasticsearch datastore", 109 ) 110 } 111 if c.DataStores[c.SecondaryVisibilityStore].Elasticsearch.GetSecondaryVisibilityIndex() != "" { 112 return fmt.Errorf( 113 "persistence config: secondary visibility datastore %q: elasticsearch config: "+ 114 "cannot set secondary_visibility", 115 c.SecondaryVisibilityStore, 116 ) 117 } 118 119 cntEsConfigs := 0 120 for _, st := range stores { 121 ds, ok := c.DataStores[st] 122 if !ok { 123 return fmt.Errorf("persistence config: missing config for datastore %q", st) 124 } 125 if err := ds.Validate(); err != nil { 126 return fmt.Errorf("persistence config: datastore %q: %s", st, err.Error()) 127 } 128 if ds.Elasticsearch != nil { 129 cntEsConfigs++ 130 } 131 } 132 133 if cntEsConfigs > 1 { 134 return fmt.Errorf( 135 "persistence config: cannot have more than one Elasticsearch visibility store config " + 136 "(use `elasticsearch.indices.secondary_visibility` config key if you need to set a " + 137 "secondary Elasticsearch visibility store)", 138 ) 139 } 140 141 return nil 142 } 143 144 // StandardVisibilityConfigExist returns whether user specified visibilityStore in config 145 func (c *Persistence) StandardVisibilityConfigExist() bool { 146 return c.VisibilityStore != "" 147 } 148 149 // SecondaryVisibilityConfigExist returns whether user specified secondaryVisibilityStore in config 150 func (c *Persistence) SecondaryVisibilityConfigExist() bool { 151 return c.SecondaryVisibilityStore != "" 152 } 153 154 // AdvancedVisibilityConfigExist returns whether user specified advancedVisibilityStore in config 155 func (c *Persistence) AdvancedVisibilityConfigExist() bool { 156 return c.AdvancedVisibilityStore != "" 157 } 158 159 func (c *Persistence) IsSQLVisibilityStore() bool { 160 return (c.StandardVisibilityConfigExist() && c.DataStores[c.VisibilityStore].SQL != nil) || 161 (c.SecondaryVisibilityConfigExist() && c.DataStores[c.SecondaryVisibilityStore].SQL != nil) 162 } 163 164 func (c *Persistence) GetVisibilityStoreConfig() DataStore { 165 if c.VisibilityStore != "" { 166 return c.DataStores[c.VisibilityStore] 167 } 168 if c.AdvancedVisibilityStore != "" { 169 return c.DataStores[c.AdvancedVisibilityStore] 170 } 171 // Based on validation above, this should never happen. 172 return DataStore{} 173 } 174 175 func (c *Persistence) GetSecondaryVisibilityStoreConfig() DataStore { 176 if c.SecondaryVisibilityStore != "" { 177 return c.DataStores[c.SecondaryVisibilityStore] 178 } 179 if c.VisibilityStore != "" { 180 if c.AdvancedVisibilityStore != "" { 181 return c.DataStores[c.AdvancedVisibilityStore] 182 } 183 ds := c.DataStores[c.VisibilityStore] 184 if ds.Elasticsearch != nil && ds.Elasticsearch.GetSecondaryVisibilityIndex() != "" { 185 esConfig := *ds.Elasticsearch 186 esConfig.Indices = map[string]string{ 187 client.VisibilityAppName: ds.Elasticsearch.GetSecondaryVisibilityIndex(), 188 } 189 ds.Elasticsearch = &esConfig 190 return ds 191 } 192 } 193 if c.AdvancedVisibilityStore != "" { 194 ds := c.DataStores[c.AdvancedVisibilityStore] 195 if ds.Elasticsearch != nil && ds.Elasticsearch.GetSecondaryVisibilityIndex() != "" { 196 esConfig := *ds.Elasticsearch 197 esConfig.Indices = map[string]string{ 198 client.VisibilityAppName: ds.Elasticsearch.GetSecondaryVisibilityIndex(), 199 } 200 ds.Elasticsearch = &esConfig 201 return ds 202 } 203 } 204 return DataStore{} 205 } 206 207 func (ds *DataStore) GetIndexName() string { 208 switch { 209 case ds.SQL != nil: 210 return ds.SQL.DatabaseName 211 case ds.Cassandra != nil: 212 return ds.Cassandra.Keyspace 213 case ds.Elasticsearch != nil: 214 return ds.Elasticsearch.GetVisibilityIndex() 215 default: 216 return "" 217 } 218 } 219 220 // Validate validates the data store config 221 func (ds *DataStore) Validate() error { 222 storeConfigCount := 0 223 if ds.SQL != nil { 224 storeConfigCount++ 225 } 226 if ds.Cassandra != nil { 227 storeConfigCount++ 228 } 229 if ds.CustomDataStoreConfig != nil { 230 storeConfigCount++ 231 } 232 if ds.Elasticsearch != nil { 233 storeConfigCount++ 234 } 235 if storeConfigCount != 1 { 236 return errors.New( 237 "must provide config for one and only one datastore: " + 238 "elasticsearch, cassandra, sql or custom store", 239 ) 240 } 241 242 if ds.SQL != nil && ds.SQL.TaskScanPartitions == 0 { 243 ds.SQL.TaskScanPartitions = 1 244 } 245 if ds.Cassandra != nil { 246 if err := ds.Cassandra.validate(); err != nil { 247 return err 248 } 249 } 250 if ds.Elasticsearch != nil { 251 if err := ds.Elasticsearch.Validate(); err != nil { 252 return err 253 } 254 } 255 return nil 256 } 257 258 // GetConsistency returns the gosql.Consistency setting from the configuration for the given store type 259 func (c *CassandraStoreConsistency) GetConsistency() gocql.Consistency { 260 return gocql.ParseConsistency(c.getConsistencySettings().Consistency) 261 } 262 263 // GetSerialConsistency returns the gosql.SerialConsistency setting from the configuration for the store 264 func (c *CassandraStoreConsistency) GetSerialConsistency() gocql.SerialConsistency { 265 res, err := parseSerialConsistency(c.getConsistencySettings().SerialConsistency) 266 if err != nil { 267 panic(fmt.Sprintf("unable to decode cassandra serial consistency: %v", err)) 268 } 269 return res 270 } 271 272 func (c *CassandraStoreConsistency) getConsistencySettings() *CassandraConsistencySettings { 273 return ensureStoreConsistencyNotNil(c).Default 274 } 275 276 func ensureStoreConsistencyNotNil(c *CassandraStoreConsistency) *CassandraStoreConsistency { 277 if c == nil { 278 c = &CassandraStoreConsistency{} 279 } 280 if c.Default == nil { 281 c.Default = &CassandraConsistencySettings{} 282 } 283 if c.Default.Consistency == "" { 284 c.Default.Consistency = "LOCAL_QUORUM" 285 } 286 if c.Default.SerialConsistency == "" { 287 c.Default.SerialConsistency = "LOCAL_SERIAL" 288 } 289 290 return c 291 } 292 293 func (c *Cassandra) validate() error { 294 return c.Consistency.validate() 295 } 296 297 func (c *CassandraStoreConsistency) validate() error { 298 if c == nil { 299 return nil 300 } 301 302 v := reflect.ValueOf(*c) 303 304 for i := 0; i < v.NumField(); i++ { 305 s, ok := v.Field(i).Interface().(*CassandraConsistencySettings) 306 if ok { 307 if err := s.validate(); err != nil { 308 return err 309 } 310 } 311 } 312 313 return nil 314 } 315 316 func (c *CassandraConsistencySettings) validate() error { 317 if c == nil { 318 return nil 319 } 320 321 if c.Consistency != "" { 322 _, err := gocql.ParseConsistencyWrapper(c.Consistency) 323 if err != nil { 324 return fmt.Errorf("bad cassandra consistency: %v", err) 325 } 326 } 327 328 if c.SerialConsistency != "" { 329 _, err := parseSerialConsistency(c.SerialConsistency) 330 if err != nil { 331 return fmt.Errorf("bad cassandra serial consistency: %v", err) 332 } 333 } 334 335 return nil 336 } 337 338 func parseSerialConsistency(serialConsistency string) (gocql.SerialConsistency, error) { 339 var s gocql.SerialConsistency 340 err := s.UnmarshalText([]byte(strings.ToUpper(serialConsistency))) 341 return s, err 342 }