Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added options for configuring removal of idle connections #674

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

veirfuna
Copy link

Approach for #651

pool.go Outdated
@@ -50,6 +60,11 @@ func (p *pool) Store(v wire) {
p.cond.L.Lock()
if !p.down && v.Error() == nil {
p.list = append(p.list, v)

if p.idleConnTTL != nil {
p.removeIdleConns()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @veirfuna,

I guess calling .removeIdleConns() alone in the .Store() can't remove idle connections. For example, if .Store() is not called, then idle connections are still there. You probably need to put .removeIdleConns() into a ticker.

and thanks for your contribution!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @rueian,

My first attempts were to create a goroutine that is observe idle connections and remove them. Do you see any problems in such solution? The only idea to not use goroutine was to add .removeIdleConns() to .Store()

Copy link
Collaborator

@rueian rueian Nov 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have no choice but to use an additional goroutine. We can use time.AfterFunc and it is actually an additional goroutine. We can schedule the cleanup function only when idle connections > minIdle.

pool.go Outdated Show resolved Hide resolved
pool.go Outdated Show resolved Hide resolved
pool.go Outdated
Comment on lines 42 to 43
stopChan chan struct{}
stopChanOnce *sync.Once
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stopChan chan struct{}
stopChanOnce *sync.Once
timer *time.Timer

Can we just use time.AfterFunc and store the timer here? I think a timer is convenient for us because we can use .Stop() when len(p.list) <= minSize and .Reset() when len(p.list) > minSize.

pool.go Outdated
idleConnTTL *time.Duration
stopChan chan struct{}
stopChanOnce *sync.Once
lastUsage map[wire]time.Time
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we need to keep track of idle time for each connection. Can we just clear every connection in the p.list that exceeds minSize whenever the timer is fired?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sound like a good idea. Made an update in PR

pool.go Outdated
Comment on lines 24 to 26
if idleConnTTL != 0 {
p.timer = time.AfterFunc(idleConnTTL, p.removeIdleConns)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to start the timer at this point. There is no idle connection.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your idea is to start the timer only after len(p.list) > p.minSize?

pool.go Outdated
}

func (p *pool) resetTimerIfNeeded() {
if p.timer != nil && p.size > p.minSize {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if p.timer != nil && p.size > p.minSize {
if p.timer != nil && len(p.list) > p.minSize {

p.size is all connections, including active ones. I think we only need to start the timer if idle connections len(p.list) exceed p.minSize.

pool.go Outdated
Comment on lines 88 to 90
if p.idleConnTTL == 0 {
return
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this check here? I think we can do it when we create the timer.

Suggested change
if p.idleConnTTL == 0 {
return
}

pool.go Outdated
p.list = p.list[:newLen]
p.size = p.minSize

p.stopTimer()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
p.stopTimer()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why you think the timer should not be stopped inside removeIdleConns?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the timer already stopped at this point?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you are right

pool.go Outdated

func (p *pool) resetTimerIfNeeded() {
if p.timer != nil && p.size > p.minSize {
p.timer.Reset(p.idleConnTTL)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that we can't always reset the timer. We can only reset it after it is fired, so we probably need another flag for that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, added a new flag

pool.go Outdated
return
}

newLen := len(p.list) - min(p.size-p.minSize, len(p.list))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
newLen := len(p.list) - min(p.size-p.minSize, len(p.list))
newLen := min(p.minSize, len(p.list))

p.cond.L.Lock()
defer p.cond.L.Unlock()

if p.down || p.size <= p.minSize || len(p.list) == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if p.down || p.size <= p.minSize || len(p.list) == 0 {
if p.down || len(p.list) <= p.minSize {

Comment on lines +105 to +107
for _, w := range p.list[newLen:] {
w.Close()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for _, w := range p.list[newLen:] {
w.Close()
}
for i, w := range p.list[newLen:] {
w.Close()
p.list[i] = nil
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows those connections to be GC.

}

p.list = p.list[:newLen]
p.size = p.minSize
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
p.size = p.minSize

Setting it directly to the p.minSize seems incorrect. We probably should put it in the above loop and use p.size-- instead.

Comment on lines +88 to +93
if p.timer == nil {
p.timer = time.AfterFunc(p.idleConnTTL, p.removeIdleConns)
return
}

p.timer.Reset(p.idleConnTTL)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if p.timer == nil {
p.timer = time.AfterFunc(p.idleConnTTL, p.removeIdleConns)
return
}
p.timer.Reset(p.idleConnTTL)
if p.timer == nil {
p.timer = time.AfterFunc(p.idleConnTTL, p.removeIdleConns)
} else {
p.timer.Reset(p.idleConnTTL)
}

nit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants