Skip to content

Commit d4892c4

Browse files
authored
Merge pull request #1153 from ydb-platform/last-usage
added lastUsage locked time
2 parents 6102156 + 3229f66 commit d4892c4

File tree

5 files changed

+160
-12
lines changed

5 files changed

+160
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* Fixed updating last usage timestamp for smart parking of the conns
2+
13
## v3.59.0
24
* Added `Struct` support for `ydb.ParamsBuilder()`
35
* Added support of `TzDate`,`TzDateTime`,`TzTimestamp` types in `ydb.ParamsBuilder()`

internal/conn/conn.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"sync/atomic"
88
"time"
99

10+
"github.com/jonboulle/clockwork"
1011
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
1112
"google.golang.org/grpc"
1213
"google.golang.org/grpc/connectivity"
@@ -55,7 +56,7 @@ type conn struct {
5556
endpoint endpoint.Endpoint // ro access
5657
closed bool
5758
state atomic.Uint32
58-
lastUsage time.Time
59+
lastUsage *lastUsage
5960
onClose []func(*conn)
6061
onTransportErrors []func(ctx context.Context, cc Conn, cause error)
6162
}
@@ -80,7 +81,7 @@ func (c *conn) LastUsage() time.Time {
8081
c.mtx.RLock()
8182
defer c.mtx.RUnlock()
8283

83-
return c.lastUsage
84+
return c.lastUsage.Get()
8485
}
8586

8687
func (c *conn) IsState(states ...State) bool {
@@ -244,12 +245,6 @@ func (c *conn) onTransportError(ctx context.Context, cause error) {
244245
}
245246
}
246247

247-
func (c *conn) touchLastUsage() {
248-
c.mtx.Lock()
249-
defer c.mtx.Unlock()
250-
c.lastUsage = time.Now()
251-
}
252-
253248
func isAvailable(raw *grpc.ClientConn) bool {
254249
return raw != nil && raw.GetState() == connectivity.Ready
255250
}
@@ -332,8 +327,7 @@ func (c *conn) Invoke(
332327
return c.wrapError(err)
333328
}
334329

335-
c.touchLastUsage()
336-
defer c.touchLastUsage()
330+
defer c.lastUsage.Lock()()
337331

