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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
store storage.Store
|
||||
defaultTransport string
|
||||
insecureRegistryCIDRs []*net.IPNet
|
||||
indexConfigs map[string]*indexInfo
|
||||
registries []string
|
||||
imageCache map[string]imageCacheItem
|
||||
}
|
||||
|
||||
// sizer knows its size.
|
||||
|
@ -172,25 +182,38 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
return nil, err
|
||||
}
|
||||
if image, err := istorage.Transport.GetStoreImage(svc.store, ref); err == nil {
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
configDigest, err := imageConfigDigest(img, nil)
|
||||
img.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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var user string
|
||||
var size *uint64
|
||||
var configDigest digest.Digest
|
||||
if cacheItem, ok := svc.imageCache[image.ID]; ok {
|
||||
user, size, configDigest = cacheItem.user, cacheItem.size, cacheItem.configDigest
|
||||
} else {
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size = imageSize(img)
|
||||
configDigest, err = imageConfigDigest(img, nil)
|
||||
img.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()
|
||||
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)
|
||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||
|
@ -202,7 +225,7 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
Size: size,
|
||||
Digest: imageDigest,
|
||||
ConfigDigest: configDigest,
|
||||
User: imageConfig.Config.User,
|
||||
User: user,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
@ -210,30 +233,65 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
if err != nil {
|
||||
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 {
|
||||
ref, err := istorage.Transport.ParseStoreReference(svc.store, "@"+image.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
configDigest, err := imageConfigDigest(img, nil)
|
||||
img.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageFull, err := ref.NewImage(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer imageFull.Close()
|
||||
visited[image.ID] = struct{}{}
|
||||
var user string
|
||||
var size *uint64
|
||||
var configDigest digest.Digest
|
||||
if cacheItem, ok := svc.imageCache[image.ID]; ok {
|
||||
user, size, configDigest = cacheItem.user, cacheItem.size, cacheItem.configDigest
|
||||
} else {
|
||||
ref, err := istorage.Transport.ParseStoreReference(svc.store, "@"+image.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size = imageSize(img)
|
||||
configDigest, err = imageConfigDigest(img, nil)
|
||||
img.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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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)
|
||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||
|
@ -245,7 +303,7 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
Size: size,
|
||||
Digest: imageDigest,
|
||||
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),
|
||||
insecureRegistryCIDRs: make([]*net.IPNet, 0),
|
||||
registries: cleanRegistries,
|
||||
imageCache: make(map[string]imageCacheItem),
|
||||
}
|
||||
|
||||
insecureRegistries = append(insecureRegistries, "127.0.0.0/8")
|
||||
|
|
Loading…
Reference in a new issue