Skip to content

daemon/disk_usage: Use context aware singleflight#44520

Merged
thaJeztah merged 1 commit intomoby:masterfrom
vvoland:disk-usage-singleflight
Nov 30, 2022
Merged

daemon/disk_usage: Use context aware singleflight#44520
thaJeztah merged 1 commit intomoby:masterfrom
vvoland:disk-usage-singleflight

Conversation

@vvoland
Copy link
Copy Markdown
Contributor

@vvoland vvoland commented Nov 24, 2022

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).

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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

^^ 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.

@vvoland vvoland requested review from corhere and thaJeztah and removed request for corhere November 24, 2022 13:00
Copy link
Copy Markdown
Contributor

@corhere corhere left a comment

Choose a reason for hiding this comment

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

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"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

@vvoland vvoland Nov 29, 2022

Choose a reason for hiding this comment

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

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>
@vvoland vvoland force-pushed the disk-usage-singleflight branch from acb32da to dec81e4 Compare November 29, 2022 15:47
Copy link
Copy Markdown
Contributor

@corhere corhere left a comment

Choose a reason for hiding this comment

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

LGTM!

@thaJeztah
Copy link
Copy Markdown
Member

Failure looks unrelated, but I don't recall seeing it before, so let me put it in a comment for posterity;

=== FAIL: github.com/docker/docker/integration-cli TestDockerAPISuite/TestContainersAPICreateMountsCreate/0_config:_{volume__c:\foo_false__<nil>_<nil>_<nil>_<nil>} (24.07s)
    docker_api_containers_test.go:2137: timeout hit after 10s: first check never completed
        --- FAIL: TestDockerAPISuite/TestContainersAPICreateMountsCreate/0_config:_{volume__c:\foo_false__<nil>_<nil>_<nil>_<nil>} (24.07s)
723

@thaJeztah thaJeztah added status/2-code-review containerd-integration Issues and PRs related to containerd integration labels Nov 30, 2022
@thaJeztah thaJeztah added this to the v-next milestone Nov 30, 2022
Copy link
Copy Markdown
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks

Comment on lines +42 to 44
if err != nil {
return nil, errors.Wrap(err, "failed to retrieve image list")
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something for a follow up;

  • we seem to be inconsistent in wrapping these errors (and not); e.g. localVolumesSize below doesn't wrap the error
  • perhaps the wrapping should be handled in daemon.imageService.Images itself? (it looks like some specific errors are returned, and not all of them currently have a proper errdefs type)
  • Wondering if the wrapping should be done inside the daemon.usageImages.Do or outside (at the exit-point of imageDiskUsage - 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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

^^ 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.

Comment on lines +109 to +112
usageContainers singleflight.Group[struct{}, []*types.Container]
usageImages singleflight.Group[struct{}, []*types.ImageSummary]
usageVolumes singleflight.Group[struct{}, []*volume.Volume]
usageLayer singleflight.Group[struct{}, int64]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 👍

Comment on lines +54 to +55
volumes, _, err := daemon.usageVolumes.Do(ctx, struct{}{}, func(ctx context.Context) ([]*volume.Volume, error) {
volumes, err := daemon.volumes.LocalVolumesSize(ctx)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

containerd-integration Issues and PRs related to containerd integration status/4-merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants