Browse code

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 <crosbymichael@gmail.com>

Michael Crosby authored on 2015/07/09 03:13:47
Showing 2 changed files
... ...
@@ -731,6 +731,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
731 731
 	d.RegistryService = registryService
732 732
 	d.EventsService = eventsService
733 733
 	d.root = config.Root
734
+	go d.execCommandGC()
734 735
 
735 736
 	if err := d.restore(); err != nil {
736 737
 		return nil, err
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"io/ioutil"
7 7
 	"strings"
8 8
 	"sync"
9
+	"time"
9 10
 
10 11
 	"github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/daemon/execdriver"
... ...
@@ -247,3 +248,34 @@ func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pi
247 247
 
248 248
 	return exitStatus, err
249 249
 }
250
+
251
+// execCommandGC runs a ticker to clean up the daemon references
252
+// of exec configs that are no longer part of the container.
253
+func (d *Daemon) execCommandGC() {
254
+	for range time.Tick(5 * time.Minute) {
255
+		var (
256
+			cleaned          int
257
+			liveExecCommands = d.containerExecIds()
258
+			ids              = d.execCommands.List()
259
+		)
260
+		for _, id := range ids {
261
+			if _, exists := liveExecCommands[id]; !exists {
262
+				cleaned++
263
+				d.execCommands.Delete(id)
264
+			}
265
+		}
266
+		logrus.Debugf("clean %d unused exec commands", cleaned)
267
+	}
268
+}
269
+
270
+// containerExecIds returns a list of all the current exec ids that are in use
271
+// and running inside a container.
272
+func (d *Daemon) containerExecIds() map[string]struct{} {
273
+	ids := map[string]struct{}{}
274
+	for _, c := range d.containers.List() {
275
+		for _, id := range c.execCommands.List() {
276
+			ids[id] = struct{}{}
277
+		}
278
+	}
279
+	return ids
280
+}