Skip to content

Commit eed4c7b

Browse files
committed
keep a consistent view of containers rendered
Replicate relevant mutations to the in-memory ACID store. Readers will then be able to query container state without locking. Signed-off-by: Fabio Kung <[email protected]>
1 parent 054728b commit eed4c7b

13 files changed

Lines changed: 107 additions & 22 deletions

container/container_unix.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,8 @@ func (container *Container) ConfigMounts() []Mount {
262262
return mounts
263263
}
264264

265-
// UpdateContainer updates configuration of a container.
265+
// UpdateContainer updates configuration of a container. Callers must hold a Lock on the Container.
266266
func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
267-
container.Lock()
268-
defer container.Unlock()
269-
270267
// update resources of container
271268
resources := hostConfig.Resources
272269
cResources := &container.HostConfig.Resources

container/container_windows.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,8 @@ func (container *Container) TmpfsMounts() ([]Mount, error) {
126126
return mounts, nil
127127
}
128128

129-
// UpdateContainer updates configuration of a container
129+
// UpdateContainer updates configuration of a container. Callers must hold a Lock on the Container.
130130
func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfig) error {
131-
container.Lock()
132-
defer container.Unlock()
133-
134131
resources := hostConfig.Resources
135132
if resources.CPUShares != 0 ||
136133
resources.Memory != 0 ||

daemon/container.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,22 @@ func (daemon *Daemon) load(id string) (*container.Container, error) {
9999
}
100100

101101
// Register makes a container object usable by the daemon as <container.ID>
102-
func (daemon *Daemon) Register(c *container.Container) {
102+
func (daemon *Daemon) Register(c *container.Container) error {
103103
// Attach to stdout and stderr
104104
if c.Config.OpenStdin {
105105
c.StreamConfig.NewInputPipes()
106106
} else {
107107
c.StreamConfig.NewNopInputPipe()
108108
}
109109

110+
// once in the memory store it is visible to other goroutines
111+
// grab a Lock until it has been replicated to avoid races
112+
c.Lock()
113+
defer c.Unlock()
114+
110115
daemon.containers.Add(c.ID, c)
111116
daemon.idIndex.Add(c.ID)
117+
return daemon.containersReplica.Save(c.Snapshot())
112118
}
113119

114120
func (daemon *Daemon) newContainer(name string, platform string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) {
@@ -212,6 +218,9 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *
212218

213219
runconfig.SetDefaultNetModeIfBlank(hostConfig)
214220
container.HostConfig = hostConfig
221+
if err := daemon.containersReplica.Save(container.Snapshot()); err != nil {
222+
return err
223+
}
215224
return container.ToDisk()
216225
}
217226

daemon/container_operations.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ func (daemon *Daemon) getDNSSearchSettings(container *container.Container) []str
4444

4545
return nil
4646
}
47+
48+
func (daemon *Daemon) saveAndReplicate(container *container.Container) error {
49+
container.Lock()
50+
defer container.Unlock()
51+
if err := daemon.containersReplica.Save(container.Snapshot()); err != nil {
52+
return fmt.Errorf("Error replicating container state: %v", err)
53+
}
54+
if err := container.ToDisk(); err != nil {
55+
return fmt.Errorf("Error saving container to disk: %v", err)
56+
}
57+
return nil
58+
}
59+
4760
func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]libnetwork.SandboxOption, error) {
4861
var (
4962
sboxOptions []libnetwork.SandboxOption
@@ -1005,7 +1018,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
10051018
return err
10061019
}
10071020
}
1008-
if err := container.ToDisk(); err != nil {
1021+
if err := daemon.saveAndReplicate(container); err != nil {
10091022
return fmt.Errorf("Error saving container to disk: %v", err)
10101023
}
10111024
return nil
@@ -1044,16 +1057,16 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, netw
10441057
return err
10451058
}
10461059

1047-
if err := container.ToDisk(); err != nil {
1060+
if err := daemon.saveAndReplicate(container); err != nil {
10481061
return fmt.Errorf("Error saving container to disk: %v", err)
10491062
}
10501063

10511064
if n != nil {
1052-
attributes := map[string]string{
1065+
daemon.LogNetworkEventWithAttributes(n, "disconnect", map[string]string{
10531066
"container": container.ID,
1054-
}
1055-
daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes)
1067+
})
10561068
}
1069+
10571070
return nil
10581071
}
10591072

daemon/create.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
172172
logrus.Errorf("Error saving new container to disk: %v", err)
173173
return nil, err
174174
}
175-
daemon.Register(container)
175+
if err := daemon.Register(container); err != nil {
176+
return nil, err
177+
}
176178
stateCtr.set(container.ID, "stopped")
177179
daemon.LogContainerEvent(container, "create")
178180
return container, nil

