Compare commits

...

17 Commits

Author SHA1 Message Date
lukaszraczylo dc9e17eead Add metrics for cached queries + cache hit/miss 2024-06-11 00:03:05 +01:00
lukaszraczylo 753df6e133 fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:46:44 +01:00
lukaszraczylo f3a3b849a0 fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:41:17 +01:00
lukaszraczylo 1555958aef fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:23:59 +01:00
lukaszraczylo bccf0006b7 fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:08:47 +01:00
lukaszraczylo 9f4d92db5b fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:08:15 +01:00
lukaszraczylo a99a926d15 fixup! fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:08:06 +01:00
lukaszraczylo 6dd0ca3069 fixup! fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 23:00:24 +01:00
lukaszraczylo ef40902998 fixup! fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 22:51:59 +01:00
lukaszraczylo f55ed6d140 fixup! fixup! fixup! fixup! Update dependencies. 2024-06-10 22:43:00 +01:00
lukaszraczylo bc36607d4e fixup! fixup! fixup! Update dependencies. 2024-06-10 22:36:25 +01:00
lukaszraczylo 51d7af5fe8 fixup! fixup! Update dependencies. 2024-06-10 22:33:35 +01:00
lukaszraczylo c9b89275ca fixup! Update dependencies. 2024-06-10 22:32:54 +01:00
lukaszraczylo f0042ffe94 Update dependencies. 2024-06-10 22:25:46 +01:00
lukaszraczylo 755b433015 fixup! fixup! Add cache operations via API. 2024-06-10 22:23:33 +01:00
lukaszraczylo 86ae4ce802 fixup! Add cache operations via API. 2024-06-10 16:03:44 +01:00
lukaszraczylo 468cd959a7 Add cache operations via API. 2024-06-10 11:34:30 +01:00
18 changed files with 185 additions and 25 deletions
+2 -1
View File
@@ -2,6 +2,7 @@ FROM gcr.io/distroless/base-debian12:nonroot
WORKDIR /go/src/app
ARG TARGETARCH
ARG TARGETOS
# silly workaround for distroless image as no chmod is available
COPY --chmod=777 --chown=nonroot:nonroot static/app /go/src/app
ADD dist/bot-$TARGETOS-$TARGETARCH /go/src/app/graphql-proxy
ADD static/default-ratelimit.json /app/ratelimit.json
ENTRYPOINT ["/go/src/app/graphql-proxy"]
+6
View File
@@ -23,6 +23,7 @@ This project is in active use by [telegram-bot.app](https://telegram-bot.app), a
- [Blocking introspection](#blocking-introspection)
- [API endpoints](#api-endpoints)
- [Ban or unban the user](#ban-or-unban-the-user)
- [Cache operations](#cache-operations)
- [General](#general)
- [Metrics which matter](#metrics-which-matter)
- [Healthcheck](#healthcheck)
@@ -236,6 +237,11 @@ To do so - you need to enable the api by setting env variable `ENABLE_API=true`
* `POST /api/user-ban` - ban the user from accessing the application
* `POST /api/user-unban` - unban the user from accessing the application
#### Cache operations
* `POST /api/cache-clear` - clear the cache
* `GET /api/cache-stats` - get the cache statistics ( hits, misses, size )
Both endpoints require the `user_id` parameter to be present in the request body and allow you to provide the reason for the ban.
Example request:
+17
View File
@@ -23,6 +23,8 @@ func enableApi() {
api := apiserver.Group("/api")
api.Post("/user-ban", apiBanUser)
api.Post("/user-unban", apiUnbanUser)
api.Post("/cache-clear", apiClearCache)
api.Get("/cache-stats", apiCacheStats)
go periodicallyReloadBannedUsers()
err := apiserver.Listen(fmt.Sprintf(":%d", cfg.Server.ApiPort))
@@ -50,6 +52,21 @@ func checkIfUserIsBanned(c *fiber.Ctx, userID string) bool {
return found
}
func apiClearCache(c *fiber.Ctx) error {
cfg.Logger.Debug("Clearing cache via API", nil)
cfg.Cache.CacheClient.ClearCache()
cfg.Logger.Info("Cache cleared via API", nil)
c.Status(200).SendString("OK: cache cleared")
return nil
}
func apiCacheStats(c *fiber.Ctx) error {
stats := cfg.Cache.CacheClient.ShowStats()
cfg.Logger.Debug("Getting cache stats via API", map[string]interface{}{"stats": stats})
c.JSON(stats)
return nil
}
type apiBanUserRequest struct {
UserID string `json:"user_id"`
Reason string `json:"reason"`
+33 -1
View File
@@ -18,6 +18,8 @@ type Cache struct {
globalTTL time.Duration
compressPool sync.Pool
decompressPool sync.Pool
cacheHits int
cacheMisses int
sync.RWMutex // Reintroduced to provide lock methods
}
@@ -75,14 +77,16 @@ func (c *Cache) Get(key string) ([]byte, bool) {
entry, ok := c.entries.Load(key)
if !ok || entry.(CacheEntry).ExpiresAt.Before(time.Now()) {
c.cacheMisses++
return nil, false
}
compressedValue := entry.(CacheEntry).Value
value, err := c.decompress(compressedValue)
if err != nil {
c.cacheMisses++
return nil, false
}
c.cacheHits++
return value, true
}
@@ -109,6 +113,34 @@ func (c *Cache) CleanExpiredEntries() {
})
}
type CacheStats struct {
CachedQueries int `json:"cached_queries"`
CacheHits int `json:"cache_hits"`
CacheMisses int `json:"cache_misses"`
}
func (c *Cache) ShowStats() CacheStats {
c.RLock()
defer c.RUnlock()
var count int
c.entries.Range(func(_, _ interface{}) bool {
count++
return true
})
cs := CacheStats{
CachedQueries: count,
CacheHits: c.cacheHits,
CacheMisses: c.cacheMisses,
}
return cs
}
func (c *Cache) ClearCache() {
c.cacheHits = 0
c.cacheMisses = 0
c.entries = sync.Map{}
}
func (c *Cache) compress(data []byte) ([]byte, error) {
w := c.compressPool.Get().(*gzip.Writer)
defer c.compressPool.Put(w)
+54
View File
@@ -0,0 +1,54 @@
package libpack_cache
import (
"testing"
"time"
)
// Assume that New function initializes the cache and it is defined somewhere in the libpack_cache package.
func BenchmarkCacheSet(b *testing.B) {
cache := New(30 * time.Second) // Initializing the cache with a TTL of 30 seconds
key := "benchmark-key"
value := []byte("benchmark-value")
b.ResetTimer() // Reset the timer to exclude the setup time from the benchmark
for i := 0; i < b.N; i++ {
cache.Set(key, value, 5*time.Second)
}
}
func BenchmarkCacheGet(b *testing.B) {
cache := New(30 * time.Second) // Initializing the cache
key := "benchmark-key"
value := []byte("benchmark-value")
cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve
b.ResetTimer() // Start timing
for i := 0; i < b.N; i++ {
_, _ = cache.Get(key)
}
}
func BenchmarkCacheExpire(b *testing.B) {
key := "benchmark-expire-key"
value := []byte("benchmark-value")
ttl := 5 * time.Millisecond // Setting a short TTL for quick expiration
for i := 0; i < b.N; i++ {
cache := New(30 * time.Second)
cache.Set(key, value, ttl)
time.Sleep(ttl) // Wait for the key to expire
_, _ = cache.Get(key)
}
}
func BenchmarkCacheStats(b *testing.B) {
cache := New(30 * time.Second) // Initializing the cache
key := "benchmark-key"
value := []byte("benchmark-value")
cache.Set(key, value, 5*time.Second) // Pre-set a value to retrieve
cache.Get(key)
}
+33
View File
@@ -110,3 +110,36 @@ func (suite *CacheTestSuite) Test_CacheExpire() {
})
}
}
func (suite *CacheTestSuite) Test_CacheStats() {
cache := New(30 * time.Second)
tests := []struct {
name string
cache_value string
ttl time.Duration
}{
{
name: "test1",
cache_value: "test1-123",
ttl: 2 * time.Second,
},
{
name: "test2",
cache_value: "test2-123",
ttl: 5 * time.Second,
},
}
for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) {
cache.Set(tt.name, []byte(tt.name), tt.ttl)
c, ok := cache.Get(tt.name)
suite.Equal(true, ok)
suite.Equal(tt.name, string(c))
})
}
cache.Get("non-existent-non-cached-key")
stats := cache.ShowStats()
suite.Equal(2, stats.CacheHits, "CacheHits")
suite.Equal(1, stats.CacheMisses, "CacheMisses")
suite.Equal(2, stats.CachedQueries, "CachedQueries")
}
+7 -7
View File
@@ -5,7 +5,7 @@ go 1.21
require (
github.com/VictoriaMetrics/metrics v1.33.1
github.com/avast/retry-go/v4 v4.6.0
github.com/goccy/go-json v0.10.2
github.com/goccy/go-json v0.10.3
github.com/gofiber/fiber/v2 v2.52.4
github.com/gofrs/flock v0.8.1
github.com/google/uuid v1.6.0
@@ -14,9 +14,9 @@ require (
github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415
github.com/lukaszraczylo/go-ratecounter v0.1.8
github.com/lukaszraczylo/go-simple-graphql v1.2.14
github.com/rs/zerolog v1.32.0
github.com/rs/zerolog v1.33.0
github.com/stretchr/testify v1.9.0
github.com/valyala/fasthttp v1.53.0
github.com/valyala/fasthttp v1.54.0
)
require (
@@ -37,11 +37,11 @@ require (
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+14 -14
View File
@@ -8,8 +8,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
@@ -57,14 +57,14 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.53.0 h1:lW/+SUkOxCx2vlIu0iaImv4JLrVRnbbkpCoaawvA4zc=
github.com/valyala/fasthttp v1.53.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM=
github.com/valyala/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0=
github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM=
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
@@ -75,19 +75,19 @@ 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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+5 -1
View File
@@ -4,6 +4,7 @@ import (
"flag"
"os"
"strings"
"sync"
"github.com/gofiber/fiber/v2/middleware/proxy"
"github.com/gookit/goutil/envutil"
@@ -13,6 +14,7 @@ import (
)
var cfg *config
var once sync.Once
// function get value from the env where the value can be anything
func getDetailsFromEnv[T any](key string, defaultValue T) T {
@@ -79,7 +81,9 @@ func parseConfig() {
enableCache() // takes close to no resources, but can be used with dynamic query cache
loadRatelimitConfig()
enableApi()
once.Do(func() {
go enableApi()
})
prepareQueriesAndExemptions()
}
+1
View File
@@ -33,6 +33,7 @@ func (suite *Tests) SetupTest() {
},
)
parseConfig()
enableApi()
StartMonitoringServer()
cfg.Logger = libpack_logging.NewLogger()
// Setup environment variables here if needed
+3
View File
@@ -5,6 +5,9 @@ func (ms *MetricsSetup) RegisterDefaultMetrics() {
ms.RegisterMetricsCounter(MetricsFailed, nil)
ms.RegisterMetricsCounter(MetricsSkipped, nil)
ms.RegisterMetricsHistogram(MetricsDuration, nil)
ms.RegisterMetricsCounter(MetricsCacheHit, nil)
ms.RegisterMetricsCounter(MetricsCacheMiss, nil)
ms.RegisterMetricsCounter(MetricsQueriesCached, nil)
}
func (ms *MetricsSetup) RegisterGoMetrics() {
+4
View File
@@ -7,4 +7,8 @@ const (
MetricsSkipped = "requests_skipped"
MetricsExecutedQuery = "executed_query"
MetricsTimedQuery = "timed_query"
MetricsCacheHit = "cache_hit"
MetricsCacheMiss = "cache_miss"
MetricsQueriesCached = "cached_queries"
)
+1 -1
View File
@@ -27,7 +27,7 @@ var ratelimit_intervals = map[string]time.Duration{
}
func loadRatelimitConfig() error {
paths := []string{"/app/ratelimit.json", "./ratelimit.json", "./static/default-ratelimit.json"}
paths := []string{"/go/src/app/ratelimit.json", "./ratelimit.json", "./static/app/default-ratelimit.json"}
for _, path := range paths {
err := loadConfigFromPath(path)
+4
View File
@@ -16,6 +16,7 @@ import (
// StartHTTPProxy starts the HTTP and points it to the GraphQL server.
func StartHTTPProxy() {
cfg.Logger.Debug("Starting the HTTP proxy", nil)
server := fiber.New(fiber.Config{
DisableStartupMessage: true,
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
@@ -151,11 +152,13 @@ func processGraphQLRequest(c *fiber.Ctx) error {
queryCacheHash = calculateHash(c)
if cachedResponse := cacheLookup(queryCacheHash); cachedResponse != nil {
cfg.Monitoring.Increment(libpack_monitoring.MetricsCacheHit, nil)
cfg.Logger.Debug("Cache hit", map[string]interface{}{"hash": queryCacheHash, "user_id": extractedUserID, "request_uuid": c.Locals("request_uuid")})
c.Request().Header.Add("X-Cache-Hit", "true")
c.Send(cachedResponse)
wasCached = true
} else {
cfg.Monitoring.Increment(libpack_monitoring.MetricsCacheMiss, nil)
cfg.Logger.Debug("Cache miss", map[string]interface{}{"hash": queryCacheHash, "user_id": extractedUserID, "request_uuid": c.Locals("request_uuid")})
proxyAndCacheTheRequest(c, queryCacheHash, parsedResult.cacheTime, parsedResult.activeEndpoint)
}
@@ -181,6 +184,7 @@ func proxyAndCacheTheRequest(c *fiber.Ctx, queryCacheHash string, cacheTime int,
return
}
cfg.Cache.CacheClient.Set(queryCacheHash, c.Response().Body(), time.Duration(cacheTime)*time.Second)
cfg.Monitoring.Increment(libpack_monitoring.MetricsQueriesCached, nil)
c.Send(c.Response().Body())
}
View File
+1
View File
@@ -0,0 +1 @@
{}
View File