imageService: cache information about images
Cache information about images that isn't trivially read from them, so that ImageStatus and particularly ListImages don't have to do potentially-expensive things for every image that they report. The cache is an in-memory map, and we prune it after ListImages has assembled its result set. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
96fb47213e
commit
c53211eacd
1 changed files with 102 additions and 43 deletions
|
@ -47,12 +47,22 @@ type indexInfo struct {
|
||||||
secure bool
|
secure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A set of information that we prefer to cache about images, so that we can
|
||||||
|
// avoid having to reread them every time we need to return information about
|
||||||
|
// images.
|
||||||
|
type imageCacheItem struct {
|
||||||
|
user string
|
||||||
|
size *uint64
|
||||||
|
configDigest digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
type imageService struct {
|
type imageService struct {
|
||||||
store storage.Store
|
store storage.Store
|
||||||
defaultTransport string
|
defaultTransport string
|
||||||
insecureRegistryCIDRs []*net.IPNet
|
insecureRegistryCIDRs []*net.IPNet
|
||||||
indexConfigs map[string]*indexInfo
|
indexConfigs map[string]*indexInfo
|
||||||
registries []string
|
registries []string
|
||||||
|
imageCache map[string]imageCacheItem
|
||||||
}
|
}
|
||||||
|
|
||||||
// sizer knows its size.
|
// sizer knows its size.
|
||||||
|
@ -172,25 +182,38 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if image, err := istorage.Transport.GetStoreImage(svc.store, ref); err == nil {
|
if image, err := istorage.Transport.GetStoreImage(svc.store, ref); err == nil {
|
||||||
img, err := ref.NewImageSource(systemContext)
|
var user string
|
||||||
if err != nil {
|
var size *uint64
|
||||||
return nil, err
|
var configDigest digest.Digest
|
||||||
}
|
if cacheItem, ok := svc.imageCache[image.ID]; ok {
|
||||||
size := imageSize(img)
|
user, size, configDigest = cacheItem.user, cacheItem.size, cacheItem.configDigest
|
||||||
configDigest, err := imageConfigDigest(img, nil)
|
} else {
|
||||||
img.Close()
|
img, err := ref.NewImageSource(systemContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
imageFull, err := ref.NewImage(systemContext)
|
size = imageSize(img)
|
||||||
if err != nil {
|
configDigest, err = imageConfigDigest(img, nil)
|
||||||
return nil, err
|
img.Close()
|
||||||
}
|
if err != nil {
|
||||||
defer imageFull.Close()
|
return nil, err
|
||||||
|
}
|
||||||
imageConfig, err := imageFull.OCIConfig()
|
imageFull, err := ref.NewImage(systemContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
defer imageFull.Close()
|
||||||
|
imageConfig, err := imageFull.OCIConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
user = imageConfig.Config.User
|
||||||
|
cacheItem := imageCacheItem{
|
||||||
|
user: user,
|
||||||
|
size: size,
|
||||||
|
configDigest: configDigest,
|
||||||
|
}
|
||||||
|
svc.imageCache[image.ID] = cacheItem
|
||||||
}
|
}
|
||||||
name, tags, digests := sortNamesByType(image.Names)
|
name, tags, digests := sortNamesByType(image.Names)
|
||||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||||
|
@ -202,7 +225,7 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
||||||
Size: size,
|
Size: size,
|
||||||
Digest: imageDigest,
|
Digest: imageDigest,
|
||||||
ConfigDigest: configDigest,
|
ConfigDigest: configDigest,
|
||||||
User: imageConfig.Config.User,
|
User: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -210,30 +233,65 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
visited := make(map[string]struct{})
|
||||||
|
defer func() {
|
||||||
|
// We built a map using IDs of images that we looked
|
||||||
|
// at, so remove any items from the cache that don't
|
||||||
|
// correspond to any of those IDs.
|
||||||
|
removedIDs := make([]string, 0, len(svc.imageCache))
|
||||||
|
for imageID := range svc.imageCache {
|
||||||
|
if _, keep := visited[imageID]; !keep {
|
||||||
|
// We have cached data for an image
|
||||||
|
// with this ID, but it's not in the
|
||||||
|
// list of images now, so the image has
|
||||||
|
// been removed.
|
||||||
|
removedIDs = append(removedIDs, imageID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle the removals.
|
||||||
|
for _, removedID := range removedIDs {
|
||||||
|
delete(svc.imageCache, removedID)
|
||||||
|
}
|
||||||
|
}()
|
||||||
for _, image := range images {
|
for _, image := range images {
|
||||||
ref, err := istorage.Transport.ParseStoreReference(svc.store, "@"+image.ID)
|
visited[image.ID] = struct{}{}
|
||||||
if err != nil {
|
var user string
|
||||||
return nil, err
|
var size *uint64
|
||||||
}
|
var configDigest digest.Digest
|
||||||
img, err := ref.NewImageSource(systemContext)
|
if cacheItem, ok := svc.imageCache[image.ID]; ok {
|
||||||
if err != nil {
|
user, size, configDigest = cacheItem.user, cacheItem.size, cacheItem.configDigest
|
||||||
return nil, err
|
} else {
|
||||||
}
|
ref, err := istorage.Transport.ParseStoreReference(svc.store, "@"+image.ID)
|
||||||
size := imageSize(img)
|
if err != nil {
|
||||||
configDigest, err := imageConfigDigest(img, nil)
|
return nil, err
|
||||||
img.Close()
|
}
|
||||||
if err != nil {
|
img, err := ref.NewImageSource(systemContext)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
imageFull, err := ref.NewImage(systemContext)
|
}
|
||||||
if err != nil {
|
size = imageSize(img)
|
||||||
return nil, err
|
configDigest, err = imageConfigDigest(img, nil)
|
||||||
}
|
img.Close()
|
||||||
defer imageFull.Close()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageFull, err := ref.NewImage(systemContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer imageFull.Close()
|
||||||
|
|
||||||
imageConfig, err := imageFull.OCIConfig()
|
imageConfig, err := imageFull.OCIConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
user = imageConfig.Config.User
|
||||||
|
cacheItem := imageCacheItem{
|
||||||
|
user: user,
|
||||||
|
size: size,
|
||||||
|
configDigest: configDigest,
|
||||||
|
}
|
||||||
|
svc.imageCache[image.ID] = cacheItem
|
||||||
}
|
}
|
||||||
name, tags, digests := sortNamesByType(image.Names)
|
name, tags, digests := sortNamesByType(image.Names)
|
||||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||||
|
@ -245,7 +303,7 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
||||||
Size: size,
|
Size: size,
|
||||||
Digest: imageDigest,
|
Digest: imageDigest,
|
||||||
ConfigDigest: configDigest,
|
ConfigDigest: configDigest,
|
||||||
User: imageConfig.Config.User,
|
User: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,6 +677,7 @@ func GetImageService(store storage.Store, defaultTransport string, insecureRegis
|
||||||
indexConfigs: make(map[string]*indexInfo, 0),
|
indexConfigs: make(map[string]*indexInfo, 0),
|
||||||
insecureRegistryCIDRs: make([]*net.IPNet, 0),
|
insecureRegistryCIDRs: make([]*net.IPNet, 0),
|
||||||
registries: cleanRegistries,
|
registries: cleanRegistries,
|
||||||
|
imageCache: make(map[string]imageCacheItem),
|
||||||
}
|
}
|
||||||
|
|
||||||
insecureRegistries = append(insecureRegistries, "127.0.0.0/8")
|
insecureRegistries = append(insecureRegistries, "127.0.0.0/8")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue