From 1fd27e4ffeeaecc322fc127a0fcd017bdfacdad9 Mon Sep 17 00:00:00 2001 From: Vadimka Komissarov Date: Mon, 15 Jul 2024 14:19:15 +0000 Subject: [PATCH] main opts optim, cache - refactoring, s2 decompresser add --- cmd/alice/main.go | 4 +-- go.mod | 4 +-- internal/cache/api.go | 4 +-- internal/cache/cache.go | 50 ++++++++++++++++++++++++++++++------- internal/proxy/handlers.go | 4 +-- internal/proxy/proxy.go | 14 ++++++----- internal/proxy/validator.go | 6 ++++- 7 files changed, 61 insertions(+), 25 deletions(-) diff --git a/cmd/alice/main.go b/cmd/alice/main.go index e8051b6..7f5f3b8 100644 --- a/cmd/alice/main.go +++ b/cmd/alice/main.go @@ -201,7 +201,7 @@ func main() { &cli.IntFlag{ Name: "cache-shards", Usage: "number of shards (must be a power of 2)", - Value: 1024, + Value: 512, }, &cli.DurationFlag{ Name: "cache-life-window", @@ -225,7 +225,7 @@ func main() { &cli.IntFlag{ Name: "cache-max-entry-size", Usage: "Max size of entry in bytes. Used only to calculate initial size for cache shards", - Value: 128 * 1024, + Value: 64 * 1024, }, &cli.StringFlag{ diff --git a/go.mod b/go.mod index bb4cb00..6f2c575 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/allegro/bigcache/v3 v3.1.0 github.com/gofiber/fiber/v2 v2.52.5 github.com/jedib0t/go-pretty/v6 v6.5.9 + github.com/klauspost/compress v1.17.9 github.com/rs/zerolog v1.33.0 github.com/urfave/cli/v2 v2.27.2 + github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.55.0 ) @@ -15,13 +17,11 @@ require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/google/uuid v1.5.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect golang.org/x/sys v0.21.0 // indirect diff --git a/internal/cache/api.go b/internal/cache/api.go index c17dbd1..cf3dfc2 100644 --- a/internal/cache/api.go +++ b/internal/cache/api.go @@ -15,8 +15,8 @@ type ApiCacheEntry struct { Key string } -func (m *Cache) ApiDump(key string) ([]byte, error) { - return m.Get(key) +func (m *Cache) ApiDump(key string, w io.Writer) error { + return m.Write(key, w) } func (m *Cache) ApiDumpKeys() io.Reader { diff --git a/internal/cache/cache.go b/internal/cache/cache.go index a7d6c13..38143a6 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -3,9 +3,11 @@ package cache import ( "context" "errors" + "io" "github.com/allegro/bigcache/v3" "github.com/anilibria/alice/internal/utils" + "github.com/klauspost/compress/s2" "github.com/rs/zerolog" "github.com/urfave/cli/v2" ) @@ -56,15 +58,7 @@ func (m *Cache) Bootstrap() { } } -func (m *Cache) CacheResponse(key string, payload []byte) error { - return m.Set(key, payload) -} - -func (m *Cache) CachedResponse(key string) ([]byte, error) { - return m.Get(key) -} - -func (m *Cache) IsResponseCached(key string) (_ bool, e error) { +func (m *Cache) IsCached(key string) (_ bool, e error) { if _, e = m.Get(key); e != nil && errors.Is(e, bigcache.ErrEntryNotFound) { return false, nil } else if e != nil { @@ -73,3 +67,41 @@ func (m *Cache) IsResponseCached(key string) (_ bool, e error) { return true, nil } + +func (m *Cache) Cache(key string, payload []byte) error { + return m.setCompressed(key, payload) +} + +func (m *Cache) Write(key string, w io.Writer) error { + return m.writeDecompressed(key, w) +} + +func (m *Cache) setCompressed(key string, payload []byte) error { + cmp := s2.Encode(nil, payload) + + if zerolog.GlobalLevel() <= zerolog.DebugLevel { + m.log.Trace().Msgf("compressed from %d to %d bytes", len(payload), len(cmp)) + } + + return m.Set(key, cmp) +} + +func (m *Cache) writeDecompressed(key string, w io.Writer) (e error) { + var cmp, decmp []byte + if cmp, e = m.Get(key); e != nil { + return + } + + if decmp, e = s2.Decode(nil, cmp); e != nil { + return + } + + var wrote int + wrote, e = w.Write(decmp) + + if zerolog.GlobalLevel() <= zerolog.DebugLevel { + m.log.Trace().Msgf("decompressed from %d to %d bytes, wrote %d bytes", len(cmp), len(decmp), wrote) + } + + return +} diff --git a/internal/proxy/handlers.go b/internal/proxy/handlers.go index fe7c8a8..a3136cb 100644 --- a/internal/proxy/handlers.go +++ b/internal/proxy/handlers.go @@ -63,12 +63,10 @@ func (m *Proxy) HandleCacheDump(c *fiber.Ctx) (e error) { return fiber.NewError(fiber.StatusBadRequest, "key could not be empty") } - var payload []byte - if payload, e = m.cache.ApiDump(cachekey); e != nil { + if e = m.cache.ApiDump(cachekey, c); e != nil { return fiber.NewError(fiber.StatusInternalServerError, e.Error()) } - c.Response().SetBodyRaw(payload) return respondPlainWithStatus(c, fiber.StatusOK) } diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index ea69570..bdf6129 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -118,7 +118,7 @@ func (m *Proxy) cacheResponse(c *fiber.Ctx, rsp *fasthttp.Response) (e error) { m.cache.Stats().DelHits, m.cache.Stats().Hits, m.cache.Stats().Misses) } - if e = m.cache.CacheResponse(key.UnsafeString(), rsp.Body()); e != nil { + if e = m.cache.Cache(key.UnsafeString(), rsp.Body()); e != nil { return } @@ -138,7 +138,7 @@ func (m *Proxy) canRespondFromCache(c *fiber.Ctx) (_ bool, e error) { key := c.Context().UserValue(utils.UVCacheKey).(*Key) var ok bool - if ok, e = m.cache.IsResponseCached(key.UnsafeString()); e != nil { + if ok, e = m.cache.IsCached(key.UnsafeString()); e != nil { rlog(c).Warn().Msg("there is problems with cache driver") return } else if !ok { @@ -151,12 +151,11 @@ func (m *Proxy) canRespondFromCache(c *fiber.Ctx) (_ bool, e error) { func (m *Proxy) respondFromCache(c *fiber.Ctx) (e error) { key := c.Context().UserValue(utils.UVCacheKey).(*Key) - var body []byte - if body, e = m.cache.CachedResponse(key.UnsafeString()); e != nil { + if e = m.cache.Write(key.UnsafeString(), c); e != nil { return } - return m.respondWithStatus(c, body, fiber.StatusOK) + return m.respondWithStatus(c, nil, fiber.StatusOK) } func (m *Proxy) respondWithStatus(c *fiber.Ctx, body []byte, status int) error { @@ -166,7 +165,10 @@ func (m *Proxy) respondWithStatus(c *fiber.Ctx, body []byte, status int) error { m.cache.Stats().Hits, m.cache.Stats().Misses) } - c.Response().SetBodyRaw(body) + if body != nil { + c.Response().SetBodyRaw(body) + } + c.Response().Header.SetContentType(fiber.MIMEApplicationJSONCharsetUTF8) return c.SendStatus(status) } diff --git a/internal/proxy/validator.go b/internal/proxy/validator.go index 5379486..6ac15bc 100644 --- a/internal/proxy/validator.go +++ b/internal/proxy/validator.go @@ -188,7 +188,10 @@ func (m *Validator) encodeFormData() (e error) { for k, v := range form.Value { m.requestArgs.Add(k, v[0]) - rlog(m.Ctx).Trace().Msg("parsed form value " + k + " - " + v[0]) + + if zerolog.GlobalLevel() <= zerolog.DebugLevel { + rlog(m.Ctx).Trace().Msg("parsed form value " + k + " - " + v[0]) + } } // TODO - with go1.21.0 we can use: @@ -206,6 +209,7 @@ func (m *Validator) encodeFormData() (e error) { func (m *Validator) isArgsWhitelisted() (_ bool) { // TODO too much allocations here: + // ? maybe make 'pool' fro chans map[size][]chan []byte? declinedKeys := make(chan []byte, m.requestArgs.Len()) m.requestArgs.VisitAll(func(key, value []byte) {