Skip to content

Commit

Permalink
Optimization(all): Decrease allocs and responses time
Browse files Browse the repository at this point in the history
  • Loading branch information
darkweak committed Jul 3, 2022
1 parent 816afa8 commit 44accec
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 111 deletions.
4 changes: 3 additions & 1 deletion api/prometheus/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ var registered map[string]interface{}

// Increment will increment the counter.
func Increment(name string) {
registered[name].(prometheus.Counter).Inc()
if _, ok := registered[name]; ok {
registered[name].(prometheus.Counter).Inc()
}
}

// Increment will add the referred value the counter.
Expand Down
11 changes: 11 additions & 0 deletions docker-compose.yml.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ services:
- "2380:2380"
command: sh -c 'apk update; apk add curl;etcd -listen-client-urls http://0.0.0.0:2379 -advertise-client-urls http://etcd:2379'
<<: *networks

traefik:
image: traefik:latest
command: --providers.docker
volumes:
- /var/run/docker.sock:/var/run/docker.sock

whoami:
image: traefik/whoami
labels:
- traefik.http.routers.whoami.rule=Host(`domain.com`)
networks:
your_network:
external: true
79 changes: 27 additions & 52 deletions plugins/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,69 +108,44 @@ func DefaultSouinPluginCallback(
) (e error) {
prometheus.Increment(prometheus.RequestCounter)
start := time.Now()
coalesceable := make(chan bool)
responses := make(chan *http.Response)
defer func() {
close(coalesceable)
close(responses)
}()
cacheCandidate := !strings.Contains(req.Header.Get("Cache-Control"), "no-cache")
cacheKey := req.Context().Value(context.Key).(string)
retriever.SetMatchedURLFromRequest(req)

go func() {
defer func() {
_ = recover()
}()
coalesceable <- retriever.GetTransport().GetCoalescingLayerStorage().Exists(cacheKey)
}()

if cacheCandidate {
go func() {
defer func() {
_ = recover()
}()
r, _ := rfc.CachedResponse(
retriever.GetProvider(),
req,
cacheKey,
retriever.GetTransport(),
false,
)

responses <- rfc.ValidateMaxAgeCachedResponse(req, r)

r, _ = rfc.CachedResponse(
retriever.GetProvider(),
req,
"STALE_"+cacheKey,
retriever.GetTransport(),
false,
)

responses <- rfc.ValidateStaleCachedResponse(req, r)
}()
}

if cacheCandidate {
response, open := <-responses
if open && nil != response {
rh := response.Header
rfc.HitCache(&rh, retriever.GetMatchedURL().TTL.Duration)
prometheus.Increment(prometheus.CachedResponseCounter)
sendAnyCachedResponse(rh, response, res)
return
r, stale, err := rfc.CachedResponse(
retriever.GetProvider(),
req,
cacheKey,
retriever.GetTransport(),
)
if err != nil {
retriever.GetConfiguration().GetLogger().Sugar().Debugf("An error ocurred while retrieving the (stale)? key %s: %v", cacheKey, err)
return err
}

response, open = <-responses
if open && nil != response {
rh := response.Header
rfc.HitStaleCache(&rh, retriever.GetMatchedURL().TTL.Duration)
sendAnyCachedResponse(rh, response, res)
if r != nil {
rh := r.Header
if stale {
rfc.HitStaleCache(&rh, retriever.GetMatchedURL().TTL.Duration)
} else {
rfc.HitCache(&rh, retriever.GetMatchedURL().TTL.Duration)
prometheus.Increment(prometheus.CachedResponseCounter)
}
sendAnyCachedResponse(rh, r, res)

return
}
}

coalesceable := make(chan bool)
defer close(coalesceable)
go func() {
defer func() {
_ = recover()
}()
coalesceable <- retriever.GetTransport().GetCoalescingLayerStorage().Exists(cacheKey)
}()
prometheus.Increment(prometheus.NoCachedResponseCounter)
if <-coalesceable && rc != nil {
rc.Temporize(req, res, nextMiddleware)
Expand Down
11 changes: 10 additions & 1 deletion plugins/echo/souin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package souin

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/darkweak/souin/configurationtypes"
"github.com/darkweak/souin/plugins"
"github.com/labstack/echo/v4"
)

Expand Down Expand Up @@ -91,7 +95,11 @@ func Test_SouinEchoPlugin_Process_APIHandle(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/souin-api/souin", nil)
req.Header = http.Header{}
res := httptest.NewRecorder()
s := New(DevDefaultConfiguration)
dc := DevDefaultConfiguration
dc.DefaultCache.Nuts = configurationtypes.CacheProvider{
Path: "/tmp/souin" + time.Now().UTC().String(),
}
s := New(dc)

e := echo.New()
c := e.NewContext(req, res)
Expand All @@ -107,6 +115,7 @@ func Test_SouinEchoPlugin_Process_APIHandle(t *testing.T) {
}
b, _ := ioutil.ReadAll(res.Result().Body)
defer res.Result().Body.Close()
fmt.Println(string(b))
if string(b) != "[]" {
t.Error("The response body must be an empty array because no request has been stored")
}
Expand Down
1 change: 1 addition & 0 deletions plugins/gin/souin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http/httptest"
"testing"

"github.com/darkweak/souin/plugins"
"github.com/gin-gonic/gin"
)

Expand Down
3 changes: 2 additions & 1 deletion plugins/goyave/souin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"testing"
"time"

"github.com/darkweak/souin/plugins"
"goyave.dev/goyave/v4"
"goyave.dev/goyave/v4/config"
)
Expand Down Expand Up @@ -123,7 +124,7 @@ func (suite *HttpCacheMiddlewareTestSuite) Test_SouinFiberPlugin_Middleware_APIH
if string(b) != "[]" {
suite.T().Error("The response body must be an empty array because no request has been stored")
}
res = suite.Middleware(httpcache.Handle, suite.CreateTestRequest(httptest.NewRequest(http.MethodGet, "/handled", nil)), func(response *goyave.Response, r *goyave.Request) {
_ = suite.Middleware(httpcache.Handle, suite.CreateTestRequest(httptest.NewRequest(http.MethodGet, "/handled", nil)), func(response *goyave.Response, r *goyave.Request) {
response.String(http.StatusOK, "Hello, World 👋!")
})
res = suite.Middleware(httpcache.Handle, SouinAPIRequest, func(response *goyave.Response, r *goyave.Request) {})
Expand Down
1 change: 1 addition & 0 deletions plugins/souin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
_ "net/http/pprof"
"net/url"
"time"

Expand Down
56 changes: 56 additions & 0 deletions plugins/souin/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"fmt"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"

"github.com/darkweak/souin/cache/coalescing"
"github.com/darkweak/souin/cache/service"
"github.com/darkweak/souin/errors"
"github.com/darkweak/souin/plugins"
"github.com/darkweak/souin/plugins/souin/configuration"
souintypes "github.com/darkweak/souin/plugins/souin/types"
)

func Benchmark_Souin_Handler(b *testing.B) {
c := configuration.GetConfiguration()
fmt.Printf("%+v\n", c)
rc := coalescing.Initialize()
retriever := souinPluginInitializerFromConfiguration(c)
for i := 0; i < b.N; i++ {
writer := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "http://domain.com/"+strconv.Itoa(i), nil)
request.Header.Set("Date", time.Now().UTC().Format(time.RFC1123))
request = retriever.GetContext().Method.SetContext(request)

if !plugins.CanHandle(request, retriever) {
writer.Header().Set("Cache-Status", "Souin; fwd=uri-miss")
return
}

request = retriever.GetContext().SetContext(request)
callback := func(rw http.ResponseWriter, rq *http.Request, ret souintypes.SouinRetrieverResponseProperties) error {
rr := service.RequestReverseProxy(rq, ret)
select {
case <-rq.Context().Done():
c.GetLogger().Debug("The request was canceled by the user.")
return &errors.CanceledRequestContextError{}
default:
rr.Proxy.ServeHTTP(rw, rq)
}

return nil
}
if plugins.HasMutation(request, writer) {
_ = callback(writer, request, *retriever)
}
retriever.SetMatchedURLFromRequest(request)
coalescing.ServeResponse(writer, request, retriever, plugins.DefaultSouinPluginCallback, rc, func(_ http.ResponseWriter, _ *http.Request) error {
return callback(writer, request, *retriever)
})
}
}
48 changes: 20 additions & 28 deletions plugins/traefik/vendor/github.com/darkweak/souin/rfc/bridge.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions plugins/webgo/souin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"

"github.com/bnkamalesh/webgo/v6"
"github.com/darkweak/souin/plugins"
)

func Test_NewHTTPCache(t *testing.T) {
Expand Down Expand Up @@ -61,6 +63,17 @@ func prepare() (res *httptest.ResponseRecorder, res2 *httptest.ResponseRecorder,
return
}

func Benchmark_SouinWebgoPlugin_Middleware(b *testing.B) {
for i := 0; i < b.N; i++ {
res := httptest.NewRecorder()
httpcache := NewHTTPCache(DevDefaultConfiguration)
httpcache.Middleware(res, httptest.NewRequest(http.MethodGet, "/handled"+strconv.Itoa(i), nil), func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Returns something"))
})
}
}

func Test_SouinWebgoPlugin_Middleware(t *testing.T) {
res, res2, router := prepare()
req := httptest.NewRequest(http.MethodGet, "/handled", nil)
Expand Down
Loading

0 comments on commit 44accec

Please sign in to comment.