-
Notifications
You must be signed in to change notification settings - Fork 236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chunked: fix reuse of the layers cache #2024
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,7 +69,6 @@ type layersCache struct { | |
refs int | ||
store storage.Store | ||
mutex sync.RWMutex | ||
created time.Time | ||
} | ||
|
||
var ( | ||
|
@@ -83,6 +82,7 @@ func (c *layer) release() { | |
if err := unix.Munmap(c.mmapBuffer); err != nil { | ||
logrus.Warnf("Error Munmap: layer %q: %v", c.id, err) | ||
} | ||
c.mmapBuffer = nil | ||
} | ||
} | ||
|
||
|
@@ -107,14 +107,13 @@ func (c *layersCache) release() { | |
func getLayersCacheRef(store storage.Store) *layersCache { | ||
cacheMutex.Lock() | ||
defer cacheMutex.Unlock() | ||
if cache != nil && cache.store == store && time.Since(cache.created).Minutes() < 10 { | ||
if cache != nil && cache.store == store { | ||
cache.refs++ | ||
return cache | ||
} | ||
cache := &layersCache{ | ||
cache = &layersCache{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I wonder about making this kind of mistake harder to do … naming the global |
||
store: store, | ||
refs: 1, | ||
created: time.Now(), | ||
} | ||
return cache | ||
} | ||
|
@@ -781,14 +780,14 @@ func (c *layersCache) findDigestInternal(digest string) (string, string, int64, | |
return "", "", -1, nil | ||
} | ||
|
||
c.mutex.RLock() | ||
defer c.mutex.RUnlock() | ||
|
||
binaryDigest, err := makeBinaryDigest(digest) | ||
if err != nil { | ||
return "", "", 0, err | ||
} | ||
|
||
c.mutex.RLock() | ||
defer c.mutex.RUnlock() | ||
|
||
for _, layer := range c.layers { | ||
if !layer.cacheFile.bloomFilter.maybeContains(binaryDigest) { | ||
continue | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3104,12 +3104,41 @@ func (s *store) CleanupStagedLayer(diffOutput *drivers.DriverWithDifferOutput) e | |
} | ||
|
||
func (s *store) ApplyDiffWithDiffer(to string, options *drivers.ApplyDiffWithDifferOpts, differ drivers.Differ) (*drivers.DriverWithDifferOutput, error) { | ||
return writeToLayerStore(s, func(rlstore rwLayerStore) (*drivers.DriverWithDifferOutput, error) { | ||
if to != "" && !rlstore.Exists(to) { | ||
return nil, ErrLayerUnknown | ||
rlstore, err := s.getLayerStore() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var layer *Layer | ||
if to != "" { | ||
if err := rlstore.startReading(); err != nil { | ||
return nil, err | ||
} | ||
return rlstore.ApplyDiffWithDiffer(to, options, differ) | ||
}) | ||
layer, err = rlstore.Get(to) | ||
rlstore.stopReading() | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
out, err := rlstore.ApplyDiffWithDifferNoLock(layer, options, differ) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if layer != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do all the error paths from now on need some variant of |
||
if err := rlstore.startWriting(); err != nil { | ||
_ = s.CleanupStagedLayer(out) | ||
return nil, err | ||
} | ||
err = rlstore.ApplyDiffWithDifferFinalize(layer.ID, out) | ||
rlstore.stopWriting() | ||
if err != nil { | ||
_ = s.CleanupStagedLayer(out) | ||
return nil, err | ||
} | ||
} | ||
return out, nil | ||
} | ||
|
||
func (s *store) DifferTarget(id string) (string, error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think this works for pre-existing layers (
layer != nil
); if the layer is unlocked:getStagingDir
c/image only ever calls
ApplyDiffWithDiffer
without the layer ID, that’s the only code path I fully care about. (The other code path is used incmd/containers-storage
and in tests, per comments instore.go
.)It is very tempting to me to have a new specialized
PrepareStagedLayer
(to match the names of{Apply,Cleanup}StagedLayer
) that only supports this simpler care; then several of the operations here could just disappear.But
ApplyDiffWithDiffer
is now a stable API; I don’t immediately know how to deal with that. The concurrent layer deletion might be possible to handle by holding a read lock on the layer store only (assuming all operations inpkg/chunked
lock the layer store only for reading, and that’s not currently the case), but the exclusion of two concurrent callers, and of any possible concurrent readers, is fundamentally the existing exclusive layer store lock.Outright refusing to do the operation on existing layers … is code that could be written… but probably shouldn’t be merged. I don’t know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know either how to solve it, unless introducing another per-layer lock file. As you said, this code path is used only by
cmd/containers-storage
.Could we just document this behavior, and warn any potential user that using
to != ""
is not safe as there can be multiple writers, and the layer could potentially be deleted while the operation runs?