github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/sqlstore/supplier_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore_test 5 6 import ( 7 "regexp" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/mattermost/gorp" 13 _ "github.com/mattn/go-sqlite3" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 "github.com/mattermost/mattermost-server/v5/model" 18 "github.com/mattermost/mattermost-server/v5/store/sqlstore" 19 "github.com/mattermost/mattermost-server/v5/store/storetest" 20 ) 21 22 func TestGetReplica(t *testing.T) { 23 t.Parallel() 24 testCases := []struct { 25 Description string 26 DataSourceReplicas []string 27 DataSourceSearchReplicas []string 28 }{ 29 { 30 "no replicas", 31 []string{}, 32 []string{}, 33 }, 34 { 35 "one source replica", 36 []string{":memory:"}, 37 []string{}, 38 }, 39 { 40 "multiple source replicas", 41 []string{":memory:", ":memory:", ":memory:"}, 42 []string{}, 43 }, 44 { 45 "one source search replica", 46 []string{}, 47 []string{":memory:"}, 48 }, 49 { 50 "multiple source search replicas", 51 []string{}, 52 []string{":memory:", ":memory:", ":memory:"}, 53 }, 54 { 55 "one source replica, one source search replica", 56 []string{":memory:"}, 57 []string{":memory:"}, 58 }, 59 { 60 "one source replica, multiple source search replicas", 61 []string{":memory:"}, 62 []string{":memory:", ":memory:", ":memory:"}, 63 }, 64 { 65 "multiple source replica, one source search replica", 66 []string{":memory:", ":memory:", ":memory:"}, 67 []string{":memory:"}, 68 }, 69 { 70 "multiple source replica, multiple source search replicas", 71 []string{":memory:", ":memory:", ":memory:"}, 72 []string{":memory:", ":memory:", ":memory:"}, 73 }, 74 } 75 76 for _, testCase := range testCases { 77 testCase := testCase 78 t.Run(testCase.Description+" with license", func(t *testing.T) { 79 t.Parallel() 80 81 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 82 settings.DataSourceReplicas = testCase.DataSourceReplicas 83 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 84 supplier := sqlstore.NewSqlSupplier(*settings, nil) 85 supplier.UpdateLicense(&model.License{}) 86 87 replicas := make(map[*gorp.DbMap]bool) 88 for i := 0; i < 5; i++ { 89 replicas[supplier.GetReplica()] = true 90 } 91 92 searchReplicas := make(map[*gorp.DbMap]bool) 93 for i := 0; i < 5; i++ { 94 searchReplicas[supplier.GetSearchReplica()] = true 95 } 96 97 if len(testCase.DataSourceReplicas) > 0 { 98 // If replicas were defined, ensure none are the master. 99 assert.Len(t, replicas, len(testCase.DataSourceReplicas)) 100 101 for replica := range replicas { 102 assert.NotEqual(t, supplier.GetMaster(), replica) 103 } 104 105 } else if assert.Len(t, replicas, 1) { 106 // Otherwise ensure the replicas contains only the master. 107 for replica := range replicas { 108 assert.Equal(t, supplier.GetMaster(), replica) 109 } 110 } 111 112 if len(testCase.DataSourceSearchReplicas) > 0 { 113 // If search replicas were defined, ensure none are the master nor the replicas. 114 assert.Len(t, searchReplicas, len(testCase.DataSourceSearchReplicas)) 115 116 for searchReplica := range searchReplicas { 117 assert.NotEqual(t, supplier.GetMaster(), searchReplica) 118 for replica := range replicas { 119 assert.NotEqual(t, searchReplica, replica) 120 } 121 } 122 123 } else if len(testCase.DataSourceReplicas) > 0 { 124 // If no search replicas were defined, but replicas were, ensure they are equal. 125 assert.Equal(t, replicas, searchReplicas) 126 127 } else if assert.Len(t, searchReplicas, 1) { 128 // Otherwise ensure the search replicas contains the master. 129 for searchReplica := range searchReplicas { 130 assert.Equal(t, supplier.GetMaster(), searchReplica) 131 } 132 } 133 }) 134 135 t.Run(testCase.Description+" without license", func(t *testing.T) { 136 t.Parallel() 137 138 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 139 settings.DataSourceReplicas = testCase.DataSourceReplicas 140 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 141 supplier := sqlstore.NewSqlSupplier(*settings, nil) 142 143 replicas := make(map[*gorp.DbMap]bool) 144 for i := 0; i < 5; i++ { 145 replicas[supplier.GetReplica()] = true 146 } 147 148 searchReplicas := make(map[*gorp.DbMap]bool) 149 for i := 0; i < 5; i++ { 150 searchReplicas[supplier.GetSearchReplica()] = true 151 } 152 153 if len(testCase.DataSourceReplicas) > 0 { 154 // If replicas were defined, ensure none are the master. 155 assert.Len(t, replicas, 1) 156 157 for replica := range replicas { 158 assert.Same(t, supplier.GetMaster(), replica) 159 } 160 161 } else if assert.Len(t, replicas, 1) { 162 // Otherwise ensure the replicas contains only the master. 163 for replica := range replicas { 164 assert.Equal(t, supplier.GetMaster(), replica) 165 } 166 } 167 168 if len(testCase.DataSourceSearchReplicas) > 0 { 169 // If search replicas were defined, ensure none are the master nor the replicas. 170 assert.Len(t, searchReplicas, 1) 171 172 for searchReplica := range searchReplicas { 173 assert.Same(t, supplier.GetMaster(), searchReplica) 174 } 175 176 } else if len(testCase.DataSourceReplicas) > 0 { 177 // If no search replicas were defined, but replicas were, ensure they are equal. 178 assert.Equal(t, replicas, searchReplicas) 179 180 } else if assert.Len(t, searchReplicas, 1) { 181 // Otherwise ensure the search replicas contains the master. 182 for searchReplica := range searchReplicas { 183 assert.Equal(t, supplier.GetMaster(), searchReplica) 184 } 185 } 186 }) 187 } 188 } 189 190 func TestGetDbVersion(t *testing.T) { 191 testDrivers := []string{ 192 model.DATABASE_DRIVER_POSTGRES, 193 model.DATABASE_DRIVER_MYSQL, 194 model.DATABASE_DRIVER_SQLITE, 195 } 196 197 for _, driver := range testDrivers { 198 t.Run("Should return db version for "+driver, func(t *testing.T) { 199 t.Parallel() 200 settings := makeSqlSettings(driver) 201 supplier := sqlstore.NewSqlSupplier(*settings, nil) 202 203 version, err := supplier.GetDbVersion() 204 require.Nil(t, err) 205 require.Regexp(t, regexp.MustCompile(`\d+\.\d+(\.\d+)?`), version) 206 }) 207 } 208 } 209 210 func TestRecycleDBConns(t *testing.T) { 211 if testing.Short() { 212 t.Skip("skipping recycle DBConns test") 213 } 214 testDrivers := []string{ 215 model.DATABASE_DRIVER_POSTGRES, 216 model.DATABASE_DRIVER_MYSQL, 217 model.DATABASE_DRIVER_SQLITE, 218 } 219 220 for _, driver := range testDrivers { 221 t.Run(driver, func(t *testing.T) { 222 settings := makeSqlSettings(driver) 223 supplier := sqlstore.NewSqlSupplier(*settings, nil) 224 225 var wg sync.WaitGroup 226 tables := []string{"Posts", "Channels", "Users"} 227 for _, table := range tables { 228 wg.Add(1) 229 go func(table string) { 230 defer wg.Done() 231 query := `SELECT count(*) FROM ` + table 232 _, err := supplier.GetMaster().SelectInt(query) 233 assert.NoError(t, err) 234 }(table) 235 } 236 wg.Wait() 237 238 stats := supplier.GetMaster().Db.Stats() 239 assert.Equal(t, 0, int(stats.MaxLifetimeClosed), "unexpected number of connections closed due to maxlifetime") 240 241 supplier.RecycleDBConnections(2 * time.Second) 242 // We cannot reliably control exactly how many open connections are there. So we 243 // just do a basic check and confirm that atleast one has been closed. 244 stats = supplier.GetMaster().Db.Stats() 245 assert.Greater(t, int(stats.MaxLifetimeClosed), 0, "unexpected number of connections closed due to maxlifetime") 246 }) 247 } 248 } 249 250 func TestGetAllConns(t *testing.T) { 251 t.Parallel() 252 testCases := []struct { 253 Description string 254 DataSourceReplicas []string 255 DataSourceSearchReplicas []string 256 ExpectedNumConnections int 257 }{ 258 { 259 "no replicas", 260 []string{}, 261 []string{}, 262 1, 263 }, 264 { 265 "one source replica", 266 []string{":memory:"}, 267 []string{}, 268 2, 269 }, 270 { 271 "multiple source replicas", 272 []string{":memory:", ":memory:", ":memory:"}, 273 []string{}, 274 4, 275 }, 276 { 277 "one source search replica", 278 []string{}, 279 []string{":memory:"}, 280 1, 281 }, 282 { 283 "multiple source search replicas", 284 []string{}, 285 []string{":memory:", ":memory:", ":memory:"}, 286 1, 287 }, 288 { 289 "one source replica, one source search replica", 290 []string{":memory:"}, 291 []string{":memory:"}, 292 2, 293 }, 294 { 295 "one source replica, multiple source search replicas", 296 []string{":memory:"}, 297 []string{":memory:", ":memory:", ":memory:"}, 298 2, 299 }, 300 { 301 "multiple source replica, one source search replica", 302 []string{":memory:", ":memory:", ":memory:"}, 303 []string{":memory:"}, 304 4, 305 }, 306 { 307 "multiple source replica, multiple source search replicas", 308 []string{":memory:", ":memory:", ":memory:"}, 309 []string{":memory:", ":memory:", ":memory:"}, 310 4, 311 }, 312 } 313 314 for _, testCase := range testCases { 315 testCase := testCase 316 t.Run(testCase.Description, func(t *testing.T) { 317 t.Parallel() 318 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 319 settings.DataSourceReplicas = testCase.DataSourceReplicas 320 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 321 supplier := sqlstore.NewSqlSupplier(*settings, nil) 322 323 assert.Len(t, supplier.GetAllConns(), testCase.ExpectedNumConnections) 324 }) 325 } 326 } 327 328 func makeSqlSettings(driver string) *model.SqlSettings { 329 switch driver { 330 case model.DATABASE_DRIVER_POSTGRES: 331 return storetest.MakeSqlSettings(driver) 332 case model.DATABASE_DRIVER_MYSQL: 333 return storetest.MakeSqlSettings(driver) 334 case model.DATABASE_DRIVER_SQLITE: 335 return makeSqliteSettings() 336 } 337 338 return nil 339 } 340 341 func makeSqliteSettings() *model.SqlSettings { 342 driverName := model.DATABASE_DRIVER_SQLITE 343 dataSource := ":memory:" 344 maxIdleConns := 1 345 connMaxLifetimeMilliseconds := 3600000 346 maxOpenConns := 1 347 queryTimeout := 5 348 349 return &model.SqlSettings{ 350 DriverName: &driverName, 351 DataSource: &dataSource, 352 MaxIdleConns: &maxIdleConns, 353 ConnMaxLifetimeMilliseconds: &connMaxLifetimeMilliseconds, 354 MaxOpenConns: &maxOpenConns, 355 QueryTimeout: &queryTimeout, 356 } 357 }