Browse code

avoid re-reading json files when copying containers

Signed-off-by: Fabio Kung <fabio.kung@gmail.com>

Fabio Kung authored on 2017/04/07 02:43:10
Showing 4 changed files
... ...
@@ -1,6 +1,7 @@
1 1
 package container
2 2
 
3 3
 import (
4
+	"bytes"
4 5
 	"encoding/json"
5 6
 	"fmt"
6 7
 	"io"
... ...
@@ -14,8 +15,6 @@ import (
14 14
 	"syscall"
15 15
 	"time"
16 16
 
17
-	"golang.org/x/net/context"
18
-
19 17
 	"github.com/Sirupsen/logrus"
20 18
 	containertypes "github.com/docker/docker/api/types/container"
21 19
 	mounttypes "github.com/docker/docker/api/types/mount"
... ...
@@ -45,6 +44,7 @@ import (
45 45
 	"github.com/docker/libnetwork/options"
46 46
 	"github.com/docker/libnetwork/types"
47 47
 	agentexec "github.com/docker/swarmkit/agent/exec"
48
+	"golang.org/x/net/context"
48 49
 )
49 50
 
50 51
 const configFileName = "config.v2.json"
... ...
@@ -154,36 +154,48 @@ func (container *Container) FromDisk() error {
154 154
 	return container.readHostConfig()
155 155
 }
156 156
 
157
-// ToDisk saves the container configuration on disk.
158
-func (container *Container) toDisk() error {
157
+// toDisk saves the container configuration on disk and returns a deep copy.
158
+func (container *Container) toDisk() (*Container, error) {
159
+	var (
160
+		buf      bytes.Buffer
161
+		deepCopy Container
162
+	)
159 163
 	pth, err := container.ConfigPath()
160 164
 	if err != nil {
161
-		return err
165
+		return nil, err
162 166
 	}
163 167
 
164
-	jsonSource, err := ioutils.NewAtomicFileWriter(pth, 0644)
168
+	// Save container settings
169
+	f, err := ioutils.NewAtomicFileWriter(pth, 0644)
165 170
 	if err != nil {
166
-		return err
171
+		return nil, err
167 172
 	}
168
-	defer jsonSource.Close()
173
+	defer f.Close()
169 174
 
170
-	enc := json.NewEncoder(jsonSource)
175
+	w := io.MultiWriter(&buf, f)
176
+	if err := json.NewEncoder(w).Encode(container); err != nil {
177
+		return nil, err
178
+	}
171 179
 
172
-	// Save container settings
173
-	if err := enc.Encode(container); err != nil {
174
-		return err
180
+	if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil {
181
+		return nil, err
182
+	}
183
+	deepCopy.HostConfig, err = container.WriteHostConfig()
184
+	if err != nil {
185
+		return nil, err
175 186
 	}
176 187
 
177
-	return container.WriteHostConfig()
188
+	return &deepCopy, nil
178 189
 }
179 190
 
180 191
 // CheckpointTo makes the Container's current state visible to queries, and persists state.
181 192
 // Callers must hold a Container lock.
182 193
 func (container *Container) CheckpointTo(store ViewDB) error {
183
-	if err := container.toDisk(); err != nil {
194
+	deepCopy, err := container.toDisk()
195
+	if err != nil {
184 196
 		return err
185 197
 	}
186
-	return store.Save(container)
198
+	return store.Save(deepCopy)
187 199
 }
188 200
 
189 201
 // readHostConfig reads the host configuration from disk for the container.
... ...
@@ -215,20 +227,34 @@ func (container *Container) readHostConfig() error {
215 215
 	return nil
216 216
 }
217 217
 
218
-// WriteHostConfig saves the host configuration on disk for the container.
219
-func (container *Container) WriteHostConfig() error {
218
+// WriteHostConfig saves the host configuration on disk for the container,
219
+// and returns a deep copy of the saved object. Callers must hold a Container lock.
220
+func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error) {
221
+	var (
222
+		buf      bytes.Buffer
223
+		deepCopy containertypes.HostConfig
224
+	)
225
+
220 226
 	pth, err := container.HostConfigPath()
221 227
 	if err != nil {
222
-		return err
228
+		return nil, err
223 229
 	}
224 230
 
225 231
 	f, err := ioutils.NewAtomicFileWriter(pth, 0644)
226 232
 	if err != nil {
227
-		return err
233
+		return nil, err
228 234
 	}
229 235
 	defer f.Close()
230 236
 
231
-	return json.NewEncoder(f).Encode(&container.HostConfig)
237
+	w := io.MultiWriter(&buf, f)
238
+	if err := json.NewEncoder(w).Encode(&container.HostConfig); err != nil {
239
+		return nil, err
240
+	}
241
+
242
+	if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil {
243
+		return nil, err
244
+	}
245
+	return &deepCopy, nil
232 246
 }
233 247
 
234 248
 // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
... ...
@@ -90,16 +90,12 @@ func (db *memDB) Snapshot(index *registrar.Registrar) View {
90 90
 	}
91 91
 }
92 92
 
93
-// Save atomically updates the in-memory store from the current on-disk state of a Container.
93
+// Save atomically updates the in-memory store state for a Container.
94
+// Only read only (deep) copies of containers may be passed in.
94 95
 func (db *memDB) Save(c *Container) error {
95 96
 	txn := db.store.Txn(true)
96 97
 	defer txn.Commit()
97
-	deepCopy := NewBaseContainer(c.ID, c.Root)
98
-	err := deepCopy.FromDisk()
99
-	if err != nil {
100
-		return err
101
-	}
102
-	return txn.Insert(memdbTable, deepCopy)
98
+	return txn.Insert(memdbTable, c)
103 99
 }
104 100
 
105 101
 // Delete removes an item by ID
... ...
@@ -579,7 +579,7 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
579 579
 
580 580
 	}
581 581
 
582
-	if err := container.WriteHostConfig(); err != nil {
582
+	if _, err := container.WriteHostConfig(); err != nil {
583 583
 		return err
584 584
 	}
585 585
 	networkActions.WithValues("allocate").UpdateSince(start)
... ...
@@ -1146,7 +1146,8 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig *
1146 1146
 
1147 1147
 	// After we load all the links into the daemon
1148 1148
 	// set them to nil on the hostconfig
1149
-	return container.WriteHostConfig()
1149
+	_, err := container.WriteHostConfig()
1150
+	return err
1150 1151
 }
1151 1152
 
1152 1153
 // conditionalMountOnStart is a platform specific helper function during the