Browse code

container: Abort transactions when memdb calls fail

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2017/07/11 02:05:14
Showing 2 changed files
... ...
@@ -66,7 +66,7 @@ type ViewDB interface {
66 66
 	Delete(*Container) error
67 67
 
68 68
 	ReserveName(name, containerID string) error
69
-	ReleaseName(name string)
69
+	ReleaseName(name string) error
70 70
 }
71 71
 
72 72
 // View can be used by readers to avoid locking
... ...
@@ -150,28 +150,20 @@ func (db *memDB) Save(c *Container) error {
150 150
 // Delete removes an item by ID
151 151
 func (db *memDB) Delete(c *Container) error {
152 152
 	txn := db.store.Txn(true)
153
-	defer txn.Commit()
154
-
155
-	// Delete any names referencing this container's ID.
156
-	iter, err := txn.Get(memdbNamesTable, memdbContainerIDIndex, c.ID)
157
-	if err != nil {
158
-		return err
159
-	}
160 153
 
161
-	var names []string
162
-	for {
163
-		item := iter.Next()
164
-		if item == nil {
165
-			break
166
-		}
167
-		names = append(names, item.(nameAssociation).name)
168
-	}
154
+	view := &memdbView{txn: txn}
155
+	names := view.getNames(c.ID)
169 156
 
170 157
 	for _, name := range names {
171 158
 		txn.Delete(memdbNamesTable, nameAssociation{name: name})
172 159
 	}
173 160
 
174
-	return txn.Delete(memdbContainersTable, NewBaseContainer(c.ID, c.Root))
161
+	if err := txn.Delete(memdbContainersTable, NewBaseContainer(c.ID, c.Root)); err != nil {
162
+		txn.Abort()
163
+		return err
164
+	}
165
+	txn.Commit()
166
+	return nil
175 167
 }
176 168
 
177 169
 // ReserveName registers a container ID to a name
... ...
@@ -180,29 +172,38 @@ func (db *memDB) Delete(c *Container) error {
180 180
 // A name reservation is globally unique
181 181
 func (db *memDB) ReserveName(name, containerID string) error {
182 182
 	txn := db.store.Txn(true)
183
-	defer txn.Commit()
184 183
 
185 184
 	s, err := txn.First(memdbNamesTable, memdbIDIndex, name)
186 185
 	if err != nil {
186
+		txn.Abort()
187 187
 		return err
188 188
 	}
189 189
 	if s != nil {
190
+		txn.Abort()
190 191
 		if s.(nameAssociation).containerID != containerID {
191 192
 			return ErrNameReserved
192 193
 		}
193 194
 		return nil
194 195
 	}
195 196
 
196
-	txn.Insert(memdbNamesTable, nameAssociation{name: name, containerID: containerID})
197
+	if err := txn.Insert(memdbNamesTable, nameAssociation{name: name, containerID: containerID}); err != nil {
198
+		txn.Abort()
199
+		return err
200
+	}
201
+	txn.Commit()
197 202
 	return nil
198 203
 }
199 204
 
200 205
 // ReleaseName releases the reserved name
201 206
 // Once released, a name can be reserved again
202
-func (db *memDB) ReleaseName(name string) {
207
+func (db *memDB) ReleaseName(name string) error {
203 208
 	txn := db.store.Txn(true)
204
-	txn.Delete(memdbNamesTable, nameAssociation{name: name})
209
+	if err := txn.Delete(memdbNamesTable, nameAssociation{name: name}); err != nil {
210
+		txn.Abort()
211
+		return err
212
+	}
205 213
 	txn.Commit()
214
+	return nil
206 215
 }
207 216
 
208 217
 type memdbView struct {
... ...
@@ -114,7 +114,7 @@ func TestNames(t *testing.T) {
114 114
 	assert.EqualError(t, db.ReserveName("name2", "containerid3"), ErrNameReserved.Error())
115 115
 
116 116
 	// Releasing a name allows the name to point to something else later.
117
-	db.ReleaseName("name2")
117
+	assert.NoError(t, db.ReleaseName("name2"))
118 118
 	assert.NoError(t, db.ReserveName("name2", "containerid3"))
119 119
 
120 120
 	view := db.Snapshot()
... ...
@@ -131,7 +131,7 @@ func TestNames(t *testing.T) {
131 131
 	assert.EqualError(t, err, ErrNameNotReserved.Error())
132 132
 
133 133
 	// Releasing and re-reserving a name doesn't affect the snapshot.
134
-	db.ReleaseName("name2")
134
+	assert.NoError(t, db.ReleaseName("name2"))
135 135
 	assert.NoError(t, db.ReserveName("name2", "containerid4"))
136 136
 
137 137
 	id, err = view.GetID("name1")