daemon/disk_usage: Use context aware singleflight#44520
daemon/disk_usage: Use context aware singleflight#44520thaJeztah merged 1 commit intomoby:masterfrom
Conversation
| google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 | ||
| google.golang.org/grpc v1.48.0 | ||
| gotest.tools/v3 v3.4.0 | ||
| resenje.org/singleflight v0.3.0 |
There was a problem hiding this comment.
Are we fine with vendoring this dependency? It's not popular but it's small and I don't see anything widely used that could be used instead.
There was a problem hiding this comment.
^^ for posterity; we briefly discussed this, and I think it's ok to vendor it for now. Looks like the module was recently updated (so actively maintained). We can revisit that in future if there's concerns or if we decide to implement it ourselves.
corhere
left a comment
There was a problem hiding this comment.
singleflight calls were also moved up from the ImageService to Daemon. This allows to avoid duplicating this behaviour in every ImageService implementation (with containerd integration, we have two now).
💯 Great call! In addition to DRYing the implementations, this change is also architecturally sound IMO.
daemon/daemon.go
Outdated
| "google.golang.org/grpc" | ||
| "google.golang.org/grpc/backoff" | ||
| "google.golang.org/grpc/credentials/insecure" | ||
| ctxsingleflight "resenje.org/singleflight" |
There was a problem hiding this comment.
There was a problem hiding this comment.
Yeah I wasn't sure about it. My intention here was to make it explicitly visible that this isn't the singleflight from go/x/sync.
On the other hand, it still does the same thing but with context handling, so it's probably fine not to disambiguate them.
The singleflight function was capturing the context.Context of the first caller that invoked the `singleflight.Do`. This could cause all concurrent calls to be cancelled when the first request is cancelled. singleflight calls were also moved from the ImageService to Daemon, to avoid having to implement this logic in both graphdriver and containerd based image services. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
acb32da to
dec81e4
Compare
|
Failure looks unrelated, but I don't recall seeing it before, so let me put it in a comment for posterity; |
| if err != nil { | ||
| return nil, errors.Wrap(err, "failed to retrieve image list") | ||
| } |
There was a problem hiding this comment.
Something for a follow up;
- we seem to be inconsistent in wrapping these errors (and not); e.g.
localVolumesSizebelow doesn't wrap the error - perhaps the wrapping should be handled in
daemon.imageService.Imagesitself? (it looks like some specific errors are returned, and not all of them currently have a propererrdefstype) - Wondering if the wrapping should be done inside the
daemon.usageImages.Door outside (at the exit-point ofimageDiskUsage- effectively won't make a difference in this case (so mostly a nit! :joy')
| google.golang.org/genproto v0.0.0-20220706185917-7780775163c4 | ||
| google.golang.org/grpc v1.48.0 | ||
| gotest.tools/v3 v3.4.0 | ||
| resenje.org/singleflight v0.3.0 |
There was a problem hiding this comment.
^^ for posterity; we briefly discussed this, and I think it's ok to vendor it for now. Looks like the module was recently updated (so actively maintained). We can revisit that in future if there's concerns or if we decide to implement it ourselves.
| usageContainers singleflight.Group[struct{}, []*types.Container] | ||
| usageImages singleflight.Group[struct{}, []*types.ImageSummary] | ||
| usageVolumes singleflight.Group[struct{}, []*volume.Volume] | ||
| usageLayer singleflight.Group[struct{}, int64] |
There was a problem hiding this comment.
Not a blocker, and I see @corhere suggested using struct{} for this (#44520 (comment)).
When reading this code, I had to look up the implementation to understand what the first argument was for, so for readability, perhaps (in a follow-up) we should consider defining an ad-hoc type to make it slightly more descriptive / self-documenting. (or add a comment).
|
|
||
| // Group represents a class of work and forms a namespace in | ||
| // which units of work can be executed with duplicate suppression. | ||
| type Group[K comparable, V any] struct { |
There was a problem hiding this comment.
I had to double-check which version of Go added generics, but it's go1.18 (https://go.dev/blog/intro-generics), which matches the Go version we define as minimum in our vendor.mod, so we should be fine on that front 👍
| volumes, _, err := daemon.usageVolumes.Do(ctx, struct{}{}, func(ctx context.Context) ([]*volume.Volume, error) { | ||
| volumes, err := daemon.volumes.LocalVolumesSize(ctx) |
There was a problem hiding this comment.
singleflight calls were also moved up from the ImageService to Daemon. This allows to avoid duplicating this behaviour in every ImageService implementation (with containerd integration, we have two now).
LOL, even though I read that comment; I started reading the code and my first thought was: why not keep this part of daemon.volumes.LocalVolumesSize so that any consumer of it would automatically get this feature? Then recalled "ooooooh" right 🤦 🙈 🙈 🙈
Makes a lot of sense from that perspective 👍
Follow up to:
Relates to:
The singleflight function was capturing the context.Context of the first caller that invoked the
singleflight.Do. So context cancellation of the first caller could cancel the execution of other concurrent invocations, even if their context wasn't cancelled.singleflight calls were also moved up from the ImageService to Daemon. This allows to avoid duplicating this behaviour in every ImageService implementation (with containerd integration, we have two now).