From 5f017bba48e5c763157e1b35a5edea64cc41fc6a Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 8 Jul 2015 11:13:47 -0700 Subject: [PATCH] Add GC loop to clean exec command refs on daemon This adds an event loop for running a GC cleanup for exec command references that are on the daemon. These cannot be cleaned up immediately because processes may need to get the exit status of the exec command but it should not grow out of bounds. The loop is set to a default 5 minute interval to perform cleanup. It should be safe to perform this cleanup because unless the clients are remembering the exec id of the process they launched they can query for the status and see that it has exited. If they don't save the exec id they will have to do an inspect on the container for all exec instances and anything that is not live inside that container will not be returned in the container inspect. Signed-off-by: Michael Crosby --- daemon/daemon.go | 1 + daemon/exec.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/daemon/daemon.go b/daemon/daemon.go index 549a1c188..6e4d7ba20 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -731,6 +731,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo d.RegistryService = registryService d.EventsService = eventsService d.root = config.Root + go d.execCommandGC() if err := d.restore(); err != nil { return nil, err diff --git a/daemon/exec.go b/daemon/exec.go index e1cfcd7f6..8ee7c1787 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "strings" "sync" + "time" "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" @@ -247,3 +248,34 @@ func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pi return exitStatus, err } + +// execCommandGC runs a ticker to clean up the daemon references +// of exec configs that are no longer part of the container. +func (d *Daemon) execCommandGC() { + for range time.Tick(5 * time.Minute) { + var ( + cleaned int + liveExecCommands = d.containerExecIds() + ids = d.execCommands.List() + ) + for _, id := range ids { + if _, exists := liveExecCommands[id]; !exists { + cleaned++ + d.execCommands.Delete(id) + } + } + logrus.Debugf("clean %d unused exec commands", cleaned) + } +} + +// containerExecIds returns a list of all the current exec ids that are in use +// and running inside a container. +func (d *Daemon) containerExecIds() map[string]struct{} { + ids := map[string]struct{}{} + for _, c := range d.containers.List() { + for _, id := range c.execCommands.List() { + ids[id] = struct{}{} + } + } + return ids +}