Skip to content

Commit

Permalink
Cache all package manifest on startup (#103)
Browse files Browse the repository at this point in the history
So far in each request to search or categories all manifest had to be reread from disk each time. As the packages do not change during the runtime of the service, this is not necessary and all can be cached on startup of the service and kept in memory. Even if there are one day 10000 packages and each manifest is 1KB, this would only consume ~10MB of memory (currently manifest are only about 250 Bytes). Using 10MB of memory instead of 10000 file read on each request seems worth it.

The code was also modified to in general pass a copy of the packages instead of using a reference. This makes sure even if a request modifies the packages locally, the global package list is not modified.
  • Loading branch information
ruflin committed Sep 12, 2019
1 parent 7104123 commit a575c39
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Add validation check that Kibana min/max are valid semver versions. [#99](https://github.com/elastic/integrations-registry/pull/99)
* Adding Cache-Control max-age headers to all http responses set to 1h. [#101](https://github.com/elastic/integrations-registry/pull/101)
* Validate packages to guarantee only predefined categories can be used. [#100](https://github.com/elastic/integrations-registry/pull/100)
* Cache all manifest on service startup for resource optimisation. [#103](https://github.com/elastic/integrations-registry/pull/103)

### Changed

Expand Down
17 changes: 7 additions & 10 deletions categories.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package main

import (
Expand All @@ -9,8 +13,6 @@ import (
"github.com/elastic/integrations-registry/util"
)



type Category struct {
Id string `yaml:"id" json:"id"`
Title string `yaml:"title" json:"title"`
Expand All @@ -22,19 +24,14 @@ func categoriesHandler() func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
cacheHeaders(w)

packagePaths, err := util.GetPackagePaths(packagesBasePath)
packages, err := util.GetPackages(packagesBasePath)
if err != nil {
notFound(w, err)
return
}

packageList := map[string]*util.Package{}
packageList := map[string]util.Package{}
// Get unique list of newest packages
for _, i := range packagePaths {
p, err := util.NewPackage(packagesBasePath, i)
if err != nil {
return
}
for _, p := range packages {

// Check if the version exists and if it should be added or not.
if pp, ok := packageList[p.Name]; ok {
Expand Down
24 changes: 9 additions & 15 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,21 @@ func BuildIntegrationPackages() error {
packagesBasePath = publicDir + "/" + packageDir + "/"
}

packagePaths, err := util.GetPackagePaths(packagesBasePath)
packages, err := util.GetPackages(packagesBasePath)
if err != nil {
return err
}

for _, path := range packagePaths {
err = buildPackage(packagesBasePath, path)
for _, p := range packages {
err = buildPackage(packagesBasePath, p)
if err != nil {
return err
}
}
return nil
}

func buildPackage(packagesBasePath, path string) error {
func buildPackage(packagesBasePath string, p util.Package) error {

// Change path to simplify tar command
currentPath, err := os.Getwd()
Expand All @@ -117,29 +117,23 @@ func buildPackage(packagesBasePath, path string) error {
}
defer os.Chdir(currentPath)

err = sh.RunV("tar", "cvzf", path+".tar.gz", filepath.Base(path)+"/")
err = sh.RunV("tar", "cvzf", p.GetPath()+".tar.gz", filepath.Base(p.GetPath())+"/")
if err != nil {
return err
}

// Build package endpoint
p, err := util.NewPackage(".", path)
if err != nil {
return fmt.Errorf("Error creating package: %s: %s", path, err)
return fmt.Errorf("Error creating package: %s: %s", p.GetPath(), err)
}

// Checks if the package is valid
err = p.Validate()
if err != nil {
return fmt.Errorf("Invalid package %s-%s: %s", p.Name, p.Version, err)
return fmt.Errorf("Invalid package: %s: %s", p.GetPath(), err)
}

err = p.LoadAssets(path)
err = p.LoadAssets(p.GetPath())
if err != nil {
return err
}

err = writeJsonFile(p, path+"/index.json")
err = writeJsonFile(p, p.GetPath()+"/index.json")
if err != nil {
return err
}
Expand Down
10 changes: 10 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"strconv"
"syscall"

"github.com/elastic/integrations-registry/util"

ucfgYAML "github.com/elastic/go-ucfg/yaml"

"github.com/gorilla/mux"
Expand Down Expand Up @@ -46,6 +48,14 @@ func main() {
}
packagesBasePath = config.PackagesPath

// Prefill the package cache
packages, err := util.GetPackages(packagesBasePath)
if err != nil {
log.Print(err)
os.Exit(1)
}
log.Printf("%v package manifests loaded into memory.\n", len(packages))

server := &http.Server{Addr: address, Handler: getRouter()}

go func() {
Expand Down
22 changes: 10 additions & 12 deletions search.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package main

import (
Expand Down Expand Up @@ -47,21 +51,15 @@ func searchHandler() func(w http.ResponseWriter, r *http.Request) {
}
}

packagePaths, err := util.GetPackagePaths(packagesBasePath)
packages, err := util.GetPackages(packagesBasePath)
if err != nil {
notFound(w, err)
notFound(w, fmt.Errorf("problem fetching packages: %s", err))
return
}

packagesList := map[string]map[string]*util.Package{}
packagesList := map[string]map[string]util.Package{}

// Checks that only the most recent version of an integration is added to the list
for _, path := range packagePaths {
p, err := util.NewPackage(packagesBasePath, path)
if err != nil {
notFound(w, err)
return
}
for _, p := range packages {

// Filter by category first as this could heavily reduce the number of packages
// It must happen before the version filtering as there only the newest version
Expand Down Expand Up @@ -100,7 +98,7 @@ func searchHandler() func(w http.ResponseWriter, r *http.Request) {
}

if _, ok := packagesList[p.Name]; !ok {
packagesList[p.Name] = map[string]*util.Package{}
packagesList[p.Name] = map[string]util.Package{}
}
packagesList[p.Name][p.Version] = p
}
Expand All @@ -116,7 +114,7 @@ func searchHandler() func(w http.ResponseWriter, r *http.Request) {
}
}

func getPackageOutput(packagesList map[string]map[string]*util.Package) ([]byte, error) {
func getPackageOutput(packagesList map[string]map[string]util.Package) ([]byte, error) {

separator := "@"
// Packages need to be sorted to be always outputted in the same order
Expand Down
13 changes: 10 additions & 3 deletions util/package.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package util

import (
Expand All @@ -8,10 +12,9 @@ import (
"strings"

"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"

"github.com/blang/semver"

"gopkg.in/yaml.v2"
)

const defaultType = "integration"
Expand Down Expand Up @@ -137,7 +140,7 @@ func (p *Package) HasKibanaVersion(version *semver.Version) bool {
return true
}

func (p *Package) IsNewer(pp *Package) bool {
func (p *Package) IsNewer(pp Package) bool {
return p.versionSemVer.GT(pp.versionSemVer)
}

Expand Down Expand Up @@ -230,3 +233,7 @@ func (p *Package) Validate() error {

return nil
}

func (p *Package) GetPath() string {
return p.Name + "-" + p.Version
}
4 changes: 4 additions & 0 deletions util/package_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package util

import (
Expand Down
38 changes: 35 additions & 3 deletions util/packages.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package util

import "io/ioutil"
import (
"io/ioutil"
)

var packageList []Package

// GetPackages returns a slice with all existing packages.
// The list is stored in memory and on the second request directly
// served from memory. This assumes chnages to packages only happen on restart.
// Caching the packages request many file reads every time this method is called.
func GetPackages(packagesBasePath string) ([]Package, error) {
if packageList != nil {
return packageList, nil
}

packagePaths, err := getPackagePaths(packagesBasePath)
if err != nil {
return nil, err
}

for _, i := range packagePaths {
p, err := NewPackage(packagesBasePath, i)
if err != nil {
return nil, err
}
packageList = append(packageList, *p)
}
return packageList, nil
}

// GetPackagePaths returns list of available packages, one for each version.
func GetPackagePaths(packagesPath string) ([]string, error) {
// getPackagePaths returns list of available packages, one for each version.
func getPackagePaths(packagesPath string) ([]string, error) {

files, err := ioutil.ReadDir(packagesPath)
if err != nil {
Expand Down

0 comments on commit a575c39

Please sign in to comment.