From 917ee1a431c130baab63f22efa5d0604113fec42 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Tue, 10 Oct 2023 19:21:25 +0100 Subject: [PATCH] Add cache ttl support (#3) * Add ability to use `@cached(ttl: 120)` * Update documentation. --- Makefile | 2 +- README.md | 5 ++++- go.mod | 2 +- go.sum | 4 ++-- graphql.go | 14 +++++++++++++- server.go | 13 +++++++++---- 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 13ea839..d7a5ad5 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ help: ## display this help .PHONY: run run: build ## run application - @LOG_LEVEL=warn BLOCK_SCHEMA_INTROSPECTION=false JWT_ROLE_RATE_LIMIT=false JWT_ROLE_CLAIM_PATH="Hasura.x-hasura-default-role" JWT_USER_CLAIM_PATH="Hasura.x-hasura-user-id" HOST_GRAPHQL=https://hasura8.lan/v1/graphql ./graphql-proxy + @LOG_LEVEL=debug BLOCK_SCHEMA_INTROSPECTION=false JWT_ROLE_RATE_LIMIT=false JWT_ROLE_CLAIM_PATH="Hasura.x-hasura-default-role" JWT_USER_CLAIM_PATH="Hasura.x-hasura-user-id" HOST_GRAPHQL=https://hasura8.lan/v1/graphql ./graphql-proxy .PHONY: build build: ## build the binary diff --git a/README.md b/README.md index 903da2e..dcca27b 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,10 @@ I wanted to monitor the queries and responses of our graphql endpoint, but we di ### Caching Cache engine is enabled in background as it does not use any additional resources. -You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` environment variable to `true` - which will enable the cache for all queries, without introspection of the query. You can leave the global cache disabled and enable the cache for specific queries by adding the `@cache` directive to the query. +You can then start using the cache by setting the `ENABLE_GLOBAL_CACHE` environment variable to `true` - which will enable the cache for all queries, without introspection of the query. You can leave the global cache disabled and enable the cache for specific queries by adding the `@cached` directive to the query. + +In case of the `@cached` you can add additional parameters to the directive which will set the cache for specific query to provided time. +For example `query MyCachedQuery @cached(ttl: 90) ....` will set the cache for the query to 90 seconds. ### Role based rate limiting diff --git a/go.mod b/go.mod index 4084a15..d3838bf 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/valyala/tcplisten v1.0.0 // indirect github.com/wI2L/jsondiff v0.4.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/net v0.16.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect diff --git a/go.sum b/go.sum index 959e8c7..3f1c172 100644 --- a/go.sum +++ b/go.sum @@ -90,8 +90,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/graphql.go b/graphql.go index e5b52fc..c7e0dbc 100644 --- a/graphql.go +++ b/graphql.go @@ -1,6 +1,8 @@ package main import ( + "strconv" + fiber "github.com/gofiber/fiber/v2" "github.com/graphql-go/graphql/language/ast" "github.com/graphql-go/graphql/language/parser" @@ -33,7 +35,7 @@ var retrospection_queries = []string{ // Saving the introspection queries as a map O(1) operation instead of O(n) for a slice. var retrospectionQuerySet = make(map[string]struct{}, len(retrospection_queries)) -func parseGraphQLQuery(c *fiber.Ctx) (operationType, operationName string, cacheRequest bool, should_block bool) { +func parseGraphQLQuery(c *fiber.Ctx) (operationType, operationName string, cacheRequest bool, cache_time int, should_block bool) { m := make(map[string]interface{}) err := json.Unmarshal(c.Body(), &m) if err != nil { @@ -68,6 +70,16 @@ func parseGraphQLQuery(c *fiber.Ctx) (operationType, operationName string, cache for _, dir := range oper.Directives { if dir.Name.Value == "cached" { cacheRequest = true + for _, arg := range dir.Arguments { + if arg.Name.Value == "ttl" { + cache_time, err = strconv.Atoi(arg.Value.GetValue().(string)) + if err != nil { + cfg.Logger.Error("Can't parse the ttl", map[string]interface{}{"ttl": arg.Value.GetValue().(string)}) + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + return + } + } + } } } if cfg.Security.BlockIntrospection { diff --git a/server.go b/server.go index 345d105..ce1012a 100644 --- a/server.go +++ b/server.go @@ -63,11 +63,16 @@ func processGraphQLRequest(c *fiber.Ctx) error { } } - opType, opName, cacheFromQuery, shouldBlock := parseGraphQLQuery(c) + opType, opName, cacheFromQuery, cache_time, shouldBlock := parseGraphQLQuery(c) if shouldBlock { return nil } + if cache_time > 0 { + cfg.Logger.Debug("Cache time set via query", map[string]interface{}{"cache_time": cache_time}) + cache_time = cfg.Cache.CacheTTL + } + wasCached := false // Handling Cache Logic @@ -81,7 +86,7 @@ func processGraphQLRequest(c *fiber.Ctx) error { wasCached = true } else { cfg.Logger.Debug("Cache miss", map[string]interface{}{"hash": queryCacheHash, "user_id": extractedUserID}) - proxyAndCacheTheRequest(c, queryCacheHash) + proxyAndCacheTheRequest(c, queryCacheHash, cache_time) } } else { proxyTheRequest(c) @@ -96,9 +101,9 @@ func processGraphQLRequest(c *fiber.Ctx) error { } // Additional helper function to avoid code repetition -func proxyAndCacheTheRequest(c *fiber.Ctx, queryCacheHash string) { +func proxyAndCacheTheRequest(c *fiber.Ctx, queryCacheHash string, cache_time int) { proxyTheRequest(c) - cfg.Cache.CacheClient.Set(queryCacheHash, c.Response().Body(), time.Duration(cfg.Cache.CacheTTL)*time.Second) + cfg.Cache.CacheClient.Set(queryCacheHash, c.Response().Body(), time.Duration(cache_time)*time.Second) c.Send(c.Response().Body()) }