Add cache ttl support (#3)

* Add ability to use `@cached(ttl: 120)`

* Update documentation.
This commit is contained in:
2023-10-10 19:21:25 +01:00
committed by GitHub
parent bc128493b0
commit 917ee1a431
6 changed files with 30 additions and 10 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ help: ## display this help
.PHONY: run .PHONY: run
run: build ## run application 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 .PHONY: build
build: ## build the binary build: ## build the binary
+4 -1
View File
@@ -45,7 +45,10 @@ I wanted to monitor the queries and responses of our graphql endpoint, but we di
### Caching ### Caching
Cache engine is enabled in background as it does not use any additional resources. 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 ### Role based rate limiting
+1 -1
View File
@@ -42,7 +42,7 @@ require (
github.com/valyala/tcplisten v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect
github.com/wI2L/jsondiff v0.4.0 // indirect github.com/wI2L/jsondiff v0.4.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // 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/sync v0.4.0 // indirect
golang.org/x/sys v0.13.0 // indirect golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect
+2 -2
View File
@@ -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= 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 h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 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= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+13 -1
View File
@@ -1,6 +1,8 @@
package main package main
import ( import (
"strconv"
fiber "github.com/gofiber/fiber/v2" fiber "github.com/gofiber/fiber/v2"
"github.com/graphql-go/graphql/language/ast" "github.com/graphql-go/graphql/language/ast"
"github.com/graphql-go/graphql/language/parser" "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. // 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)) 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{}) m := make(map[string]interface{})
err := json.Unmarshal(c.Body(), &m) err := json.Unmarshal(c.Body(), &m)
if err != nil { if err != nil {
@@ -68,6 +70,16 @@ func parseGraphQLQuery(c *fiber.Ctx) (operationType, operationName string, cache
for _, dir := range oper.Directives { for _, dir := range oper.Directives {
if dir.Name.Value == "cached" { if dir.Name.Value == "cached" {
cacheRequest = true 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 { if cfg.Security.BlockIntrospection {
+9 -4
View File
@@ -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 { if shouldBlock {
return nil 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 wasCached := false
// Handling Cache Logic // Handling Cache Logic
@@ -81,7 +86,7 @@ func processGraphQLRequest(c *fiber.Ctx) error {
wasCached = true wasCached = true
} else { } else {
cfg.Logger.Debug("Cache miss", map[string]interface{}{"hash": queryCacheHash, "user_id": extractedUserID}) cfg.Logger.Debug("Cache miss", map[string]interface{}{"hash": queryCacheHash, "user_id": extractedUserID})
proxyAndCacheTheRequest(c, queryCacheHash) proxyAndCacheTheRequest(c, queryCacheHash, cache_time)
} }
} else { } else {
proxyTheRequest(c) proxyTheRequest(c)
@@ -96,9 +101,9 @@ func processGraphQLRequest(c *fiber.Ctx) error {
} }
// Additional helper function to avoid code repetition // 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) 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()) c.Send(c.Response().Body())
} }