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  }