daemon/daemon.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ type Daemon struct {
8383
ID string
8484
repository string
8585
containers container.Store
86+
containersReplica *container.MemDB
8687
execCommands *exec.Store
8788
downloadManager *xfer.LayerDownloadManager
8889
uploadManager *xfer.LayerUploadManager
@@ -182,11 +183,15 @@ func (daemon *Daemon) restore() error {
182183
activeSandboxes := make(map[string]interface{})
183184
for id, c := range containers {
184185
if err := daemon.registerName(c); err != nil {
186+
logrus.Errorf("Failed to register container name %s: %s", c.ID, err)
187+
delete(containers, id)
188+
continue
189+
}
190+
if err := daemon.Register(c); err != nil {
185191
logrus.Errorf("Failed to register container %s: %s", c.ID, err)
186192
delete(containers, id)
187193
continue
188194
}
189-
daemon.Register(c)
190195

191196
// verify that all volumes valid and have been migrated from the pre-1.7 layout
192197
if err := daemon.verifyVolumesInfo(c); err != nil {
@@ -757,6 +762,9 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
757762
d.ID = trustKey.PublicKey().KeyID()
758763
d.repository = daemonRepo
759764
d.containers = container.NewMemoryStore()
765+
if d.containersReplica, err = container.NewMemDB(); err != nil {
766+
return nil, err
767+
}
760768
d.execCommands = exec.NewStore()
761769
d.trustKey = trustKey
762770
d.idIndex = truncindex.NewTruncIndex([]string{})

daemon/delete.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,20 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
103103
}
104104

105105
// Mark container dead. We don't want anybody to be restarting it.
106-
container.SetDead()
106+
container.Lock()
107+
container.Dead = true
108+
if err = daemon.containersReplica.Save(container.Snapshot()); err != nil {
109+
container.Unlock()
110+
return err
111+
}
107112

108113
// Save container state to disk. So that if error happens before
109114
// container meta file got removed from disk, then a restart of
110115
// docker should not make a dead container alive.
111-
if err := container.ToDiskLocking(); err != nil && !os.IsNotExist(err) {
116+
if err := container.ToDisk(); err != nil && !os.IsNotExist(err) {
112117
logrus.Errorf("Error saving dying container to disk: %v", err)
113118
}
119+
container.Unlock()
114120

115121
// When container creation fails and `RWLayer` has not been created yet, we
116122
// do not call `ReleaseRWLayer`
@@ -131,6 +137,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
131137
selinuxFreeLxcContexts(container.ProcessLabel)
132138
daemon.idIndex.Delete(container.ID)
133139
daemon.containers.Delete(container.ID)
140+
daemon.containersReplica.Delete(container.ID)
134141
if e := daemon.removeMountPoints(container, removeVolume); e != nil {
135142
logrus.Error(e)
136143
}

daemon/health.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
167167
// Else we're starting or healthy. Stay in that state.
168168
}
169169

170+
// replicate Health status changes
171+
if err := d.containersReplica.Save(c.Snapshot()); err != nil {
172+
// queries will be inconsistent until the next probe runs or other state mutations
173+
// trigger a replication
174+
logrus.Errorf("Error replicating health state for container %s: %v", c.ID, err)
175+
}
176+
170177
if oldStatus != h.Status {
171178
d.LogContainerEvent(c, "health_status: "+h.Status)
172179
}

daemon/health_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ func TestNoneHealthcheck(t *testing.T) {
2929
},
3030
State: &container.State{},
3131
}
32-
daemon := &Daemon{}
32+
store, err := container.NewMemDB()
33+
if err != nil {
34+
t.Fatal(err)
35+
}
36+
daemon := &Daemon{
37+
containersReplica: store,
38+
}
3339

3440
daemon.initHealthMonitor(c)
3541
if c.State.Health != nil {
@@ -62,8 +68,15 @@ func TestHealthStates(t *testing.T) {
6268
Image: "image_name",
6369
},
6470
}
71+
72+
store, err := container.NewMemDB()
73+
if err != nil {
74+
t.Fatal(err)
75+
}
76+
6577
daemon := &Daemon{
66-
EventsService: e,
78+
EventsService: e,
79+
containersReplica: store,
6780
}
6881

6982
c.Config.Healthcheck = &containertypes.HealthConfig{

daemon/monitor.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
9090
daemon.setStateCounter(c)
9191

9292
defer c.Unlock()
93+
if err := daemon.containersReplica.Save(c.Snapshot()); err != nil {
94+
return err
95+
}
9396
if err := c.ToDisk(); err != nil {
9497
return err
9598
}
@@ -119,6 +122,10 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
119122
c.HasBeenStartedBefore = true
120123
daemon.setStateCounter(c)
121124

125+
if err := daemon.containersReplica.Save(c.Snapshot()); err != nil {
126+
c.Reset(false)
127+
return err
128+
}
122129
if err := c.ToDisk(); err != nil {
123130
c.Reset(false)
124131
return err
@@ -130,6 +137,9 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
130137
// Container is already locked in this case
131138
c.Paused = true
132139
daemon.setStateCounter(c)
140+
if err := daemon.containersReplica.Save(c.Snapshot()); err != nil {
141+
return err
142+
}
133143
if err := c.ToDisk(); err != nil {
134144
return err
135145
}
@@ -139,6 +149,9 @@ func (daemon *Daemon) StateChanged(id string, e libcontainerd.StateInfo) error {
139149
// Container is already locked in this case
140150
c.Paused = false
141151
daemon.setStateCounter(c)
152+
if err := daemon.containersReplica.Save(c.Snapshot()); err != nil {
153+
return err
154+
}
142155
if err := c.ToDisk(); err != nil {
143156
return err
144157
}

0 commit comments

Comments
 (0)