Skip to content

Commit

Permalink
router, backend: replace time.Time with mono-time to optimize duratio…
Browse files Browse the repository at this point in the history
…n calculation (#461)
  • Loading branch information
djshow832 authored Jan 25, 2024
1 parent 5273b42 commit 837db0b
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ vendor
work
.vscode/
.cover*
coverage.dat
grafonnet-lib
dist
5 changes: 3 additions & 2 deletions pkg/manager/router/backend_observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/pingcap/tiproxy/lib/util/errors"
"github.com/pingcap/tiproxy/lib/util/waitgroup"
"github.com/pingcap/tiproxy/pkg/metrics"
"github.com/pingcap/tiproxy/pkg/util/monotime"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -138,7 +139,7 @@ func (bo *BackendObserver) Refresh() {

func (bo *BackendObserver) observe(ctx context.Context) {
for ctx.Err() == nil {
startTime := time.Now()
startTime := monotime.Now()
backendInfo, err := bo.fetcher.GetBackendList(ctx)
if err != nil {
bo.logger.Error("fetching backends encounters error", zap.Error(err))
Expand All @@ -151,7 +152,7 @@ func (bo *BackendObserver) observe(ctx context.Context) {
bo.notifyIfChanged(bhMap)
}

cost := time.Since(startTime)
cost := monotime.Since(startTime)
metrics.HealthCheckCycleGauge.Set(cost.Seconds())
wait := bo.healthCheckConfig.Interval - cost
if wait > 0 {
Expand Down
3 changes: 2 additions & 1 deletion pkg/manager/router/health_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/pingcap/tiproxy/lib/config"
"github.com/pingcap/tiproxy/lib/util/errors"
pnet "github.com/pingcap/tiproxy/pkg/proxy/net"
"github.com/pingcap/tiproxy/pkg/util/monotime"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -90,7 +91,7 @@ func (dhc *DefaultHealthCheck) Check(ctx context.Context, addr string, info *Bac
// Also dial the SQL port just in case that the SQL port hangs.
var serverVersion string
err := dhc.connectWithRetry(ctx, func() error {
startTime := time.Now()
startTime := monotime.Now()
conn, err := net.DialTimeout("tcp", addr, dhc.cfg.DialTimeout)
setPingBackendMetrics(addr, err == nil, startTime)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions pkg/manager/router/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/pingcap/tiproxy/pkg/metrics"
"github.com/pingcap/tiproxy/pkg/util/monotime"
)

func updateBackendStatusMetrics(addr string, prevStatus, curStatus BackendStatus) {
Expand Down Expand Up @@ -38,20 +39,20 @@ func succeedToLabel(succeed bool) string {
return "fail"
}

func addMigrateMetrics(from, to string, succeed bool, startTime time.Time) {
func addMigrateMetrics(from, to string, succeed bool, startTime monotime.Time) {
resLabel := succeedToLabel(succeed)
metrics.MigrateCounter.WithLabelValues(from, to, resLabel).Inc()

cost := time.Since(startTime)
cost := monotime.Since(startTime)
metrics.MigrateDurationHistogram.WithLabelValues(from, to, resLabel).Observe(cost.Seconds())
}

func readMigrateCounter(from, to string, succeed bool) (int, error) {
return metrics.ReadCounter(metrics.MigrateCounter.WithLabelValues(from, to, succeedToLabel(succeed)))
}

func setPingBackendMetrics(addr string, succeed bool, startTime time.Time) {
cost := time.Since(startTime)
func setPingBackendMetrics(addr string, succeed bool, startTime monotime.Time) {
cost := monotime.Since(startTime)
metrics.PingBackendGauge.WithLabelValues(addr).Set(cost.Seconds())
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/manager/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

glist "github.com/bahlo/generic-list-go"
"github.com/pingcap/tiproxy/pkg/util/monotime"
)

// ConnEventReceiver receives connection events.
Expand Down Expand Up @@ -142,6 +143,6 @@ type connWrapper struct {
// Reference to the target backend if it's redirecting, otherwise nil.
redirectingBackend *backendWrapper
// Last redirect start time of this connection.
lastRedirect time.Time
lastRedirect monotime.Time
phase connPhase
}
3 changes: 2 additions & 1 deletion pkg/manager/router/router_score.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
glist "github.com/bahlo/generic-list-go"
"github.com/pingcap/tiproxy/lib/config"
"github.com/pingcap/tiproxy/lib/util/waitgroup"
"github.com/pingcap/tiproxy/pkg/util/monotime"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -335,7 +336,7 @@ func (router *ScoreBasedRouter) rebalanceLoop(ctx context.Context) {
}

func (router *ScoreBasedRouter) rebalance(maxNum int) {
curTime := time.Now()
curTime := monotime.Now()
router.Lock()
defer router.Unlock()
for i := 0; i < maxNum; i++ {
Expand Down
7 changes: 4 additions & 3 deletions pkg/proxy/backend/backend_conn_mgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/pingcap/tiproxy/lib/util/waitgroup"
"github.com/pingcap/tiproxy/pkg/manager/router"
pnet "github.com/pingcap/tiproxy/pkg/proxy/net"
"github.com/pingcap/tiproxy/pkg/util/monotime"
"github.com/siddontang/go/hack"
"go.uber.org/zap"
)
Expand Down Expand Up @@ -203,7 +204,7 @@ func (mgr *BackendConnManager) getBackendIO(ctx context.Context, cctx ConnContex
// - One TiDB may be just shut down and another is just started but not ready yet
bctx, cancel := context.WithTimeout(ctx, mgr.config.ConnectTimeout)
selector := r.GetBackendSelector()
startTime := time.Now()
startTime := monotime.Now()
var addr string
var origErr error
io, err := backoff.RetryNotifyWithData(
Expand Down Expand Up @@ -245,7 +246,7 @@ func (mgr *BackendConnManager) getBackendIO(ctx context.Context, cctx ConnContex
)
cancel()

duration := time.Since(startTime)
duration := monotime.Since(startTime)
addGetBackendMetrics(duration, err == nil)
if err != nil {
mgr.logger.Error("get backend failed", zap.Duration("duration", duration), zap.NamedError("last_err", origErr))
Expand Down Expand Up @@ -275,7 +276,7 @@ func (mgr *BackendConnManager) ExecuteCmd(ctx context.Context, request []byte) (
return
}
cmd := pnet.Command(request[0])
startTime := time.Now()
startTime := monotime.Now()

// Once the request is accepted, it's treated in the transaction, so we don't check graceful shutdown here.
if mgr.closeStatus.Load() >= statusClosing {
Expand Down
5 changes: 3 additions & 2 deletions pkg/proxy/backend/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import (

"github.com/pingcap/tiproxy/pkg/metrics"
pnet "github.com/pingcap/tiproxy/pkg/proxy/net"
"github.com/pingcap/tiproxy/pkg/util/monotime"
)

func addCmdMetrics(cmd pnet.Command, addr string, startTime time.Time) {
func addCmdMetrics(cmd pnet.Command, addr string, startTime monotime.Time) {
label := cmd.String()
metrics.QueryTotalCounter.WithLabelValues(addr, label).Inc()

// The duration labels are different with TiDB: Labels in TiDB are statement types.
// However, the proxy is not aware of the statement types, so we use command types instead.
cost := time.Since(startTime)
cost := monotime.Since(startTime)
metrics.QueryDurationHistogram.WithLabelValues(addr, label).Observe(cost.Seconds())
}

Expand Down
41 changes: 41 additions & 0 deletions pkg/util/monotime/monotime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2024 PingCAP, Inc.
// SPDX-License-Identifier: Apache-2.0

package monotime

import (
"time"
_ "unsafe"
)

//go:noescape
//go:linkname nanotime runtime.nanotime
func nanotime() int64

// Time is a monotonic clock time which is used to calculate duration.
// It's 2x faster than time.Time.
type Time int64

func Now() Time {
return Time(nanotime())
}

func Since(t Time) time.Duration {
return time.Duration(Time(nanotime()) - t)
}

func (t Time) Add(d time.Duration) Time {
return t + Time(d)
}

func (t Time) Sub(d time.Duration) Time {
return t - Time(d)
}

func (t Time) Before(u Time) bool {
return t < u
}

func (t Time) After(u Time) bool {
return t > u
}
46 changes: 46 additions & 0 deletions pkg/util/monotime/monotime_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 PingCAP, Inc.
// SPDX-License-Identifier: Apache-2.0

package monotime

import (
"testing"
"time"

"github.com/stretchr/testify/require"
)

func BenchmarkGoNow(b *testing.B) {
for i := 0; i < b.N; i++ {
time.Now()
}
}

func BenchmarkMonoNow(b *testing.B) {
for i := 0; i < b.N; i++ {
Now()
}
}

func BenchmarkGoSince(b *testing.B) {
for i := 0; i < b.N; i++ {
time.Since(time.Now())
}
}

func BenchmarkMonoSince(b *testing.B) {
for i := 0; i < b.N; i++ {
Since(Now())
}
}

func TestAfter(t *testing.T) {
t1 := Now()
time.Sleep(100 * time.Millisecond)
d := Since(t1)
require.GreaterOrEqual(t, d, 100*time.Millisecond)
require.True(t, Now().After(t1))
require.True(t, t1.Before(Now()))
require.Greater(t, t1.Add(time.Millisecond), t1)
require.Less(t, t1.Sub(time.Millisecond), t1)
}

0 comments on commit 837db0b

Please sign in to comment.