From 43ff96b7a85d6c928bf9b24ab6d1e1c4cf0ba032 Mon Sep 17 00:00:00 2001 From: Trajan0x Date: Fri, 3 May 2024 20:23:11 +0200 Subject: [PATCH] cache api result [goreleaser] --- services/rfq/api/rest/server.go | 54 +++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/services/rfq/api/rest/server.go b/services/rfq/api/rest/server.go index 76dd5b4eb3..93d74511e0 100644 --- a/services/rfq/api/rest/server.go +++ b/services/rfq/api/rest/server.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/gin-gonic/gin" + "github.com/jellydator/ttlcache/v3" "github.com/synapsecns/sanguine/core/metrics" baseServer "github.com/synapsecns/sanguine/core/server" omniClient "github.com/synapsecns/sanguine/services/omnirpc/client" @@ -35,6 +36,7 @@ type QuoterAPIServer struct { omnirpcClient omniClient.RPCClient handler metrics.Handler fastBridgeContracts map[uint32]*fastbridge.FastBridge + roleCache map[uint32]*ttlcache.Cache[string, bool] } // NewAPI holds the configuration, database connection, gin engine, RPC client, metrics handler, and fast bridge contracts. @@ -62,6 +64,7 @@ func NewAPI( docs.SwaggerInfo.Title = "RFQ Quoter API" bridges := make(map[uint32]*fastbridge.FastBridge) + roles := make(map[uint32]*ttlcache.Cache[string, bool]) for chainID, bridge := range cfg.Bridges { chainClient, err := omniRPCClient.GetChainClient(ctx, int(chainID)) if err != nil { @@ -71,6 +74,27 @@ func NewAPI( if err != nil { return nil, fmt.Errorf("could not create bridge contract: %w", err) } + + // create the roles cache + roles[chainID] = ttlcache.New[string, bool]( + ttlcache.WithTTL[string, bool](cacheInterval), + ) + // purge periodically + const cachePurgeInterval = 5 * time.Second + cacheTimer := time.NewTimer(cachePurgeInterval) + + roleCache := roles[chainID] + + go func() { + select { + case <-ctx.Done(): + cacheTimer.Stop() + case <-cacheTimer.C: + cacheTimer.Reset(cachePurgeInterval) + roleCache.DeleteExpired() + } + }() + } return &QuoterAPIServer{ @@ -79,12 +103,14 @@ func NewAPI( omnirpcClient: omniRPCClient, handler: handler, fastBridgeContracts: bridges, + roleCache: roles, }, nil } // QuoteRoute is the API endpoint for handling quote related requests. const ( - QuoteRoute = "/quotes" + QuoteRoute = "/quotes" + cacheInterval = time.Minute ) var logger = log.Logger("rfq-api") @@ -145,15 +171,23 @@ func (r *QuoterAPIServer) AuthMiddleware() gin.HandlerFunc { return } - has, err := bridge.HasRole(ops, relayerRole, addressRecovered) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"msg": "unable to check relayer role on-chain"}) - c.Abort() - return - } else if !has { - c.JSON(http.StatusBadRequest, gin.H{"msg": "q.Relayer not an on-chain relayer"}) - c.Abort() - return + hasRole := r.roleCache[uint32(req.DestChainID)].Get(addressRecovered.Hex()) + + if hasRole == nil || hasRole.IsExpired() { + has, err := bridge.HasRole(ops, relayerRole, addressRecovered) + if err == nil { + r.roleCache[uint32(req.DestChainID)].Set(addressRecovered.Hex(), has, cacheInterval) + } + + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"msg": "unable to check relayer role on-chain"}) + c.Abort() + return + } else if !has { + c.JSON(http.StatusBadRequest, gin.H{"msg": "q.Relayer not an on-chain relayer"}) + c.Abort() + return + } } // Log and pass to the next middleware if authentication succeeds