Skip to content

Commit 9e82212

Browse files
mtrmacruncom
authored andcommitted
Don't fail on two concurrent reference.store.AddDigest calls
reference.store.addReference fails when adding a digest reference that already exists (regardless of the reference target). Both callers (via reference.store.AddDigest) do check in advance, using reference.store.Get, whether the digest reference exists before calling AddDigest, but the reference store lock is released between the two calls, so if another thread sets the reference in the meantime, AddDigest may fail with > Cannot overwrite digest ... . Handle this by checking that the pre-existing reference points at the same image, i.e. that there is nothing to do, and succeeding immediately in that case. This is even cheaper, avoids a reference.store.save() call. (In principle, the same failure could have happened via reference.store.AddTag, as > Conflict: Tag %s is already set to image %s, if you want to replace it, please use -f option but almost all callers (except for migrate/v1.Migrate, which is run single-threaded anyway) set the "force" parameter of AddTag to true, which makes the race invisible. This commit does not change the behavior of that case, except for speeding it up by avoiding the reference.store.save() call.) The existing reference.store.Get checks are now, in a sense, redundant as such, but their existence allows the callers to provide nice context-dependent error messages, so this commit leaves them unchanged. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
1 parent 0378675 commit 9e82212

2 files changed

Lines changed: 13 additions & 0 deletions

File tree

reference/store.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ func (store *store) addReference(ref Named, id digest.Digest, force bool) error
128128
oldID, exists := repository[refStr]
129129

130130
if exists {
131+
if oldID == id {
132+
// Nothing to do. The caller may have checked for this using store.Get in advance, but store.mu was unlocked in the meantime, so this can legitimately happen nevertheless.
133+
return nil
134+
}
135+
131136
// force only works for tags
132137
if digested, isDigest := ref.(Canonical); isDigest {
133138
return fmt.Errorf("Cannot overwrite digest %s", digested.Digest().String())

reference/store_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ func TestAddDeleteGet(t *testing.T) {
160160
if err = store.AddTag(ref4, testImageID2, false); err != nil {
161161
t.Fatalf("error adding to store: %v", err)
162162
}
163+
// Write the same values again; should silently succeed
164+
if err = store.AddTag(ref4, testImageID2, false); err != nil {
165+
t.Fatalf("error redundantly adding to store: %v", err)
166+
}
163167

164168
ref5, err := ParseNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c")
165169
if err != nil {
@@ -168,6 +172,10 @@ func TestAddDeleteGet(t *testing.T) {
168172
if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
169173
t.Fatalf("error adding to store: %v", err)
170174
}
175+
// Write the same values again; should silently succeed
176+
if err = store.AddDigest(ref5.(Canonical), testImageID2, false); err != nil {
177+
t.Fatalf("error redundantly adding to store: %v", err)
178+
}
171179

172180
// Attempt to overwrite with force == false
173181
if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") {

0 commit comments

Comments
 (0)