go.temporal.io/server@v1.23.0/common/persistence/cassandra/test.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 cassandra 26 27 import ( 28 "path" 29 "strings" 30 "time" 31 32 "github.com/gocql/gocql" 33 "go.temporal.io/server/common" 34 "go.temporal.io/server/common/backoff" 35 "go.temporal.io/server/common/config" 36 "go.temporal.io/server/common/debug" 37 "go.temporal.io/server/common/dynamicconfig" 38 "go.temporal.io/server/common/log" 39 "go.temporal.io/server/common/log/tag" 40 "go.temporal.io/server/common/metrics" 41 p "go.temporal.io/server/common/persistence" 42 commongocql "go.temporal.io/server/common/persistence/nosql/nosqlplugin/cassandra/gocql" 43 "go.temporal.io/server/common/resolver" 44 "go.temporal.io/server/environment" 45 "go.temporal.io/server/tests/testutils" 46 ) 47 48 const ( 49 testSchemaDir = "schema/cassandra/" 50 ) 51 52 // TestCluster allows executing cassandra operations in testing. 53 type TestCluster struct { 54 keyspace string 55 schemaDir string 56 session commongocql.Session 57 cfg config.Cassandra 58 faultInjection *config.FaultInjection 59 logger log.Logger 60 } 61 62 // NewTestCluster returns a new cassandra test cluster 63 func NewTestCluster(keyspace, username, password, host string, port int, schemaDir string, faultInjection *config.FaultInjection, logger log.Logger) *TestCluster { 64 var result TestCluster 65 result.logger = logger 66 result.keyspace = keyspace 67 if port == 0 { 68 port = environment.GetCassandraPort() 69 } 70 if schemaDir == "" { 71 schemaDir = testSchemaDir 72 } 73 if host == "" { 74 host = environment.GetCassandraAddress() 75 } 76 result.schemaDir = schemaDir 77 result.cfg = config.Cassandra{ 78 User: username, 79 Password: password, 80 Hosts: host, 81 Port: port, 82 MaxConns: 2, 83 ConnectTimeout: 30 * time.Second * debug.TimeoutMultiplier, 84 Keyspace: keyspace, 85 } 86 result.faultInjection = faultInjection 87 return &result 88 } 89 90 // Config returns the persistence config for connecting to this test cluster 91 func (s *TestCluster) Config() config.Persistence { 92 cfg := s.cfg 93 return config.Persistence{ 94 DefaultStore: "test", 95 VisibilityStore: "test", 96 DataStores: map[string]config.DataStore{ 97 "test": {Cassandra: &cfg, FaultInjection: s.faultInjection}, 98 }, 99 TransactionSizeLimit: dynamicconfig.GetIntPropertyFn(common.DefaultTransactionSizeLimit), 100 } 101 } 102 103 // DatabaseName from PersistenceTestCluster interface 104 func (s *TestCluster) DatabaseName() string { 105 return s.keyspace 106 } 107 108 // SetupTestDatabase from PersistenceTestCluster interface 109 func (s *TestCluster) SetupTestDatabase() { 110 s.CreateSession("system") 111 s.CreateDatabase() 112 s.CreateSession(s.DatabaseName()) 113 schemaDir := s.schemaDir + "/" 114 115 if !strings.HasPrefix(schemaDir, "/") && !strings.HasPrefix(schemaDir, "../") { 116 temporalPackageDir := testutils.GetRepoRootDirectory() 117 schemaDir = path.Join(temporalPackageDir, schemaDir) 118 } 119 120 s.LoadSchema(path.Join(schemaDir, "temporal", "schema.cql")) 121 s.LoadSchema(path.Join(schemaDir, "visibility", "schema.cql")) 122 } 123 124 // TearDownTestDatabase from PersistenceTestCluster interface 125 func (s *TestCluster) TearDownTestDatabase() { 126 s.DropDatabase() 127 s.session.Close() 128 } 129 130 // CreateSession from PersistenceTestCluster interface 131 func (s *TestCluster) CreateSession( 132 keyspace string, 133 ) { 134 if s.session != nil { 135 s.session.Close() 136 } 137 138 var err error 139 op := func() error { 140 session, err := commongocql.NewSession( 141 func() (*gocql.ClusterConfig, error) { 142 return commongocql.NewCassandraCluster( 143 config.Cassandra{ 144 Hosts: s.cfg.Hosts, 145 Port: s.cfg.Port, 146 User: s.cfg.User, 147 Password: s.cfg.Password, 148 Keyspace: keyspace, 149 Consistency: &config.CassandraStoreConsistency{ 150 Default: &config.CassandraConsistencySettings{ 151 Consistency: "ONE", 152 }, 153 }, 154 ConnectTimeout: s.cfg.ConnectTimeout, 155 }, 156 resolver.NewNoopResolver(), 157 ) 158 }, 159 log.NewNoopLogger(), 160 metrics.NoopMetricsHandler, 161 ) 162 if err == nil { 163 s.session = session 164 } 165 return err 166 } 167 err = backoff.ThrottleRetry( 168 op, 169 backoff.NewExponentialRetryPolicy(time.Second).WithExpirationInterval(time.Minute), 170 nil, 171 ) 172 if err != nil { 173 s.logger.Fatal("CreateSession", tag.Error(err)) 174 } 175 s.logger.Debug("created session", tag.NewStringTag("keyspace", keyspace)) 176 } 177 178 // CreateDatabase from PersistenceTestCluster interface 179 func (s *TestCluster) CreateDatabase() { 180 err := CreateCassandraKeyspace(s.session, s.DatabaseName(), 1, true, s.logger) 181 if err != nil { 182 s.logger.Fatal("CreateCassandraKeyspace", tag.Error(err)) 183 } 184 s.logger.Info("created database", tag.NewStringTag("database", s.DatabaseName())) 185 } 186 187 // DropDatabase from PersistenceTestCluster interface 188 func (s *TestCluster) DropDatabase() { 189 err := DropCassandraKeyspace(s.session, s.DatabaseName(), s.logger) 190 if err != nil && !strings.Contains(err.Error(), "AlreadyExists") { 191 s.logger.Fatal("DropCassandraKeyspace", tag.Error(err)) 192 } 193 s.logger.Info("dropped database", tag.NewStringTag("database", s.DatabaseName())) 194 } 195 196 // LoadSchema from PersistenceTestCluster interface 197 func (s *TestCluster) LoadSchema(schemaFile string) { 198 statements, err := p.LoadAndSplitQuery([]string{schemaFile}) 199 if err != nil { 200 s.logger.Fatal("LoadSchema", tag.Error(err)) 201 } 202 for _, stmt := range statements { 203 if err = s.session.Query(stmt).Exec(); err != nil { 204 s.logger.Fatal("LoadSchema", tag.Error(err)) 205 } 206 } 207 s.logger.Info("loaded schema") 208 } 209 210 func (s *TestCluster) GetSession() commongocql.Session { 211 return s.session 212 }