diff --git a/domain/url.go b/domain/url.go new file mode 100644 index 000000000..e59916f85 --- /dev/null +++ b/domain/url.go @@ -0,0 +1,36 @@ +package domain + +import ( + "context" + "net/url" + + "github.com/labstack/echo" +) + +// RequestPathKeyType is a custom type for request path key. +type RequestPathKeyType string + +const ( + // RequestPathCtxKey is the key used to store the request path in the request context + RequestPathCtxKey RequestPathKeyType = "request_path" +) + +// ParseURLPath parses the URL path from the echo context +func ParseURLPath(c echo.Context) (string, error) { + parsedURL, err := url.Parse(c.Request().RequestURI) + if err != nil { + return "", err + } + + return parsedURL.Path, nil +} + +// GetURLPathFromContext returns the request path from the context +func GetURLPathFromContext(ctx context.Context) (string, error) { + // Get request path for metrics + requestPath, ok := ctx.Value(RequestPathCtxKey).(string) + if !ok || (ok && len(requestPath) == 0) { + requestPath = "unknown" + } + return requestPath, nil +} diff --git a/middleware/middleware.go b/middleware/middleware.go index 8f282b0b6..d6edbc621 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -1,11 +1,13 @@ package middleware import ( - "net/url" + "context" "time" "github.com/labstack/echo" "github.com/prometheus/client_golang/prometheus" + + "github.com/osmosis-labs/osmosis/v21/ingest/sqs/domain" ) // GoMiddleware represent the data-struct for middleware @@ -57,17 +59,21 @@ func (m *GoMiddleware) InstrumentMiddleware(next echo.HandlerFunc) echo.HandlerF return func(c echo.Context) error { start := time.Now() - parsedURL, err := url.Parse(c.Request().RequestURI) + requestMethod := c.Request().Method + requestPath, err := domain.ParseURLPath(c) if err != nil { return err } - requestMethod := c.Request().Method - requestPath := parsedURL.Path - // Increment the request counter requestsTotal.WithLabelValues(requestMethod, requestPath).Inc() + // Insert the request path into the context + ctx := c.Request().Context() + ctx = context.WithValue(ctx, domain.RequestPathCtxKey, requestPath) + request := c.Request().WithContext(ctx) + c.SetRequest(request) + err = next(c) duration := time.Since(start).Seconds() diff --git a/router/usecase/router_usecase.go b/router/usecase/router_usecase.go index 80f145389..8ca31a86d 100644 --- a/router/usecase/router_usecase.go +++ b/router/usecase/router_usecase.go @@ -7,6 +7,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" "github.com/osmosis-labs/sqs/domain" @@ -34,6 +35,33 @@ type routerUseCaseImpl struct { rankedRouteCache *cache.Cache } +const ( + candidateRouteCacheLabel = "candidate_route" + rankedRouteCacheLabel = "ranked_route" +) + +var ( + cacheHits = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "sqs_cache_hits_total", + Help: "Total number of cache hits", + }, + []string{"route", "cache_type", "token_in", "token_out"}, + ) + cacheMisses = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "sqs_cache_misses_total", + Help: "Total number of cache misses", + }, + []string{"route", "cache_type", "token_in", "token_out"}, + ) +) + +func init() { + prometheus.MustRegister(cacheHits) + prometheus.MustRegister(cacheMisses) +} + // NewRouterUsecase will create a new pools use case object func NewRouterUsecase(timeout time.Duration, routerRepository routerredisrepo.RouterRepository, poolsUsecase mvc.PoolsUsecase, config domain.RouterConfig, logger log.Logger, rankedRouteCache *cache.Cache) mvc.RouterUsecase { return &routerUseCaseImpl{ @@ -57,7 +85,6 @@ func NewRouterUsecase(timeout time.Duration, routerRepository routerredisrepo.Ro // Returns error if: // - fails to estimate direct quotes for ranked routes // - fails to retrieve candidate routes -// - func (r *routerUseCaseImpl) GetOptimalQuote(ctx context.Context, tokenIn sdk.Coin, tokenOutDenom string) (domain.Quote, error) { // Get an order of magnitude for the token in amount // This is used for caching ranked routes as these might differ depending on the amount swapped in. @@ -73,7 +100,16 @@ func (r *routerUseCaseImpl) GetOptimalQuote(ctx context.Context, tokenIn sdk.Coi router := r.initializeRouter() + // Get request path for metrics + requestURLPath, err := domain.GetURLPathFromContext(ctx) + if err != nil { + return nil, err + } + if hasRankedRoutesInCache { + // Increase cache hits + cacheHits.WithLabelValues(requestURLPath, rankedRouteCacheLabel, tokenIn.Denom, tokenOutDenom).Inc() + rankedCandidateRoutes, ok := rankedRoutesData.(sqsdomain.CandidateRoutes) if !ok { return nil, fmt.Errorf("error casting ranked routes from cache") @@ -85,6 +121,9 @@ func (r *routerUseCaseImpl) GetOptimalQuote(ctx context.Context, tokenIn sdk.Coi return nil, err } } else { + // Increase cache misses + cacheMisses.WithLabelValues(requestURLPath, rankedRouteCacheLabel, tokenIn.Denom, tokenOutDenom).Inc() + // If top routes are not present in cache, retrieve unranked candidate routes candidateRoutes, err := r.handleCandidateRoutes(ctx, router, tokenIn.Denom, tokenOutDenom) if err != nil {