diff --git a/cmd/api_server.go b/cmd/api_server.go index ff5700e..ccdef72 100644 --- a/cmd/api_server.go +++ b/cmd/api_server.go @@ -6,12 +6,14 @@ import ( "os" "postcode-polygons/routes" spatialindex "postcode-polygons/spatial-index" + "time" "github.com/Depado/ginprom" "github.com/aurowora/compress" "github.com/gin-contrib/cors" "github.com/gin-contrib/pprof" "github.com/gin-gonic/gin" + "github.com/kofalt/go-memoize" "github.com/tavsec/gin-healthcheck/checks" cachecontrol "go.eigsys.de/gin-cachecontrol/v2" @@ -58,8 +60,10 @@ func ApiServer(zipFile string, port int, debug bool) { log.Fatalf("failed to initialize healthcheck: %v", err) } + cache := memoize.NewMemoizer(5*time.Minute, 10*time.Minute) + r.GET("/v1/postcode/codepoints", routes.CodePointSearch(spatialIndex)) - r.GET("/v1/postcode/polygons", routes.PolygonSearch(spatialIndex)) + r.GET("/v1/postcode/polygons", routes.PolygonSearch(spatialIndex, cache)) addr := fmt.Sprintf(":%d", port) log.Printf("Starting HTTP API Server on port %d...", port) diff --git a/go.mod b/go.mod index 9975287..1392916 100644 --- a/go.mod +++ b/go.mod @@ -36,13 +36,14 @@ require ( github.com/influxdata/influxdb-client-go/v2 v2.14.0 // indirect github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect @@ -71,7 +72,8 @@ require ( github.com/dsnet/compress v0.0.1 github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/kofalt/go-memoize v0.0.0-20240506050413-9e5eb99a0f2a + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/tidwall/rtree v1.10.0 go.mongodb.org/mongo-driver v1.17.4 // indirect diff --git a/go.sum b/go.sum index 55e2328..d1b7f53 100644 --- a/go.sum +++ b/go.sum @@ -141,7 +141,11 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kofalt/go-memoize v0.0.0-20240506050413-9e5eb99a0f2a h1:yyeZ0oZLWgSakB9QzPuL/Kyx9kcXYblDOswXaOEx0tg= +github.com/kofalt/go-memoize v0.0.0-20240506050413-9e5eb99a0f2a/go.mod h1:EUxMohcCc4AiiO1SImzCQo3EdrEYj9Xkyrxbepg02nQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -162,6 +166,8 @@ github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8S github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -196,6 +202,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= diff --git a/routes/search.go b/routes/search.go index 9c9334f..ae8f4cd 100644 --- a/routes/search.go +++ b/routes/search.go @@ -13,6 +13,7 @@ import ( "os" "github.com/gin-gonic/gin" + "github.com/kofalt/go-memoize" "github.com/paulmach/orb/geojson" ) @@ -50,7 +51,7 @@ func CodePointSearch(spatialIndex *spatialindex.SpatialIndex) func(c *gin.Contex } } -func PolygonSearch(spatialIndex *spatialindex.SpatialIndex) func(c *gin.Context) { +func PolygonSearch(spatialIndex *spatialindex.SpatialIndex, cache *memoize.Memoizer) func(c *gin.Context) { return func(c *gin.Context) { bbox, err := parseBBox(c.Query("bbox")) if err != nil { @@ -85,7 +86,9 @@ func PolygonSearch(spatialIndex *spatialindex.SpatialIndex) func(c *gin.Context) for district := range districts { filename := fmt.Sprintf("./data/postcodes/%s/%s.geojson.bz2", target, district) - featureCollection, err := internal.DecompressFeatureCollection(filename) + featureCollection, err, _ := memoize.Call(cache, filename, func() (*geojson.FeatureCollection, error) { + return internal.DecompressFeatureCollection(filename) + }) if err != nil && os.IsNotExist(err) { log.Printf("polygon file %s does not exist, skipping", filename) continue