From 4712d668444bf32f1fa55d65faf5452ddfcc8466 Mon Sep 17 00:00:00 2001 From: Artem Mironov Date: Tue, 11 Jun 2024 01:32:07 +0300 Subject: [PATCH] feat: add cache --- api/api.go | 16 ++++++++++++++++ api/check.go | 12 ++++++++++++ cmd/apikeyman/main.go | 12 ++++++++++++ deploy/charts/apikeyman/Chart.yaml | 4 ++-- go.mod | 6 ++++-- go.sum | 4 ++++ 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/api/api.go b/api/api.go index 20eca6b..a9bc167 100644 --- a/api/api.go +++ b/api/api.go @@ -10,6 +10,8 @@ import ( "github.com/gin-gonic/gin" "github.com/jaspeen/apikeyman/algo" + "github.com/jaspeen/apikeyman/db/queries" + "github.com/jellydator/ttlcache/v3" ) const ( @@ -27,6 +29,8 @@ type Config struct { TimestampQueryParam string TimestampExpiration time.Duration DefaultKeyExpiration time.Duration + CacheMaxSize uint64 + CacheTTL time.Duration } var ErrUnauthorized = errors.New("Unauthorized") @@ -74,6 +78,18 @@ type Api struct { Log *slog.Logger Db *sql.DB Config Config + cache *ttlcache.Cache[string, *queries.GetApiKeyForVerifyRow] +} + +func (a *Api) NewApi(log *slog.Logger, db *sql.DB, config Config) (*Api, error) { + var cache *ttlcache.Cache[string, *queries.GetApiKeyForVerifyRow] + if config.CacheMaxSize > 0 { + cache = ttlcache.New( + ttlcache.WithTTL[string, *queries.GetApiKeyForVerifyRow](config.CacheTTL), + ttlcache.WithCapacity[string, *queries.GetApiKeyForVerifyRow](config.CacheMaxSize), + ) + } + return &Api{Log: log, Db: db, Config: config, cache: cache}, nil } func (a *Api) Routes(prefix string) *gin.Engine { diff --git a/api/check.go b/api/check.go index aa4ddfa..d6ab332 100644 --- a/api/check.go +++ b/api/check.go @@ -13,6 +13,7 @@ import ( "github.com/jaspeen/apikeyman/algo" "github.com/jaspeen/apikeyman/db" "github.com/jaspeen/apikeyman/db/queries" + "github.com/jellydator/ttlcache/v3" ) func (a *Api) checkAndGetApiKeyData(c *gin.Context) (*queries.GetApiKeyForVerifyRow, error) { @@ -26,6 +27,13 @@ func (a *Api) checkAndGetApiKeyData(c *gin.Context) (*queries.GetApiKeyForVerify return nil, ErrUnauthorized } + if a.cache != nil { + var cached = a.cache.Get(apiKeyString) + if cached != nil && !cached.IsExpired() { + return cached.Value(), nil + } + } + apiKey, err := ParseApiKey(apiKeyString) if err != nil { @@ -44,6 +52,10 @@ func (a *Api) checkAndGetApiKeyData(c *gin.Context) (*queries.GetApiKeyForVerify return nil, ErrUnauthorized } + if a.cache != nil { + a.cache.Set(apiKeyString, &apiKeyData, ttlcache.DefaultTTL) + } + return &apiKeyData, nil } diff --git a/cmd/apikeyman/main.go b/cmd/apikeyman/main.go index ba3e8c7..f1d121e 100644 --- a/cmd/apikeyman/main.go +++ b/cmd/apikeyman/main.go @@ -107,6 +107,16 @@ func main() { Value: "/", Usage: "Base URL path for API", }, + &cli.Uint64Flag{ + Name: "cache-max-size", + Value: 0, + Usage: "Max number of keys to cache", + }, + &cli.DurationFlag{ + Name: "cache-ttl", + Value: 5 * time.Minute, + Usage: "Time to live for cache entries", + }, }, Action: func(cCtx *cli.Context) error { db, err := sql.Open("postgres", cCtx.String("db")) @@ -139,6 +149,8 @@ func main() { TimestampQueryParam: "timestamp", TimestampExpiration: cCtx.Duration("timestamp-threshold-ms"), DefaultKeyExpiration: 30 * 24 * time.Hour, + CacheMaxSize: cCtx.Uint64("cache-max-size"), + CacheTTL: cCtx.Duration("cache-ttl"), }} r := a.Routes(cCtx.String("base-path")) return r.Run(cCtx.String("addr")) diff --git a/deploy/charts/apikeyman/Chart.yaml b/deploy/charts/apikeyman/Chart.yaml index c59783a..d586a26 100644 --- a/deploy/charts/apikeyman/Chart.yaml +++ b/deploy/charts/apikeyman/Chart.yaml @@ -7,5 +7,5 @@ keywords: - security sources: - https://github.com/jaspeen/apikeyman -version: 0.1.7 -appVersion: "0.1.6" +version: 0.1.8 +appVersion: "0.1.8" diff --git a/go.mod b/go.mod index b69e2db..faa149d 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module github.com/jaspeen/apikeyman go 1.21.1 require ( + github.com/cenkalti/backoff/v4 v4.1.3 github.com/golang-migrate/migrate/v4 v4.17.1 github.com/ory/dockertest/v3 v3.10.0 github.com/shengdoushi/base58 v1.0.0 + github.com/sqlc-dev/pqtype v0.3.0 github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.2 golang.org/x/crypto v0.23.0 @@ -17,7 +19,6 @@ require ( github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/containerd/continuity v0.3.0 // indirect @@ -52,7 +53,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.9.2 // indirect - github.com/sqlc-dev/pqtype v0.3.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect @@ -62,6 +62,7 @@ require ( golang.org/x/arch v0.8.0 // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.10.0 // indirect @@ -74,6 +75,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 github.com/gin-gonic/gin v1.10.0 + github.com/jellydator/ttlcache/v3 v3.2.0 github.com/lib/pq v1.10.9 github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect diff --git a/go.sum b/go.sum index 2945aaf..39cb962 100644 --- a/go.sum +++ b/go.sum @@ -84,6 +84,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= +github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -177,6 +179,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=