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

Explicitly Close() obj after ReadFull() #552

Merged
merged 1 commit into from
Jul 29, 2016

Conversation

benagricola
Copy link
Contributor

Restic makes heavy use of short reads from S3 when performing incremental backups. It runs GetObject from minio-go and reads part of the returned stream (e.g. 45 bytes from a specific offset) and then returns.

This leaves the HTTP connection managed by minio-go containing unread data, and in this situation the minio-go library will create a new connection for subsequent requests even though restic is finished with the previous object. Performing an incremental backup of a 7GiB filesystem I noticed connection counts from Restic to the S3 endpoint of up to 30,000 when the open file limit was raised high enough to allow this.

Explicitly calling Close() after io.ReadFull() causes the remaining data to be discarded and allows minio-go to handle connection reuse effectively. The same incremental backup described above will use on average 15 to 30 connections for the duration of the backup.

@benagricola benagricola mentioned this pull request Jul 29, 2016
@fd0
Copy link
Member

fd0 commented Jul 29, 2016

Hey, thanks for giving this a try!

// This may not read the whole object, so ensure object
// is closed to avoid duplicate connections.
n, err := io.ReadFull(obj, p)
obj.Close()
Copy link
Member

Choose a reason for hiding this comment

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

This discards the error that Close() may have returned. What do you think about returning the error from Close() in case ReadFull() did not return any error?

Signed-off-by: Ben Agricola <bagricola@squiz.co.uk>
// is closed to avoid duplicate connections.
n, err := io.ReadFull(obj, p)
if err != nil {
obj.Close()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I assume in this situation that the important part is the Read error rather than the close error.

I wonder if it's possible for a Read error to cause obj to be already-closed, in which case this would throw an error attempting to close it twice...

@benagricola
Copy link
Contributor Author

Also one thing to note, there's been a number of changes to minio-go upstream which help to reduce the concurrent connection count (also to do with connections being recreated instead of reused). Would be good to have updated minio-go pulled in.

@fd0
Copy link
Member

fd0 commented Jul 29, 2016

Yes, I agree. Thanks for your contribution!

@fd0 fd0 merged commit edb1843 into restic:master Jul 29, 2016
fd0 added a commit that referenced this pull request Jul 29, 2016
Explicitly Close() obj after ReadFull()
@fd0 fd0 mentioned this pull request Jul 29, 2016
@benagricola benagricola deleted the fix-connection-leak branch July 31, 2016 21:59
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.

None yet

2 participants