Skip to content

Commit

Permalink
Add is_cache_enabled boolean flag
Browse files Browse the repository at this point in the history
  • Loading branch information
evgeniy-scherbina committed Oct 19, 2023
1 parent 667fa36 commit 5e6ad5e
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 22 deletions.
12 changes: 7 additions & 5 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,18 @@ METRIC_PARTITIONING_ROUTINE_DELAY_FIRST_RUN_SECONDS=10
METRIC_PARTITIONINING_PREFILL_PERIOD_DAYS=7
# Used by `ready` script to ensure metric partitions have been created.
MINIMUM_REQUIRED_PARTITIONS=30
# RedisEndpointURL is an url of redis
# CACHE_ENABLED specifies if cache should be enabled. By default cache is disabled.
CACHE_ENABLED=true
# REDIS_ENDPOINT_URL is an url of redis
REDIS_ENDPOINT_URL=redis:6379
REDIS_PASSWORD=
# TTL for cached evm requests
# TTL should be specified in seconds
# CACHE_TTL is a TTL for cached evm requests
# CACHE_TTL should be specified in seconds
CACHE_TTL=600
# CachePrefix is used as prefix for any key in the cache, key has such structure:
# CACHE_PREFIX is used as prefix for any key in the cache, key has such structure:
# <cache_prefix>:evm-request:<method_name>:sha256:<sha256(body)>
# Possible values are testnet, mainnet, etc...
# CachePrefix must not contain colon symbol
# CACHE_PREFIX must not contain colon symbol
CACHE_PREFIX=local-chain

