Commit 4b5422be authored by Felipe Andrade's avatar Felipe Andrade

refactor(proxy): cache only immutable and hash-based methods

parent 382d38b7
...@@ -116,15 +116,17 @@ type rpcCache struct { ...@@ -116,15 +116,17 @@ type rpcCache struct {
handlers map[string]RPCMethodHandler handlers map[string]RPCMethodHandler
} }
func newRPCCache(cache Cache, getLatestBlockNumFn GetLatestBlockNumFn, getLatestGasPriceFn GetLatestGasPriceFn, numBlockConfirmations int) RPCCache { func newRPCCache(cache Cache) RPCCache {
handlers := map[string]RPCMethodHandler{ handlers := map[string]RPCMethodHandler{
"eth_chainId": &StaticMethodHandler{}, "eth_chainId": &StaticMethodHandler{cache: cache},
"net_version": &StaticMethodHandler{}, "net_version": &StaticMethodHandler{cache: cache},
"eth_getBlockByNumber": &EthGetBlockByNumberMethodHandler{cache, getLatestBlockNumFn, numBlockConfirmations}, "eth_getBlockTransactionCountByHash": &StaticMethodHandler{cache: cache},
"eth_getBlockRange": &EthGetBlockRangeMethodHandler{cache, getLatestBlockNumFn, numBlockConfirmations}, "eth_getUncleCountByBlockHash": &StaticMethodHandler{cache: cache},
"eth_blockNumber": &EthBlockNumberMethodHandler{getLatestBlockNumFn}, "eth_getBlockByHash": &StaticMethodHandler{cache: cache},
"eth_gasPrice": &EthGasPriceMethodHandler{getLatestGasPriceFn}, "eth_getTransactionByHash": &StaticMethodHandler{cache: cache},
"eth_call": &EthCallMethodHandler{cache, getLatestBlockNumFn, numBlockConfirmations}, "eth_getTransactionByBlockHashAndIndex": &StaticMethodHandler{cache: cache},
"eth_getUncleByBlockHashAndIndex": &StaticMethodHandler{cache: cache},
"eth_getTransactionReceipt": &StaticMethodHandler{cache: cache},
} }
return &rpcCache{ return &rpcCache{
cache: cache, cache: cache,
...@@ -138,13 +140,15 @@ func (c *rpcCache) GetRPC(ctx context.Context, req *RPCReq) (*RPCRes, error) { ...@@ -138,13 +140,15 @@ func (c *rpcCache) GetRPC(ctx context.Context, req *RPCReq) (*RPCRes, error) {
return nil, nil return nil, nil
} }
res, err := handler.GetRPCMethod(ctx, req) res, err := handler.GetRPCMethod(ctx, req)
if res != nil { if err != nil {
RecordCacheError(req.Method)
return nil, err
}
if res == nil { if res == nil {
RecordCacheMiss(req.Method) RecordCacheMiss(req.Method)
} else { } else {
RecordCacheHit(req.Method) RecordCacheHit(req.Method)
} }
}
return res, err return res, err
} }
......
This diff is collapsed.
...@@ -18,15 +18,19 @@ func TestCaching(t *testing.T) { ...@@ -18,15 +18,19 @@ func TestCaching(t *testing.T) {
defer redis.Close() defer redis.Close()
hdlr := NewBatchRPCResponseRouter() hdlr := NewBatchRPCResponseRouter()
/* cacheable */
hdlr.SetRoute("eth_chainId", "999", "0x420") hdlr.SetRoute("eth_chainId", "999", "0x420")
hdlr.SetRoute("net_version", "999", "0x1234") hdlr.SetRoute("net_version", "999", "0x1234")
hdlr.SetRoute("eth_blockNumber", "999", "0x64") hdlr.SetRoute("eth_getBlockTransactionCountByHash", "999", "eth_getBlockTransactionCountByHash")
hdlr.SetRoute("eth_getBlockByNumber", "999", "dummy_block") hdlr.SetRoute("eth_getBlockByHash", "999", "eth_getBlockByHash")
hdlr.SetRoute("eth_call", "999", "dummy_call") hdlr.SetRoute("eth_getTransactionByHash", "999", "eth_getTransactionByHash")
hdlr.SetRoute("eth_getTransactionByBlockHashAndIndex", "999", "eth_getTransactionByBlockHashAndIndex")
// mock LVC requests hdlr.SetRoute("eth_getUncleByBlockHashAndIndex", "999", "eth_getUncleByBlockHashAndIndex")
hdlr.SetFallbackRoute("eth_blockNumber", "0x64") hdlr.SetRoute("eth_getTransactionReceipt", "999", "eth_getTransactionReceipt")
hdlr.SetFallbackRoute("eth_gasPrice", "0x420") /* not cacheable */
hdlr.SetRoute("eth_getBlockByNumber", "999", "eth_getBlockByNumber")
hdlr.SetRoute("eth_blockNumber", "999", "eth_blockNumber")
hdlr.SetRoute("eth_call", "999", "eth_call")
backend := NewMockBackend(hdlr) backend := NewMockBackend(hdlr)
defer backend.Close() defer backend.Close()
...@@ -48,6 +52,7 @@ func TestCaching(t *testing.T) { ...@@ -48,6 +52,7 @@ func TestCaching(t *testing.T) {
response string response string
backendCalls int backendCalls int
}{ }{
/* cacheable */
{ {
"eth_chainId", "eth_chainId",
nil, nil,
...@@ -60,14 +65,51 @@ func TestCaching(t *testing.T) { ...@@ -60,14 +65,51 @@ func TestCaching(t *testing.T) {
"{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 999}", "{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 999}",
1, 1,
}, },
{
"eth_getBlockTransactionCountByHash",
[]interface{}{"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getBlockTransactionCountByHash\", \"id\": 999}",
1,
},
{
"eth_getBlockByHash",
[]interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "false"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getBlockByHash\", \"id\": 999}",
1,
},
{
"eth_getTransactionByHash",
[]interface{}{"0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getTransactionByHash\", \"id\": 999}",
1,
},
{
"eth_getTransactionByBlockHashAndIndex",
[]interface{}{"0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", "0x55"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getTransactionByBlockHashAndIndex\", \"id\": 999}",
1,
},
{
"eth_getUncleByBlockHashAndIndex",
[]interface{}{"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238", "0x90"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getUncleByBlockHashAndIndex\", \"id\": 999}",
1,
},
{
"eth_getTransactionReceipt",
[]interface{}{"0x85d995eba9763907fdf35cd2034144dd9d53ce32cbec21349d4b12823c6860c5"},
"{\"jsonrpc\": \"2.0\", \"result\": \"eth_getTransactionReceipt\", \"id\": 999}",
1,
},
/* not cacheable */
{ {
"eth_getBlockByNumber", "eth_getBlockByNumber",
[]interface{}{ []interface{}{
"0x1", "0x1",
true, true,
}, },
"{\"jsonrpc\": \"2.0\", \"result\": \"dummy_block\", \"id\": 999}", "{\"jsonrpc\": \"2.0\", \"result\": \"eth_getBlockByNumber\", \"id\": 999}",
1, 2,
}, },
{ {
"eth_call", "eth_call",
...@@ -79,14 +121,14 @@ func TestCaching(t *testing.T) { ...@@ -79,14 +121,14 @@ func TestCaching(t *testing.T) {
}, },
"0x60", "0x60",
}, },
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}", "{\"jsonrpc\": \"2.0\", \"result\": \"eth_call\", \"id\": 999}",
1, 2,
}, },
{ {
"eth_blockNumber", "eth_blockNumber",
nil, nil,
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"0x64\"}", "{\"jsonrpc\": \"2.0\", \"result\": \"eth_blockNumber\", \"id\": 999}",
0, 2,
}, },
{ {
"eth_call", "eth_call",
...@@ -98,7 +140,7 @@ func TestCaching(t *testing.T) { ...@@ -98,7 +140,7 @@ func TestCaching(t *testing.T) {
}, },
"latest", "latest",
}, },
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}", "{\"jsonrpc\": \"2.0\", \"result\": \"eth_call\", \"id\": 999}",
2, 2,
}, },
{ {
...@@ -111,7 +153,7 @@ func TestCaching(t *testing.T) { ...@@ -111,7 +153,7 @@ func TestCaching(t *testing.T) {
}, },
"pending", "pending",
}, },
"{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"dummy_call\"}", "{\"jsonrpc\": \"2.0\", \"result\": \"eth_call\", \"id\": 999}",
2, 2,
}, },
} }
...@@ -128,24 +170,15 @@ func TestCaching(t *testing.T) { ...@@ -128,24 +170,15 @@ func TestCaching(t *testing.T) {
}) })
} }
t.Run("block numbers update", func(t *testing.T) {
hdlr.SetFallbackRoute("eth_blockNumber", "0x100")
time.Sleep(1500 * time.Millisecond)
resRaw, _, err := client.SendRPC("eth_blockNumber", nil)
require.NoError(t, err)
RequireEqualJSON(t, []byte("{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":\"0x100\"}"), resRaw)
backend.Reset()
})
t.Run("nil responses should not be cached", func(t *testing.T) { t.Run("nil responses should not be cached", func(t *testing.T) {
hdlr.SetRoute("eth_getBlockByNumber", "999", nil) hdlr.SetRoute("eth_getBlockByHash", "999", nil)
resRaw, _, err := client.SendRPC("eth_getBlockByNumber", []interface{}{"0x123"}) resRaw, _, err := client.SendRPC("eth_getBlockByHash", []interface{}{"0x123"})
require.NoError(t, err) require.NoError(t, err)
resCache, _, err := client.SendRPC("eth_getBlockByNumber", []interface{}{"0x123"}) resCache, _, err := client.SendRPC("eth_getBlockByHash", []interface{}{"0x123"})
require.NoError(t, err) require.NoError(t, err)
RequireEqualJSON(t, []byte("{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":null}"), resRaw) RequireEqualJSON(t, []byte("{\"id\":999,\"jsonrpc\":\"2.0\",\"result\":null}"), resRaw)
RequireEqualJSON(t, resRaw, resCache) RequireEqualJSON(t, resRaw, resCache)
require.Equal(t, 2, countRequests(backend, "eth_getBlockByNumber")) require.Equal(t, 2, countRequests(backend, "eth_getBlockByHash"))
}) })
} }
...@@ -158,10 +191,7 @@ func TestBatchCaching(t *testing.T) { ...@@ -158,10 +191,7 @@ func TestBatchCaching(t *testing.T) {
hdlr.SetRoute("eth_chainId", "1", "0x420") hdlr.SetRoute("eth_chainId", "1", "0x420")
hdlr.SetRoute("net_version", "1", "0x1234") hdlr.SetRoute("net_version", "1", "0x1234")
hdlr.SetRoute("eth_call", "1", "dummy_call") hdlr.SetRoute("eth_call", "1", "dummy_call")
hdlr.SetRoute("eth_getBlockByHash", "1", "eth_getBlockByHash")
// mock LVC requests
hdlr.SetFallbackRoute("eth_blockNumber", "0x64")
hdlr.SetFallbackRoute("eth_gasPrice", "0x420")
backend := NewMockBackend(hdlr) backend := NewMockBackend(hdlr)
defer backend.Close() defer backend.Close()
...@@ -181,26 +211,31 @@ func TestBatchCaching(t *testing.T) { ...@@ -181,26 +211,31 @@ func TestBatchCaching(t *testing.T) {
goodChainIdResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x420\", \"id\": 1}" goodChainIdResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x420\", \"id\": 1}"
goodNetVersionResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 1}" goodNetVersionResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 1}"
goodEthCallResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"dummy_call\", \"id\": 1}" goodEthCallResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"dummy_call\", \"id\": 1}"
goodEthGetBlockByHash := "{\"jsonrpc\": \"2.0\", \"result\": \"eth_getBlockByHash\", \"id\": 1}"
res, _, err := client.SendBatchRPC( res, _, err := client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil), NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "net_version", nil), NewRPCReq("1", "net_version", nil),
NewRPCReq("1", "eth_getBlockByHash", []interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "false"}),
) )
require.NoError(t, err) require.NoError(t, err)
RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodNetVersionResponse)), res) RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodNetVersionResponse, goodEthGetBlockByHash)), res)
require.Equal(t, 1, countRequests(backend, "eth_chainId")) require.Equal(t, 1, countRequests(backend, "eth_chainId"))
require.Equal(t, 1, countRequests(backend, "net_version")) require.Equal(t, 1, countRequests(backend, "net_version"))
require.Equal(t, 1, countRequests(backend, "eth_getBlockByHash"))
backend.Reset() backend.Reset()
res, _, err = client.SendBatchRPC( res, _, err = client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil), NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "eth_call", []interface{}{`{"to":"0x1234"}`, "pending"}), NewRPCReq("1", "eth_call", []interface{}{`{"to":"0x1234"}`, "pending"}),
NewRPCReq("1", "net_version", nil), NewRPCReq("1", "net_version", nil),
NewRPCReq("1", "eth_getBlockByHash", []interface{}{"0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "false"}),
) )
require.NoError(t, err) require.NoError(t, err)
RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodEthCallResponse, goodNetVersionResponse)), res) RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodEthCallResponse, goodNetVersionResponse, goodEthGetBlockByHash)), res)
require.Equal(t, 0, countRequests(backend, "eth_chainId")) require.Equal(t, 0, countRequests(backend, "eth_chainId"))
require.Equal(t, 0, countRequests(backend, "net_version")) require.Equal(t, 0, countRequests(backend, "net_version"))
require.Equal(t, 0, countRequests(backend, "eth_getBlockByHash"))
require.Equal(t, 1, countRequests(backend, "eth_call")) require.Equal(t, 1, countRequests(backend, "eth_call"))
} }
......
...@@ -27,3 +27,10 @@ net_version = "main" ...@@ -27,3 +27,10 @@ net_version = "main"
eth_getBlockByNumber = "main" eth_getBlockByNumber = "main"
eth_blockNumber = "main" eth_blockNumber = "main"
eth_call = "main" eth_call = "main"
eth_getBlockTransactionCountByHash = "main"
eth_getUncleCountByBlockHash = "main"
eth_getBlockByHash = "main"
eth_getTransactionByHash = "main"
eth_getTransactionByBlockHashAndIndex = "main"
eth_getUncleByBlockHashAndIndex = "main"
eth_getTransactionReceipt = "main"
This diff is collapsed.
...@@ -182,6 +182,14 @@ var ( ...@@ -182,6 +182,14 @@ var (
"method", "method",
}) })
cacheErrorsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace,
Name: "cache_errors_total",
Help: "Number of cache errors.",
}, []string{
"method",
})
lvcErrorsTotal = promauto.NewCounterVec(prometheus.CounterOpts{ lvcErrorsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: MetricsNamespace, Namespace: MetricsNamespace,
Name: "lvc_errors_total", Name: "lvc_errors_total",
...@@ -374,6 +382,10 @@ func RecordCacheMiss(method string) { ...@@ -374,6 +382,10 @@ func RecordCacheMiss(method string) {
cacheMissesTotal.WithLabelValues(method).Inc() cacheMissesTotal.WithLabelValues(method).Inc()
} }
func RecordCacheError(method string) {
cacheMissesTotal.WithLabelValues(method).Inc()
}
func RecordBatchSize(size int) { func RecordBatchSize(size int) {
batchSizeHistogram.Observe(float64(size)) batchSizeHistogram.Observe(float64(size))
} }
......
...@@ -213,17 +213,10 @@ func Start(config *Config) (*Server, func(), error) { ...@@ -213,17 +213,10 @@ func Start(config *Config) (*Server, func(), error) {
} }
var ( var (
cache Cache
rpcCache RPCCache rpcCache RPCCache
blockNumLVC *EthLastValueCache
gasPriceLVC *EthLastValueCache
) )
if config.Cache.Enabled { if config.Cache.Enabled {
var (
cache Cache
blockNumFn GetLatestBlockNumFn
gasPriceFn GetLatestGasPriceFn
)
if config.Cache.BlockSyncRPCURL == "" { if config.Cache.BlockSyncRPCURL == "" {
return nil, nil, fmt.Errorf("block sync node required for caching") return nil, nil, fmt.Errorf("block sync node required for caching")
} }
...@@ -245,9 +238,7 @@ func Start(config *Config) (*Server, func(), error) { ...@@ -245,9 +238,7 @@ func Start(config *Config) (*Server, func(), error) {
} }
defer ethClient.Close() defer ethClient.Close()
blockNumLVC, blockNumFn = makeGetLatestBlockNumFn(ethClient, cache) rpcCache = newRPCCache(newCacheWithCompression(cache))
gasPriceLVC, gasPriceFn = makeGetLatestGasPriceFn(ethClient, cache)
rpcCache = newRPCCache(newCacheWithCompression(cache), blockNumFn, gasPriceFn, config.Cache.NumBlockConfirmations)
} }
srv, err := NewServer( srv, err := NewServer(
...@@ -345,12 +336,6 @@ func Start(config *Config) (*Server, func(), error) { ...@@ -345,12 +336,6 @@ func Start(config *Config) (*Server, func(), error) {
shutdownFunc := func() { shutdownFunc := func() {
log.Info("shutting down proxyd") log.Info("shutting down proxyd")
if blockNumLVC != nil {
blockNumLVC.Stop()
}
if gasPriceLVC != nil {
gasPriceLVC.Stop()
}
srv.Shutdown() srv.Shutdown()
if err := lim.FlushBackendWSConns(backendNames); err != nil { if err := lim.FlushBackendWSConns(backendNames); err != nil {
log.Error("error flushing backend ws conns", "err", err) log.Error("error flushing backend ws conns", "err", err)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment