perf: avoid duplicate provider metadata fetch on stop

The previous implementation fetched the provider binary path and
metadata twice per service during `compose stop`: once in stop.go
to gate on the `stop` capability, and again inside runPlugin via
setupPluginCommand.

setupPluginCommand now signals "skip" by returning (nil, nil) when
the requested command is absent from the provider's metadata.
stop.go calls runPlugin directly; the skip-when-unadvertised check
moves into runPlugin.

Addresses PR #13779 review feedback.

Signed-off-by: Guillaume Lours <[email protected]>
This commit is contained in:
Guillaume Lours
2026-05-18 11:11:38 +02:00
committed by Guillaume Lours
parent 672dc14d29
commit 19fc292eb9
3 changed files with 8 additions and 12 deletions
+2 -2
View File
@@ -111,8 +111,8 @@ The provider is responsible for releasing all resources associated with the serv
When the user runs `docker compose stop`, Compose invokes `<provider> compose --project-name <NAME> stop <SERVICE>` for each
provider-backed service in reverse dependency order. The provider should pause the resource without releasing it, so a later
`docker compose start` or `docker compose up` can resume it. Any `setenv` JSON message returned during `stop` is ignored,
since dependent services are also stopping.
`docker compose up` can resume it (note that `docker compose start` only restarts existing containers and does not invoke
provider hooks). Any `setenv` JSON message returned during `stop` is ignored, since dependent services are also stopping.
The `stop` hook is opt-in: Compose invokes it only when the provider declares a `stop` block in its `metadata` subcommand
output. Providers that do not advertise `stop` in metadata (or do not implement the `metadata` subcommand at all) are
+6 -2
View File
@@ -66,6 +66,9 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project,
if err != nil {
return err
}
if cmd == nil {
return nil
}
variables, err := s.executePlugin(cmd, command, service)
if err != nil {
@@ -188,9 +191,10 @@ func (s *composeService) setupPluginCommand(ctx context.Context, project *types.
case "down":
currentCommandMetadata = cmdOptionsMetadata.Down
case "stop":
if cmdOptionsMetadata.Stop != nil {
currentCommandMetadata = *cmdOptionsMetadata.Stop
if cmdOptionsMetadata.Stop == nil {
return nil, nil
}
currentCommandMetadata = *cmdOptionsMetadata.Stop
}
provider := *service.Provider
-8
View File
@@ -54,14 +54,6 @@ func (s *composeService) stop(ctx context.Context, projectName string, options a
}
serv := project.Services[service]
if serv.Provider != nil {
path, err := s.getPluginBinaryPath(serv.Provider.Type)
if err != nil {
return err
}
metadata := s.getPluginMetadata(path, serv.Provider.Type, project)
if metadata.Stop == nil {
return nil
}
return s.runPlugin(ctx, project, serv, "stop")
}
return s.stopContainers(ctx, &serv, containers.filter(isService(service)).filter(isNotOneOff), options.Timeout, event)