##### Database Config
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Config struct {
MetricPartitioningRoutineInterval time.Duration
MetricPartitioningRoutineDelayFirstRun time.Duration
MetricPartitioningPrefillPeriodDays int
CacheEnabled bool
RedisEndpointURL string
RedisPassword string
CacheTTL time.Duration
Expand Down Expand Up @@ -83,6 +84,7 @@ const (
DEFAULT_DATABASE_READ_TIMEOUT_SECONDS = 60
DATABASE_WRITE_TIMEOUT_SECONDS_ENVIRONMENT_KEY = "DATABASE_WRITE_TIMEOUT_SECONDS"
DEFAULT_DATABASE_WRITE_TIMEOUT_SECONDS = 10
CACHE_ENABLED_ENVIRONMENT_KEY = "CACHE_ENABLED"
REDIS_ENDPOINT_URL_ENVIRONMENT_KEY = "REDIS_ENDPOINT_URL"
REDIS_PASSWORD_ENVIRONMENT_KEY = "REDIS_PASSWORD"
CACHE_TTL_ENVIRONMENT_KEY = "CACHE_TTL"
Expand Down Expand Up @@ -212,6 +214,7 @@ func ReadConfig() Config {
MetricPartitioningRoutineInterval: time.Duration(time.Duration(EnvOrDefaultInt(METRIC_PARTITIONING_ROUTINE_INTERVAL_SECONDS_ENVIRONMENT_KEY, DEFAULT_METRIC_PARTITIONING_ROUTINE_INTERVAL_SECONDS)) * time.Second),
MetricPartitioningRoutineDelayFirstRun: time.Duration(time.Duration(EnvOrDefaultInt(METRIC_PARTITIONING_ROUTINE_DELAY_FIRST_RUN_SECONDS_ENVIRONMENT_KEY, DEFAULT_METRIC_PARTITIONING_ROUTINE_DELAY_FIRST_RUN_SECONDS)) * time.Second),
MetricPartitioningPrefillPeriodDays: EnvOrDefaultInt(METRIC_PARTITIONING_PREFILL_PERIOD_DAYS_ENVIRONMENT_KEY, DEFAULT_METRIC_PARTITIONING_PREFILL_PERIOD_DAYS),
CacheEnabled: EnvOrDefaultBool(CACHE_ENABLED_ENVIRONMENT_KEY, false),
RedisEndpointURL: os.Getenv(REDIS_ENDPOINT_URL_ENVIRONMENT_KEY),
RedisPassword: os.Getenv(REDIS_PASSWORD_ENVIRONMENT_KEY),
CacheTTL: time.Duration(EnvOrDefaultInt(CACHE_TTL_ENVIRONMENT_KEY, 0)) * time.Second,
Expand Down
5 changes: 4 additions & 1 deletion service/cachemdw/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ type ServiceCache struct {
cacheTTL time.Duration
decodedRequestContextKey any
// cachePrefix is used as prefix for any key in the cache
cachePrefix string
cachePrefix string
cacheEnabled bool

*logging.ServiceLogger
}
Expand All @@ -31,6 +32,7 @@ func NewServiceCache(
cacheTTL time.Duration,
decodedRequestContextKey any,
cachePrefix string,
cacheEnabled bool,
logger *logging.ServiceLogger,
) *ServiceCache {
return &ServiceCache{
Expand All @@ -39,6 +41,7 @@ func NewServiceCache(
cacheTTL: cacheTTL,
decodedRequestContextKey: decodedRequestContextKey,
cachePrefix: cachePrefix,
cacheEnabled: cacheEnabled,
ServiceLogger: logger,
}
}
Expand Down
10 changes: 9 additions & 1 deletion service/cachemdw/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,15 @@ func TestUnitTestCacheQueryResponse(t *testing.T) {
cacheTTL := time.Hour
ctxb := context.Background()

serviceCache := cachemdw.NewServiceCache(inMemoryCache, blockGetter, cacheTTL, service.DecodedRequestContextKey, defaultCachePrefixString, &logger)
serviceCache := cachemdw.NewServiceCache(
inMemoryCache,
blockGetter,
cacheTTL,
service.DecodedRequestContextKey,
defaultCachePrefixString,
true,
&logger,
)

req := mkEVMRPCRequestEnvelope(defaultBlockNumber)
resp, err := serviceCache.GetCachedQueryResponse(ctxb, req)
Expand Down
6 changes: 6 additions & 0 deletions service/cachemdw/caching_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ func (c *ServiceCache) CachingMiddleware(
next http.Handler,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// if cache is not enabled - do nothing and forward to next middleware
if !c.cacheEnabled {
next.ServeHTTP(w, r)
return
}

// if we can't get decoded request then forward to next middleware
req := r.Context().Value(c.decodedRequestContextKey)
decodedReq, ok := (req).(*decode.EVMRPCRequestEnvelope)
Expand Down
6 changes: 6 additions & 0 deletions service/cachemdw/is_cached_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ func (c *ServiceCache) IsCachedMiddleware(
next http.Handler,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// if cache is not enabled - do nothing and forward to next middleware
if !c.cacheEnabled {
next.ServeHTTP(w, r)
return
}

uncachedContext := context.WithValue(r.Context(), CachedContextKey, false)
cachedContext := context.WithValue(r.Context(), CachedContextKey, true)

Expand Down
10 changes: 9 additions & 1 deletion service/cachemdw/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ func TestE2ETestServiceCacheMiddleware(t *testing.T) {
blockGetter := NewMockEVMBlockGetter()
cacheTTL := time.Duration(0) // TTL: no expiry

serviceCache := cachemdw.NewServiceCache(inMemoryCache, blockGetter, cacheTTL, service.DecodedRequestContextKey, defaultCachePrefixString, &logger)
serviceCache := cachemdw.NewServiceCache(
inMemoryCache,
blockGetter,
cacheTTL,
service.DecodedRequestContextKey,
defaultCachePrefixString,
true,
&logger,
)

emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
cachingMdw := serviceCache.CachingMiddleware(emptyHandler)
Expand Down
31 changes: 17 additions & 14 deletions service/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ func createProxyRequestMiddleware(next http.Handler, config config.Config, servi
response := r.Context().Value(cachemdw.ResponseContextKey)
typedResponse, ok := response.([]byte)

// if request is cached and response is present in context - serve the request from the cache
// if cache is enabled, request is cached and response is present in context - serve the request from the cache
// otherwise proxy to the actual backend
if isCached && ok {
if config.CacheEnabled && isCached && ok {
serviceLogger.Logger.Trace().
Str("method", r.Method).
Str("url", r.URL.String()).
Expand Down Expand Up @@ -271,20 +271,23 @@ func createProxyRequestMiddleware(next http.Handler, config config.Config, servi
// extract the original hostname the request was sent to
requestHostnameContext := context.WithValue(originRoundtripLatencyContext, RequestHostnameContextKey, r.Host)

var bodyCopy bytes.Buffer
tee := io.TeeReader(lrw.body, &bodyCopy)
// read all body from reader into bodyBytes, and copy into bodyCopy
bodyBytes, err := io.ReadAll(tee)
if err != nil {
serviceLogger.Error().Err(err).Msg("can't read lrw.body")
}
enrichedContext := requestHostnameContext

// replace empty body reader with fresh copy
lrw.body = &bodyCopy
// set body in context
responseContext := context.WithValue(requestHostnameContext, cachemdw.ResponseContextKey, bodyBytes)
// if cache is enabled, update enrichedContext with cachemdw.ResponseContextKey -> bodyBytes key-value pair
if config.CacheEnabled {
var bodyCopy bytes.Buffer
tee := io.TeeReader(lrw.body, &bodyCopy)
// read all body from reader into bodyBytes, and copy into bodyCopy
bodyBytes, err := io.ReadAll(tee)
if err != nil {
serviceLogger.Error().Err(err).Msg("can't read lrw.body")
}

enrichedContext := responseContext
// replace empty body reader with fresh copy
lrw.body = &bodyCopy
// set body in context
enrichedContext = context.WithValue(enrichedContext, cachemdw.ResponseContextKey, bodyBytes)
}

// parse the remote address of the request for use below
remoteAddressParts := strings.Split(r.RemoteAddr, ":")
Expand Down
1 change: 1 addition & 0 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func createServiceCache(
config.CacheTTL,
DecodedRequestContextKey,
config.CachePrefix,
config.CacheEnabled,
logger,
)

Expand Down

0 comments on commit 5e6ad5e

Please sign in to comment.