Skip to content

Commit a16161a

Browse files
authored
Merge pull request #1476 from ydb-platform/limit-usage
* Added `ydb.WithSessionPoolSessionUsageLimit()` option for limitation max count of session usage
2 parents f4c4c8a + 2f69e79 commit a16161a

File tree

10 files changed

+84
-9
lines changed

10 files changed

+84
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
* refactored experimental topic iterators in topicsugar package
1+
* Added `ydb.WithSessionPoolSessionUsageLimit()` option for limitation max count of session usage
2+
* Refactored experimental topic iterators in `topicsugar` package
23

34
## v3.80.9
45
* Fixed bug in experimental api: `ydb.ParamsBuilder().Param().Optional()` receive pointer and really produce optional value.

internal/pool/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func isRetriable(err error) bool {
2323

2424
switch {
2525
case
26-
xerrors.Is(err, errPoolIsOverflow),
26+
xerrors.Is(err, errPoolIsOverflow, errItemIsNotAlive, errNoProgress),
2727
xerrors.IsRetryableError(err),
2828
xerrors.IsOperationError(err, Ydb.StatusIds_OVERLOADED),
2929
xerrors.IsTransportError(

internal/pool/pool.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ type (
3434
closeTimeout time.Duration
3535
closeItem func(ctx context.Context, item PT)
3636
idleTimeToLive time.Duration
37+
itemUsageLimit uint64
3738
}
3839
itemInfo[PT ItemConstraint[T], T any] struct {
39-
idle *xlist.Element[PT]
40-
lastUsage time.Time
40+
idle *xlist.Element[PT]
41+
lastUsage time.Time
42+
useCounter *uint64
4143
}
4244
waitChPool[PT ItemConstraint[T], T any] interface {
4345
GetOrNew() *chan PT
@@ -93,6 +95,12 @@ func WithLimit[PT ItemConstraint[T], T any](size int) Option[PT, T] {
9395
}
9496
}
9597

98+
func WithItemUsageLimit[PT ItemConstraint[T], T any](itemUsageLimit uint64) Option[PT, T] {
99+
return func(c *Config[PT, T]) {
100+
c.itemUsageLimit = itemUsageLimit
101+
}
102+
}
103+
96104
func WithTrace[PT ItemConstraint[T], T any](t *Trace) Option[PT, T] {
97105
return func(c *Config[PT, T]) {
98106
c.trace = t
@@ -217,8 +225,10 @@ func makeAsyncCreateItemFunc[PT ItemConstraint[T], T any]( //nolint:funlen
217225
newItem, err := p.config.createItem(createCtx)
218226
if newItem != nil {
219227
p.mu.WithLock(func() {
228+
var useCounter uint64
220229
p.index[newItem] = itemInfo[PT, T]{
221-
lastUsage: p.config.clock.Now(),
230+
lastUsage: p.config.clock.Now(),
231+
useCounter: &useCounter,
222232
}
223233
})
224234
}
@@ -592,10 +602,13 @@ func (p *Pool[PT, T]) getItem(ctx context.Context) (item PT, finalErr error) { /
592602
panic("no index for item")
593603
}
594604

605+
*info.useCounter++
606+
595607
return info
596608
})
597609

598-
if p.config.idleTimeToLive > 0 && p.config.clock.Since(info.lastUsage) > p.config.idleTimeToLive {
610+
if (p.config.itemUsageLimit > 0 && *info.useCounter > p.config.itemUsageLimit) ||
611+
(p.config.idleTimeToLive > 0 && p.config.clock.Since(info.lastUsage) > p.config.idleTimeToLive) {
599612
p.closeItem(ctx, item)
600613
p.mu.WithLock(func() {
601614
p.changeState(func() Stats {

internal/pool/pool_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func mustClose(t testing.TB, pool closer.Closer) {
159159
}
160160
}
161161

162-
func TestPool(t *testing.T) {
162+
func TestPool(t *testing.T) { //nolint:gocyclo
163163
rootCtx := xtest.Context(t)
164164
t.Run("New", func(t *testing.T) {
165165
t.Run("Default", func(t *testing.T) {
@@ -177,6 +177,33 @@ func TestPool(t *testing.T) {
177177
)
178178
require.EqualValues(t, 1, p.config.limit)
179179
})
180+
t.Run("WithItemUsageLimit", func(t *testing.T) {
181+
var newCounter int64
182+
p := New[*testItem, testItem](rootCtx,
183+
WithLimit[*testItem, testItem](1),
184+
WithItemUsageLimit[*testItem, testItem](5),
185+
WithCreateItemTimeout[*testItem, testItem](50*time.Millisecond),
186+
WithCloseItemTimeout[*testItem, testItem](50*time.Millisecond),
187+
WithCreateItemFunc(func(context.Context) (*testItem, error) {
188+
atomic.AddInt64(&newCounter, 1)
189+
190+
var v testItem
191+
192+
return &v, nil
193+
}),
194+
)
195+
require.EqualValues(t, 1, p.config.limit)
196+
var lambdaCounter int64
197+
err := p.With(rootCtx, func(ctx context.Context, item *testItem) error {
198+
if atomic.AddInt64(&lambdaCounter, 1) < 10 {
199+
return xerrors.Retryable(errors.New("test"))
200+
}
201+
202+
return nil
203+
})
204+
require.NoError(t, err)
205+
require.EqualValues(t, 2, newCounter)
206+
})
180207
t.Run("WithCreateItemFunc", func(t *testing.T) {
181208
var newCounter int64
182209
p := New(rootCtx,

internal/query/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ func New(ctx context.Context, cc grpc.ClientConnInterface, cfg *config.Config) *
525525
done: make(chan struct{}),
526526
pool: pool.New(ctx,
527527
pool.WithLimit[*Session, Session](cfg.PoolLimit()),
528+
pool.WithItemUsageLimit[*Session, Session](cfg.PoolSessionUsageLimit()),
528529
pool.WithTrace[*Session, Session](poolTrace(cfg.Trace())),
529530
pool.WithCreateItemTimeout[*Session, Session](cfg.SessionCreateTimeout()),
530531
pool.WithCloseItemTimeout[*Session, Session](cfg.SessionDeleteTimeout()),

internal/query/config/config.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ const (
1717
type Config struct {
1818
config.Common
1919

20-
poolLimit int
20+
poolLimit int
21+
poolSessionUsageLimit uint64
2122

2223
sessionCreateTimeout time.Duration
2324
sessionDeleteTimeout time.Duration
@@ -60,6 +61,10 @@ func (c *Config) PoolLimit() int {
6061
return c.poolLimit
6162
}
6263

64+
func (c *Config) PoolSessionUsageLimit() uint64 {
65+
return c.poolSessionUsageLimit
66+
}
67+
6368
// SessionCreateTimeout limits maximum time spent on Create session request
6469
func (c *Config) SessionCreateTimeout() time.Duration {
6570
return c.sessionCreateTimeout

internal/query/config/options.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ func WithPoolLimit(size int) Option {
3434
}
3535
}
3636

37+
func WithPoolSessionUsageLimit(sessionUsageLimit uint64) Option {
38+
return func(c *Config) {
39+
c.poolSessionUsageLimit = sessionUsageLimit
40+
}
41+
}
42+
3743
// WithSessionCreateTimeout limits maximum time spent on Create session request
3844
// If sessionCreateTimeout is less than or equal to zero then no used timeout on create session request
3945
func WithSessionCreateTimeout(createSessionTimeout time.Duration) Option {

internal/table/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func New(ctx context.Context, cc grpc.ClientConnInterface, config *config.Config
3333
},
3434
pool: pool.New[*session, session](ctx,
3535
pool.WithLimit[*session, session](config.SizeLimit()),
36+
pool.WithItemUsageLimit[*session, session](config.SessionUsageLimit()),
3637
pool.WithIdleTimeToLive[*session, session](config.IdleThreshold()),
3738
pool.WithCreateItemTimeout[*session, session](config.CreateSessionTimeout()),
3839
pool.WithCloseItemTimeout[*session, session](config.DeleteTimeout()),

internal/table/config/config.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ func WithSizeLimit(sizeLimit int) Option {
6262
}
6363
}
6464

65+
func WithPoolSessionUsageLimit(sessionUsageLimit uint64) Option {
66+
return func(c *Config) {
67+
c.sessionUsageLimit = sessionUsageLimit
68+
}
69+
}
70+
6571
// WithKeepAliveMinSize defines lower bound for sessions in the pool. If there are more sessions open, then
6672
// the excess idle ones will be closed and removed after IdleKeepAliveThreshold is reached for each of them.
6773
// If keepAliveMinSize is less than zero, then no sessions will be preserved
@@ -159,7 +165,8 @@ func WithClock(clock clockwork.Clock) Option {
159165
type Config struct {
160166
config.Common
161167

162-
sizeLimit int
168+
sizeLimit int
169+
sessionUsageLimit uint64
163170

164171
createSessionTimeout time.Duration
165172
deleteTimeout time.Duration
@@ -189,6 +196,10 @@ func (c *Config) SizeLimit() int {
189196
return c.sizeLimit
190197
}
191198

199+
func (c *Config) SessionUsageLimit() uint64 {
200+
return c.sessionUsageLimit
201+
}
202+
192203
// KeepAliveMinSize is a lower bound for sessions in the pool. If there are more sessions open, then
193204
// the excess idle ones will be closed and removed after IdleKeepAliveThreshold is reached for each of them.
194205
// If KeepAliveMinSize is less than zero, then no sessions will be preserved

options.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,16 @@ func WithSessionPoolSizeLimit(sizeLimit int) Option {
488488
}
489489
}
490490

491+
// WithSessionPoolSessionUsageLimit set max count for use session
492+
func WithSessionPoolSessionUsageLimit(sessionUsageLimit uint64) Option {
493+
return func(ctx context.Context, d *Driver) error {
494+
d.tableOptions = append(d.tableOptions, tableConfig.WithPoolSessionUsageLimit(sessionUsageLimit))
495+
d.queryOptions = append(d.queryOptions, queryConfig.WithPoolSessionUsageLimit(sessionUsageLimit))
496+
497+
return nil
498+
}
499+
}
500+
491501
func WithLazyTx(lazyTx bool) Option {
492502
return func(ctx context.Context, d *Driver) error {
493503
d.queryOptions = append(d.queryOptions, queryConfig.WithLazyTx(lazyTx))

0 commit comments

Comments
 (0)