338332
ctx, traceID, err := meta.TraceID(ctx)
339333
if err != nil {
@@ -418,8 +412,7 @@ func (c *conn) NewStream(
418412
return nil, c.wrapError(err)
419413
}
420414

421-
c.touchLastUsage()
422-
defer c.touchLastUsage()
415+
defer c.lastUsage.Lock()()
423416

424417
ctx, traceID, err := meta.TraceID(ctx)
425418
if err != nil {
@@ -494,10 +487,15 @@ func withOnTransportError(onTransportError func(ctx context.Context, cc Conn, ca
494487
}
495488

496489
func newConn(e endpoint.Endpoint, config Config, opts ...option) *conn {
490+
clock := clockwork.NewRealClock()
497491
c := &conn{
498492
endpoint: e,
499493
config: config,
500494
done: make(chan struct{}),
495+
lastUsage: &lastUsage{
496+
t: clock.Now(),
497+
clock: clock,
498+
},
501499
}
502500
c.state.Store(uint32(Created))
503501
for _, opt := range opts {

internal/conn/grpc_client_stream.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ func (s *grpcClientStream) CloseSend() (err error) {
3030
onDone(err)
3131
}()
3232

33+
defer s.c.lastUsage.Lock()()
34+
3335
err = s.ClientStream.CloseSend()
3436

3537
if err != nil {
@@ -59,6 +61,8 @@ func (s *grpcClientStream) SendMsg(m interface{}) (err error) {
5961
onDone(err)
6062
}()
6163

64+
defer s.c.lastUsage.Lock()()
65+
6266
err = s.ClientStream.SendMsg(m)
6367

6468
if err != nil {
@@ -96,6 +100,8 @@ func (s *grpcClientStream) RecvMsg(m interface{}) (err error) {
96100
onDone(err)
97101
}()
98102

103+
defer s.c.lastUsage.Lock()()
104+
99105
defer func() {
100106
if err != nil {
101107
md := s.ClientStream.Trailer()

internal/conn/last_usage.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package conn
2+
3+
import (
4+
"sync"
5+
"sync/atomic"
6+
"time"
7+
8+
"github.com/jonboulle/clockwork"
9+
10+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
11+
)
12+
13+
type lastUsage struct {
14+
locks atomic.Int64
15+
mu xsync.RWMutex
16+
t time.Time
17+
clock clockwork.Clock
18+
}
19+
20+
func (l *lastUsage) Get() time.Time {
21+
if l.locks.CompareAndSwap(0, 1) {
22+
defer func() {
23+
l.locks.Add(-1)
24+
}()
25+
26+
l.mu.RLock()
27+
defer l.mu.RUnlock()
28+
29+
return l.t
30+
}
31+
32+
return l.clock.Now()
33+
}
34+
35+
func (l *lastUsage) Lock() (releaseFunc func()) {
36+
l.locks.Add(1)
37+
38+
return sync.OnceFunc(func() {
39+
if l.locks.Add(-1) == 0 {
40+
l.mu.WithLock(func() {
41+
l.t = l.clock.Now()
42+
})
43+
}
44+
})
45+
}

internal/conn/last_usage_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package conn
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/jonboulle/clockwork"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func Test_lastUsage_Lock(t *testing.T) {
12+
t.Run("NowFromLocked", func(t *testing.T) {
13+
start := time.Unix(0, 0)
14+
clock := clockwork.NewFakeClockAt(start)
15+
lu := &lastUsage{
16+
t: start,
17+
clock: clock,
18+
}
19+
t1 := lu.Get()
20+
require.Equal(t, start, t1)
21+
f := lu.Lock()
22+
clock.Advance(time.Hour)
23+
t2 := lu.Get()
24+
require.Equal(t, start.Add(time.Hour), t2)
25+
clock.Advance(time.Hour)
26+
f()
27+
t3 := lu.Get()
28+
require.Equal(t, start.Add(2*time.Hour), t3)
29+
clock.Advance(time.Hour)
30+
t4 := lu.Get()
31+
require.Equal(t, start.Add(2*time.Hour), t4)
32+
})
33+
t.Run("UpdateAfterLastUnlock", func(t *testing.T) {
34+
start := time.Unix(0, 0)
35+
clock := clockwork.NewFakeClockAt(start)
36+
lu := &lastUsage{
37+
t: start,
38+
clock: clock,
39+
}
40+
t1 := lu.Get()
41+
require.Equal(t, start, t1)
42+
f1 := lu.Lock()
43+
clock.Advance(time.Hour)
44+
t2 := lu.Get()
45+
require.Equal(t, start.Add(time.Hour), t2)
46+
f2 := lu.Lock()
47+
clock.Advance(time.Hour)
48+
f1()
49+
f3 := lu.Lock()
50+
clock.Advance(time.Hour)
51+
t3 := lu.Get()
52+
require.Equal(t, start.Add(3*time.Hour), t3)
53+
clock.Advance(time.Hour)
54+
t4 := lu.Get()
55+
require.Equal(t, start.Add(4*time.Hour), t4)
56+
f3()
57+
t5 := lu.Get()
58+
require.Equal(t, start.Add(4*time.Hour), t5)
59+
clock.Advance(time.Hour)
60+
t6 := lu.Get()
61+
require.Equal(t, start.Add(5*time.Hour), t6)
62+
clock.Advance(time.Hour)
63+
f2()
64+
t7 := lu.Get()
65+
require.Equal(t, start.Add(6*time.Hour), t7)
66+
clock.Advance(time.Hour)
67+
f2()
68+
t8 := lu.Get()
69+
require.Equal(t, start.Add(6*time.Hour), t8)
70+
})
71+
t.Run("DeferRelease", func(t *testing.T) {
72+
start := time.Unix(0, 0)
73+
clock := clockwork.NewFakeClockAt(start)
74+
lu := &lastUsage{
75+
t: start,
76+
clock: clock,
77+
}
78+
func() {
79+
t1 := lu.Get()
80+
require.Equal(t, start, t1)
81+
clock.Advance(time.Hour)
82+
t2 := lu.Get()
83+
require.Equal(t, start, t2)
84+
clock.Advance(time.Hour)
85+
defer lu.Lock()()
86+
t3 := lu.Get()
87+
require.Equal(t, start.Add(2*time.Hour), t3)
88+
clock.Advance(time.Hour)
89+
t4 := lu.Get()
90+
require.Equal(t, start.Add(3*time.Hour), t4)
91+
clock.Advance(time.Hour)
92+
}()
93+
clock.Advance(time.Hour)
94+
t5 := lu.Get()
95+
require.Equal(t, start.Add(4*time.Hour), t5)
96+
})
97+
}

0 commit comments

Comments
 (0)