diff --git a/lib/bindevict.go b/lib/bindevict.go index 59eacfd6..67d5bb22 100644 --- a/lib/bindevict.go +++ b/lib/bindevict.go @@ -54,10 +54,9 @@ func GetBindEvict() *BindEvict { } return cfg.(*BindEvict) } - -func (bindEvict *BindEvict) Copy() *BindEvict { +func (this *BindEvict) Copy() *BindEvict { out := BindEvict{BindThrottle: make(map[uint32]map[string]*BindThrottle)} - for k, v := range bindEvict.BindThrottle { + for k, v := range this.BindThrottle { out.BindThrottle[k] = v } return &out @@ -86,30 +85,26 @@ func (entry *BindThrottle) decrAllowEveryX(y int) { return } entry.AllowEveryX = 0 - bindEvict := GetBindEvict() - bindEvict.lock.Lock() - defer bindEvict.lock.Unlock() + GetBindEvict().lock.Lock() + defer GetBindEvict().lock.Unlock() // delete entry - if len(bindEvict.BindThrottle[entry.Sqlhash]) == 1 { - updateCopy := bindEvict.Copy() + if len(GetBindEvict().BindThrottle[entry.Sqlhash]) == 1 { + updateCopy := GetBindEvict().Copy() delete(updateCopy.BindThrottle, entry.Sqlhash) gBindEvict.Store(updateCopy) } else { // copy everything except bindKV (skipping it is deleting it) bindKV := fmt.Sprintf("%s|%s", entry.Name, entry.Value) - updatedBindThrottleMap := make(map[string]*BindThrottle) - updateCopy := bindEvict.Copy() - for k, v := range bindEvict.BindThrottle[entry.Sqlhash] { + updateCopy := make(map[string]*BindThrottle) + for k, v := range GetBindEvict().BindThrottle[entry.Sqlhash] { if k == bindKV { continue } - updatedBindThrottleMap[k] = v + updateCopy[k] = v } - updateCopy.BindThrottle[entry.Sqlhash] = updatedBindThrottleMap - gBindEvict.Store(updateCopy) + GetBindEvict().BindThrottle[entry.Sqlhash] = updateCopy } } - func (entry *BindThrottle) incrAllowEveryX() { if logger.GetLogger().V(logger.Warning) { info := fmt.Sprintf("hash:%d bindName:%s val:%s prev:%d", entry.Sqlhash, entry.Name, entry.Value, entry.AllowEveryX) @@ -121,16 +116,10 @@ func (entry *BindThrottle) incrAllowEveryX() { } } -func (bindEvict *BindEvict) ShouldBlock(sqlhash uint32, bindKV map[string]string, heavyUsage bool) (bool, *BindThrottle) { - entryTime := time.Now() - defer func() { - if logger.GetLogger().V(logger.Verbose) { - logger.GetLogger().Log(logger.Info, fmt.Sprintf("bind throttle check operation exec duration is %v microseconds and Bind-eviction-decrease/sec %v", time.Now().Sub(entryTime).Microseconds(), GetConfig().BindEvictionDecrPerSec)) - } - }() - bindEvict.lock.Lock() - sqlBinds := bindEvict.BindThrottle[sqlhash] - bindEvict.lock.Unlock() +func (be *BindEvict) ShouldBlock(sqlhash uint32, bindKV map[string]string, heavyUsage bool) (bool, *BindThrottle) { + GetBindEvict().lock.Lock() + sqlBinds := GetBindEvict().BindThrottle[sqlhash] + GetBindEvict().lock.Unlock() for k0, v := range bindKV /*parseBinds(request)*/ { k := NormalizeBindName(k0) concatKey := fmt.Sprintf("%s|%s", k, v) @@ -151,9 +140,8 @@ func (bindEvict *BindEvict) ShouldBlock(sqlhash uint32, bindKV map[string]string // check if not used in a while now := time.Now() recent := entry.RecentAttempt.Load().(*time.Time) - throttleReductionBase := now.Sub(*recent).Seconds() - throttleReductionRate := throttleReductionBase * GetConfig().BindEvictionDecrPerSec - entry.decrAllowEveryX(int(throttleReductionRate)) + gap := now.Sub(*recent).Seconds() * GetConfig().BindEvictionDecrPerSec + entry.decrAllowEveryX(int(gap)) if entry.AllowEveryX == 0 { return false, nil } diff --git a/tests/unittest/bindEvict/main_test.go b/tests/unittest/bindEvict/main_test.go index 24ef1838..3b8b87e0 100644 --- a/tests/unittest/bindEvict/main_test.go +++ b/tests/unittest/bindEvict/main_test.go @@ -100,49 +100,38 @@ func sleepyQ(conn *sql.Conn, delayRow int) error { return nil } -var normCliErr error - -func NormCliErr() error { - if normCliErr == nil { - normCliErr = fmt.Errorf("normal client got error") - } - return normCliErr -} - -func partialBadLoad(fracBad float64) error { +func fastAndSlowBinds() error { db, err := sql.Open("hera", "127.0.0.1:31002") if err != nil { fmt.Printf("Error db %s\n", err.Error()) return err } - db.SetConnMaxLifetime(111 * time.Second) + db.SetConnMaxLifetime(22 * time.Second) db.SetMaxIdleConns(0) db.SetMaxOpenConns(22111) defer db.Close() // client threads of slow queries var stop2 int - var stop3 int var badCliErr string - var cliErr string - numBad := int(max_conn * fracBad) - numNorm := int(max_conn*2.1) + 1 - numBad - fmt.Printf("spawning clients bad%d norm%d\n", numBad, numNorm) - mkClients(numBad, &stop2, 29001111, "badClient", &badCliErr, db) - mkClients(numNorm, &stop3, 100, "normClient", &cliErr, db) // bind value is short, so bindevict won't trigger + mkClients(1+int(max_conn*1.6), &stop2, 29001111, "badClient", &badCliErr, db) time.Sleep(3100 * time.Millisecond) - //time.Sleep(33100 * time.Millisecond) + /* if (testutil.RegexCountFile("BIND_THROTTLE", "cal.log") == 0) { + return fmt.Errorf("BIND_THROTTLE was not triggered") + } + if (testutil.RegexCountFile("BIND_EVICT", "cal.log") == 0) { + return fmt.Errorf("BIND_EVICT was not triggered") + } // */ // start normal clients after initial backlog timeouts - var stop int var normCliErrStr string + var stop int mkClients(1, &stop, 21001111, "n client", &normCliErrStr, db) time.Sleep(1100 * time.Millisecond) // if we throttle down or stop, it restores stop2 = 1 // stop bad clients - stop3 = 1 - lib.GetConfig().BindEvictionDecrPerSec = 11333.1 + lib.GetConfig().BindEvictionDecrPerSec = 11500.1 defer func() { lib.GetConfig().BindEvictionDecrPerSec = 1.1 }() time.Sleep(1 * time.Second) conn, err := db.Conn(context.Background()) @@ -159,13 +148,21 @@ func partialBadLoad(fracBad float64) error { } stop = 1 - // tolerate soft eviction on normal client when we did not use bind eviction if len(normCliErrStr) != 0 { return NormCliErr() - } // */ + } return nil } +var normCliErr error + +func NormCliErr() error { + if normCliErr == nil { + normCliErr = fmt.Errorf("normal client got error") + } + return normCliErr +} + func mkClients(num int, stop *int, bindV int, grpName string, outErr *string, db *sql.DB) { for i := 0; i < num; i++ { go func(clientId int) { @@ -209,34 +206,15 @@ func mkClients(num int, stop *int, bindV int, grpName string, outErr *string, db func TestBindEvict(t *testing.T) { // we would like to clear hera.log, but even if we try, lots of messages still go there logger.GetLogger().Log(logger.Debug, "TestBindEvict +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") - testutil.BackupAndClear("cal", "TestBindEvict start") - testutil.BackupAndClear("hera", "TestBindEvict start") - err := partialBadLoad(0.10) - if err != nil && err != NormCliErr() { + err := fastAndSlowBinds() + if err != nil { t.Fatalf("main step function returned err %s", err.Error()) } - if testutil.RegexCountFile("BIND_THROTTLE", "cal.log") > 0 { - t.Fatalf("BIND_THROTTLE should not trigger") + if testutil.RegexCountFile("BIND_THROTTLE", "cal.log") == 0 { + t.Fatalf("BIND_THROTTLE was not triggered") } - if testutil.RegexCountFile("BIND_EVICT", "cal.log") > 0 { - t.Fatalf("BIND_EVICT should not trigger") + if testutil.RegexCountFile("BIND_EVICT", "cal.log") == 0 { + t.Fatalf("BIND_EVICT was not triggered") } - if testutil.RegexCountFile("HERA-10", "hera.log") == 0 { - t.Fatal("backlog timeout or saturation was not triggered") - } // */ - - if true { - logger.GetLogger().Log(logger.Debug, "TestBindEvict midpt +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") - err := partialBadLoad(0.7) - if err != nil { - // t.Fatalf("main step function returned err %s", err.Error()) // can be triggered since test only has one sql - } - if testutil.RegexCountFile("BIND_THROTTLE", "cal.log") == 0 { - t.Fatalf("BIND_THROTTLE should trigger") - } - if testutil.RegexCountFile("BIND_EVICT", "cal.log") == 0 { - t.Fatalf("BIND_EVICT should trigger") - } - } // endif - logger.GetLogger().Log(logger.Debug, "TestBindEvict done +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") + logger.GetLogger().Log(logger.Debug, "TestBindEvict stop +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") } // */