-
Notifications
You must be signed in to change notification settings - Fork 882
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
GODRIVER-2778 Reduce memory usage on compressed loads. #1200
GODRIVER-2778 Reduce memory usage on compressed loads. #1200
Conversation
7a59d89
to
c7ff6fc
Compare
c7ff6fc
to
51f9261
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really good, just a few considerations.
x/mongo/driver/compression.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
defer r.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defer r.Close() | |
defer func() { | |
if err := r.Close(); err != nil { | |
panic(err) | |
} | |
}() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DecompressPayload
returns an error, so we should return the error instead of panicking.
E.g.
// ...
_, err = io.ReadFull(r, uncompressed)
if err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
return uncompressed, nil
// ...
Note that calling Close
on the reader is probably redundant because it basically returns the last seen error (see Close
code here and here), so io.ReadFull
will already have encountered that error. We can include it for completeness, or a comment that describes that we never expect Close
to return a new error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've modified it to use a named return value to return the error from Close
in defer.
x/mongo/driver/compression.go
Outdated
type zlibEncoder struct { | ||
l sync.Locker | ||
w *zlib.Writer | ||
b *bytes.Buffer | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use more readable field names like "mu", "writer" and "buf"?
x/mongo/driver/compression.go
Outdated
@@ -41,6 +40,50 @@ func getZstdEncoder(l zstd.EncoderLevel) (*zstd.Encoder, error) { | |||
return encoder, nil | |||
} | |||
|
|||
var zlibEncoders = &sync.Map{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC the pointer to this map is unecessary.
var zlibEncoders = &sync.Map{} | |
var zlibEncoders = sync.Map |
x/mongo/driver/compression.go
Outdated
if v, ok := zlibEncoders.Load(l); ok { | ||
return v.(*zlibEncoder), nil | ||
} | ||
b := bytes.NewBuffer(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional way of constructing a new bytes.Buffer without the awkward "nil" initializer.
b := bytes.NewBuffer(nil) | |
b := new(bytes.Buffer) |
x/mongo/driver/compression.go
Outdated
defer func() { | ||
e.b.Reset() | ||
e.w.Reset(e.b) | ||
}() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are your thoughts on removing the defer functoin here and executing the "Reset" methods at the top of the calls to "Encode". This seems marginally safer.
@@ -1602,25 +1599,10 @@ func (Operation) canCompress(cmd string) bool { | |||
// includesHeader: specifies whether or not wm includes the message header | |||
// Returns the decoded OP_REPLY. If the err field of the returned opReply is non-nil, an error occurred while decoding | |||
// or validating the response and the other fields are undefined. | |||
func (Operation) decodeOpReply(wm []byte, includesHeader bool) opReply { | |||
func (Operation) decodeOpReply(wm []byte) opReply { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: OP_MSG
is supported in MongoDB server version 3.6+: https://github.com/mongodb/specifications/blob/master/source/wireversion-featurelist.rst
I think OP_REPLY
is only used for the initial command to a server until the wire version is discovered here. The Go driver requires server version 3.6+. I expect decompression for OP_REPLY
is not commonly executed.
No need to change.
x/mongo/driver/operation.go
Outdated
@@ -1602,25 +1599,10 @@ func (Operation) canCompress(cmd string) bool { | |||
// includesHeader: specifies whether or not wm includes the message header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove comment for includesHeader
.
x/mongo/driver/compression.go
Outdated
if err != nil { | ||
return nil, err | ||
} | ||
defer r.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DecompressPayload
returns an error, so we should return the error instead of panicking.
E.g.
// ...
_, err = io.ReadFull(r, uncompressed)
if err != nil {
return nil, err
}
if err := r.Close(); err != nil {
return nil, err
}
return uncompressed, nil
// ...
Note that calling Close
on the reader is probably redundant because it basically returns the last seen error (see Close
code here and here), so io.ReadFull
will already have encountered that error. We can include it for completeness, or a comment that describes that we never expect Close
to return a new error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
GODRIVER-2778
Summary
Operation.decompressWireMessage
to eliminate duplicate memory allocation;zlib.Writer
;ReadWireMessage
in the interface ofdriver.Connection
.Background & Motivation
Memory usage of "ClientRead/snappy" shows a ~20% reduction because we have already allocated memory for the uncompressed wiremessage (code).