mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-06-05 23:03:48 +00:00
Add cache ttl support (#3)
* Add ability to use `@cached(ttl: 120)` * Update documentation.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user