-
Notifications
You must be signed in to change notification settings - Fork 56
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
fix(logger): Make MockLogger threadsafe #441
Conversation
As an additional datapoint, snapd had the same issue, but they solved it in a slightly different way: |
In the abstract I think I prefer the direction you're going, however, I'd like to stay close to snapd for common things where possible. Can we pick their approach here instead? What would the diff (especially test diff) look like with that approach? |
It's of course possible to add and use https://github.com/search?q=repo%3Asnapcore%2Fsnapd%20logbuf.String&type=code vs https://github.com/search?q=repo%3Asnapcore%2Fsnapd+WithLoggerLock&type=code Trying to log anything in the function passed to logger.WithLoggerLock(func () {
for i := 0; i < 10; i++ {
logger.Noticef(s.logbuf.String())
}
}) Will have to be split up like this: for i := 0; i < 10; i++ {
var str string
logger.WithLoggerLock(func () {
str = s.logbuf.String()
})
logger.Noticef(str)
} Expressions like I haven't measured what the overhead of locking and unlocking an uncontested mutex is in cases where there's no background goroutine when accessing cc @jhenstridge and @pedronis from the snapd commit canonical/snapd@16c3680b78d for additional input on the design of |
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.
Okay, I agree with your reasoning above -- let's merge this. If you want to convince the snapd folks yours is better than their opt-in-to-safety and get this merged there, go ahead. :-)
Done here now (slightly different due to snapd's requirement of a In case this gets merged in snapd upstream, it might make sense to sync back the changes ( |
When using a
logger.MockLogger()
, a background goroutine callinglogger.Noticef()
might race the underlyingbytes.Buffer
for read/write access. To avoid that, limit the returned buffer type forMockLogger()
to justfmt.Stringer
(and mutex-protect its reading from the buffer) as well as mutex-protectWrite()
calls to the underlying buffer.To reproduce the race condition, run
go test -race ./internals/logger
withTestMockLoggerReadWriteThreadsafe
added, but with the unmodifiedMockLogger
implementation.If more functions from
bytes.Buffer
are needed from theMockLogger
return value, the interface can be expanded with additional pass-through lock-protected calls, but in all of Pebble, it seems only the.String()
function is used, so only that is exposed for now.While
logger.Noticef()
calls are serialized bylogger.loggerLock
, the same cannot be said for parallel access to the underlyingbytes.Buffer
that is returned fromMockLogger
when callinglogger.Noticef()
in one goroutine, and accessing thebytes.Buffer
from another goroutine.