vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/connpool/pool_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package connpool 18 19 import ( 20 "context" 21 "runtime" 22 "sync" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 29 "vitess.io/vitess/go/mysql/fakesqldb" 30 "vitess.io/vitess/go/pools" 31 "vitess.io/vitess/go/sqltypes" 32 "vitess.io/vitess/go/vt/callerid" 33 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 34 ) 35 36 func TestConnPoolGet(t *testing.T) { 37 db := fakesqldb.New(t) 38 defer db.Close() 39 connPool := newPool() 40 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 41 defer connPool.Close() 42 dbConn, err := connPool.Get(context.Background(), nil) 43 if err != nil { 44 t.Fatalf("should not get an error, but got: %v", err) 45 } 46 if dbConn == nil { 47 t.Fatalf("db conn should not be nil") 48 } 49 // There is no context, it should not use appdebug connection 50 if dbConn.pool == nil { 51 t.Fatalf("db conn pool should not be nil") 52 } 53 dbConn.Recycle() 54 } 55 56 func TestConnPoolTimeout(t *testing.T) { 57 db := fakesqldb.New(t) 58 defer db.Close() 59 connPool := NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ 60 Size: 1, 61 TimeoutSeconds: 1, 62 IdleTimeoutSeconds: 10, 63 }) 64 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 65 defer connPool.Close() 66 dbConn, err := connPool.Get(context.Background(), nil) 67 require.NoError(t, err) 68 defer dbConn.Recycle() 69 _, err = connPool.Get(context.Background(), nil) 70 assert.EqualError(t, err, "resource pool timed out") 71 } 72 73 func TestConnPoolMaxWaiters(t *testing.T) { 74 db := fakesqldb.New(t) 75 defer db.Close() 76 connPool := NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ 77 Size: 1, 78 MaxWaiters: 1, 79 }) 80 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 81 defer connPool.Close() 82 dbConn, err := connPool.Get(context.Background(), nil) 83 require.NoError(t, err) 84 85 // waiter 1 86 var wg sync.WaitGroup 87 wg.Add(1) 88 go func() { 89 defer wg.Done() 90 c1, err := connPool.Get(context.Background(), nil) 91 if err != nil { 92 t.Errorf("unexpected error: %v", err) 93 return 94 } 95 c1.Recycle() 96 }() 97 // Wait for the first waiter to increment count. 98 for { 99 runtime.Gosched() 100 if connPool.waiterCount.Get() == 1 { 101 break 102 } 103 } 104 105 // waiter 2 106 _, err = connPool.Get(context.Background(), nil) 107 assert.EqualError(t, err, "pool TestPool waiter count exceeded") 108 109 // This recycle will make waiter1 succeed. 110 dbConn.Recycle() 111 wg.Wait() 112 } 113 114 func TestConnPoolGetEmptyDebugConfig(t *testing.T) { 115 db := fakesqldb.New(t) 116 debugConn := db.ConnParamsWithUname("") 117 defer db.Close() 118 connPool := newPool() 119 connPool.Open(db.ConnParams(), db.ConnParams(), debugConn) 120 im := callerid.NewImmediateCallerID("") 121 ecid := callerid.NewEffectiveCallerID("p", "c", "sc") 122 ctx := context.Background() 123 ctx = callerid.NewContext(ctx, ecid, im) 124 defer connPool.Close() 125 dbConn, err := connPool.Get(ctx, nil) 126 if err != nil { 127 t.Fatalf("should not get an error, but got: %v", err) 128 } 129 if dbConn == nil { 130 t.Fatalf("db conn should not be nil") 131 } 132 // Context is empty, it should not use appdebug connection 133 if dbConn.pool == nil { 134 t.Fatalf("db conn pool should not be nil") 135 } 136 dbConn.Recycle() 137 } 138 139 func TestConnPoolGetAppDebug(t *testing.T) { 140 db := fakesqldb.New(t) 141 debugConn := db.ConnParamsWithUname("debugUsername") 142 ctx := context.Background() 143 im := callerid.NewImmediateCallerID("debugUsername") 144 ecid := callerid.NewEffectiveCallerID("p", "c", "sc") 145 ctx = callerid.NewContext(ctx, ecid, im) 146 defer db.Close() 147 connPool := newPool() 148 connPool.Open(db.ConnParams(), db.ConnParams(), debugConn) 149 defer connPool.Close() 150 dbConn, err := connPool.Get(ctx, nil) 151 if err != nil { 152 t.Fatalf("should not get an error, but got: %v", err) 153 } 154 if dbConn == nil { 155 t.Fatalf("db conn should not be nil") 156 } 157 if dbConn.pool != nil { 158 t.Fatalf("db conn pool should be nil for appDebug") 159 } 160 dbConn.Recycle() 161 if !dbConn.IsClosed() { 162 t.Fatalf("db conn should be closed after recycle") 163 } 164 } 165 166 func TestConnPoolPutWhilePoolIsClosed(t *testing.T) { 167 connPool := newPool() 168 defer func() { 169 if recover() == nil { 170 t.Fatalf("pool is closed, should get an error") 171 } 172 }() 173 connPool.Put(nil) 174 } 175 176 func TestConnPoolSetCapacity(t *testing.T) { 177 db := fakesqldb.New(t) 178 defer db.Close() 179 connPool := newPool() 180 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 181 defer connPool.Close() 182 err := connPool.SetCapacity(-10) 183 if err == nil { 184 t.Fatalf("set capacity should return error for negative capacity") 185 } 186 err = connPool.SetCapacity(10) 187 if err != nil { 188 t.Fatalf("set capacity should succeed") 189 } 190 if connPool.Capacity() != 10 { 191 t.Fatalf("capacity should be 10") 192 } 193 } 194 195 func TestConnPoolStatJSON(t *testing.T) { 196 db := fakesqldb.New(t) 197 defer db.Close() 198 connPool := newPool() 199 if connPool.StatsJSON() != "{}" { 200 t.Fatalf("pool is closed, stats json should be empty: {}") 201 } 202 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 203 defer connPool.Close() 204 statsJSON := connPool.StatsJSON() 205 if statsJSON == "" || statsJSON == "{}" { 206 t.Fatalf("stats json should not be empty") 207 } 208 } 209 210 func TestConnPoolStateWhilePoolIsClosed(t *testing.T) { 211 connPool := newPool() 212 assert.EqualValues(t, 0, connPool.Capacity(), "pool capacity should be 0 because it is still closed") 213 assert.EqualValues(t, 0, connPool.Available(), "pool available connections should be 0 because it is still closed") 214 assert.EqualValues(t, 0, connPool.MaxCap(), "pool max capacity should be 0 because it is still closed") 215 assert.EqualValues(t, 0, connPool.WaitCount(), "pool wait count should be 0 because it is still closed") 216 assert.EqualValues(t, 0, connPool.WaitTime(), "pool wait time should be 0 because it is still closed") 217 assert.EqualValues(t, 0, connPool.IdleTimeout(), "pool idle timeout should be 0 because it is still closed") 218 } 219 220 func TestConnPoolStateWhilePoolIsOpen(t *testing.T) { 221 db := fakesqldb.New(t) 222 defer db.Close() 223 idleTimeout := 10 * time.Second 224 connPool := newPool() 225 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 226 defer connPool.Close() 227 assert.EqualValues(t, 100, connPool.Capacity(), "pool capacity should be 100") 228 assert.EqualValues(t, 100, connPool.MaxCap(), "pool max capacity should be 100") 229 assert.EqualValues(t, 0, connPool.WaitTime(), "pool wait time should be 0") 230 assert.EqualValues(t, 0, connPool.WaitCount(), "pool wait count should be 0") 231 assert.EqualValues(t, idleTimeout, connPool.IdleTimeout(), "pool idle timeout should be 0") 232 assert.EqualValues(t, 100, connPool.Available(), "pool available connections should be 100") 233 assert.EqualValues(t, 0, connPool.Active(), "pool active connections should be 0") 234 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 235 236 dbConn, _ := connPool.Get(context.Background(), nil) 237 assert.EqualValues(t, 99, connPool.Available(), "pool available connections should be 99") 238 assert.EqualValues(t, 1, connPool.Active(), "pool active connections should be 1") 239 assert.EqualValues(t, 1, connPool.InUse(), "pool inUse connections should be 1") 240 241 dbConn.Recycle() 242 assert.EqualValues(t, 100, connPool.Available(), "pool available connections should be 100") 243 assert.EqualValues(t, 1, connPool.Active(), "pool active connections should be 1") 244 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 245 } 246 247 func TestConnPoolStateWithSettings(t *testing.T) { 248 db := fakesqldb.New(t) 249 defer db.Close() 250 capacity := 5 251 connPool := newPoolWithCapacity(capacity) 252 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 253 defer connPool.Close() 254 assert.EqualValues(t, 5, connPool.Available(), "pool available connections should be 5") 255 assert.EqualValues(t, 0, connPool.Active(), "pool active connections should be 0") 256 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 257 assert.EqualValues(t, 0, connPool.GetCount(), "pool get count should be 0") 258 assert.EqualValues(t, 0, connPool.GetSettingCount(), "pool get with settings should be 0") 259 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 260 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 261 262 dbConn, err := connPool.Get(context.Background(), nil) 263 require.NoError(t, err) 264 assert.EqualValues(t, 4, connPool.Available(), "pool available connections should be 4") 265 assert.EqualValues(t, 1, connPool.Active(), "pool active connections should be 1") 266 assert.EqualValues(t, 1, connPool.InUse(), "pool inUse connections should be 1") 267 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 1") 268 assert.EqualValues(t, 0, connPool.GetSettingCount(), "pool get with settings should be 0") 269 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 270 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 271 272 dbConn.Recycle() 273 assert.EqualValues(t, 5, connPool.Available(), "pool available connections should be 5") 274 assert.EqualValues(t, 1, connPool.Active(), "pool active connections should be 1") 275 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 276 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 0") 277 assert.EqualValues(t, 0, connPool.GetSettingCount(), "pool get with settings should be 0") 278 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 279 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 280 281 db.AddQuery("a", &sqltypes.Result{}) 282 sa := pools.NewSetting("a", "") 283 dbConn, err = connPool.Get(context.Background(), sa) 284 require.NoError(t, err) 285 assert.EqualValues(t, 4, connPool.Available(), "pool available connections should be 4") 286 assert.EqualValues(t, 2, connPool.Active(), "pool active connections should be 2") 287 assert.EqualValues(t, 1, connPool.InUse(), "pool inUse connections should be 1") 288 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 1") 289 assert.EqualValues(t, 1, connPool.GetSettingCount(), "pool get with settings should be 1") 290 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 291 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 292 293 dbConn.Recycle() 294 assert.EqualValues(t, 5, connPool.Available(), "pool available connections should be 5") 295 assert.EqualValues(t, 2, connPool.Active(), "pool active connections should be 2") 296 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 297 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 1") 298 assert.EqualValues(t, 1, connPool.GetSettingCount(), "pool get with settings should be 1") 299 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 300 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 301 302 // now showcasing diff and reset setting. 303 // Steps 1: acquire all connection with same setting 304 // Steps 2: put all back 305 // Steps 3: acquire a connection with no setting - this will show reset setting count 306 // Steps 4: acquire a connection with different setting - this will show diff setting count 307 308 // Step 1 309 var conns []*DBConn 310 for i := 0; i < capacity; i++ { 311 dbConn, err = connPool.Get(context.Background(), sa) 312 require.NoError(t, err) 313 conns = append(conns, dbConn) 314 } 315 assert.EqualValues(t, 0, connPool.Available(), "pool available connections should be 0") 316 assert.EqualValues(t, 5, connPool.Active(), "pool active connections should be 5") 317 assert.EqualValues(t, 5, connPool.InUse(), "pool inUse connections should be 5") 318 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 1") 319 assert.EqualValues(t, 6, connPool.GetSettingCount(), "pool get with settings should be 6") 320 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 321 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 322 323 // Step 2 324 for _, conn := range conns { 325 conn.Recycle() 326 } 327 assert.EqualValues(t, 5, connPool.Available(), "pool available connections should be 5") 328 assert.EqualValues(t, 5, connPool.Active(), "pool active connections should be 5") 329 assert.EqualValues(t, 0, connPool.InUse(), "pool inUse connections should be 0") 330 assert.EqualValues(t, 1, connPool.GetCount(), "pool get count should be 1") 331 assert.EqualValues(t, 6, connPool.GetSettingCount(), "pool get with settings should be 6") 332 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 333 assert.EqualValues(t, 0, connPool.ResetSettingCount(), "pool reset settings count should be 0") 334 335 // Step 3 336 dbConn, err = connPool.Get(context.Background(), nil) 337 require.NoError(t, err) 338 assert.EqualValues(t, 4, connPool.Available(), "pool available connections should be 4") 339 assert.EqualValues(t, 5, connPool.Active(), "pool active connections should be 5") 340 assert.EqualValues(t, 1, connPool.InUse(), "pool inUse connections should be 1") 341 assert.EqualValues(t, 2, connPool.GetCount(), "pool get count should be 2") 342 assert.EqualValues(t, 6, connPool.GetSettingCount(), "pool get with settings should be 6") 343 assert.EqualValues(t, 0, connPool.DiffSettingCount(), "pool different settings count should be 0") 344 assert.EqualValues(t, 1, connPool.ResetSettingCount(), "pool reset settings count should be 1") 345 dbConn.Recycle() 346 347 // Step 4 348 db.AddQuery("b", &sqltypes.Result{}) 349 sb := pools.NewSetting("b", "") 350 dbConn, err = connPool.Get(context.Background(), sb) 351 require.NoError(t, err) 352 assert.EqualValues(t, 4, connPool.Available(), "pool available connections should be 4") 353 assert.EqualValues(t, 5, connPool.Active(), "pool active connections should be 5") 354 assert.EqualValues(t, 1, connPool.InUse(), "pool inUse connections should be 1") 355 assert.EqualValues(t, 2, connPool.GetCount(), "pool get count should be 2") 356 assert.EqualValues(t, 7, connPool.GetSettingCount(), "pool get with settings should be 7") 357 assert.EqualValues(t, 1, connPool.DiffSettingCount(), "pool different settings count should be 1") 358 assert.EqualValues(t, 1, connPool.ResetSettingCount(), "pool reset settings count should be 1") 359 dbConn.Recycle() 360 } 361 362 func TestPoolGetConnTime(t *testing.T) { 363 db := fakesqldb.New(t) 364 defer db.Close() 365 366 connPool := newPool() 367 connPool.Open(db.ConnParams(), db.ConnParams(), db.ConnParams()) 368 defer connPool.Close() 369 connPool.getConnTime.Reset() 370 371 getTimeMap := connPool.getConnTime.Counts() 372 assert.Zero(t, getTimeMap["PoolTest.GetWithSettings"]) 373 assert.Zero(t, getTimeMap["PoolTest.GetWithoutSettings"]) 374 375 dbConn, err := connPool.Get(context.Background(), nil) 376 require.NoError(t, err) 377 defer dbConn.Recycle() 378 379 getTimeMap = connPool.getConnTime.Counts() 380 assert.EqualValues(t, 1, getTimeMap["PoolTest.GetWithoutSettings"]) 381 assert.Zero(t, getTimeMap["PoolTest.GetWithSettings"]) 382 383 db.AddQuery("b", &sqltypes.Result{}) 384 sb := pools.NewSetting("b", "") 385 dbConn, err = connPool.Get(context.Background(), sb) 386 require.NoError(t, err) 387 defer dbConn.Recycle() 388 389 getTimeMap = connPool.getConnTime.Counts() 390 assert.EqualValues(t, 1, getTimeMap["PoolTest.GetWithSettings"]) 391 } 392 393 func newPool() *Pool { 394 return newPoolWithCapacity(100) 395 } 396 397 func newPoolWithCapacity(capacity int) *Pool { 398 return NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ 399 Size: capacity, 400 IdleTimeoutSeconds: 10, 401 }) 402 }