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:
Nalin Dahyabhai 2018-02-14 13:57:18 -05:00
parent 96fb47213e
commit c53211eacd

View file

@ -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")