mirror of
https://github.com/status-im/eth-rpc-proxy.git
synced 2026-08-31 11:41:08 +00:00
173 lines
5.1 KiB
Lua
173 lines
5.1 KiB
Lua
local json = require("cjson")
|
|
|
|
local _M = {}
|
|
|
|
-- Default TTLs (in seconds)
|
|
local ttl_defaults = {
|
|
default = { permanent = 86400, short = 5, minimal = 0 },
|
|
["ethereum:mainnet"] = { short = 15, minimal = 5 },
|
|
["arbitrum:mainnet"] = { short = 1},
|
|
["optimism:mainnet"] = { short = 1},
|
|
["polygon:mainnet"] = { short = 2}
|
|
}
|
|
|
|
-- Shared dicts per cache type
|
|
local cache_dicts = {
|
|
permanent = ngx.shared.rpc_cache,
|
|
short = ngx.shared.rpc_cache_short,
|
|
minimal = ngx.shared.rpc_cache_minimal
|
|
}
|
|
|
|
-- Permanent methods that can be cached for 24 hours
|
|
local permanent_methods = {
|
|
["eth_getBlockByHash"] = true,
|
|
["eth_getTransactionByHash"] = true,
|
|
["eth_getTransactionReceipt"] = true,
|
|
["eth_getTransactionByBlockHashAndIndex"] = true,
|
|
["eth_getUncleByBlockHashAndIndex"] = true,
|
|
["net_version"] = true,
|
|
["eth_chainId"] = true,
|
|
["web3_clientVersion"] = true,
|
|
["net_listening"] = true,
|
|
["eth_protocolVersion"] = true
|
|
}
|
|
|
|
-- Short-term methods that can be cached for 5 seconds
|
|
local short_methods = {
|
|
["eth_getBlockByNumber"] = true,
|
|
["eth_getTransactionByBlockNumberAndIndex"] = true,
|
|
["eth_getUncleByBlockNumberAndIndex"] = true,
|
|
["eth_getBalance"] = true,
|
|
["eth_getCode"] = true,
|
|
["eth_getStorageAt"] = true,
|
|
["eth_blockNumber"] = true
|
|
}
|
|
|
|
-- Minimal cache methods
|
|
local minimal_methods = {
|
|
["eth_gasPrice"] = true,
|
|
["eth_maxPriorityFeePerGas"] = true,
|
|
["eth_feeHistory"] = true
|
|
}
|
|
|
|
-- Helper function to decode JSON once and cache result
|
|
local function get_decoded_body(body_data)
|
|
if not body_data then return nil end
|
|
|
|
-- If already decoded (table), return as is
|
|
if type(body_data) == "table" then
|
|
return body_data
|
|
end
|
|
|
|
-- If string, decode it
|
|
local ok, body_json = pcall(json.decode, body_data)
|
|
if not ok or not body_json or not body_json.method then
|
|
return nil
|
|
end
|
|
|
|
return body_json
|
|
end
|
|
|
|
-- Unified cache function that handles all cache operations
|
|
function _M.check_cache(chain, network, body_data)
|
|
-- Decode body once
|
|
local decoded_body = get_decoded_body(body_data)
|
|
if not decoded_body then
|
|
return {
|
|
cache_type = nil,
|
|
cache_key = nil,
|
|
ttl = nil,
|
|
cached_response = nil
|
|
}
|
|
end
|
|
|
|
local method = decoded_body.method
|
|
local cache_type = nil
|
|
|
|
if permanent_methods[method] then
|
|
cache_type = "permanent"
|
|
elseif short_methods[method] then
|
|
cache_type = "short"
|
|
elseif minimal_methods[method] then
|
|
cache_type = "minimal"
|
|
end
|
|
|
|
if not cache_type then
|
|
return {
|
|
cache_type = nil,
|
|
cache_key = nil,
|
|
ttl = nil,
|
|
cached_response = nil
|
|
}
|
|
end
|
|
|
|
-- Generate cache key
|
|
local cache_key = chain .. ":" .. network .. ":" .. ngx.md5(body_data)
|
|
|
|
-- Fetch TTL config for this chain/network (fallback to default)
|
|
local key = chain .. ":" .. network
|
|
local cfg = ttl_defaults[key] or ttl_defaults.default
|
|
local ttl = cfg[cache_type] or ttl_defaults.default[cache_type]
|
|
|
|
-- If TTL is 0 or nil, treat as non-cacheable
|
|
if not ttl or ttl == 0 then
|
|
return {
|
|
cache_type = nil,
|
|
cache_key = nil,
|
|
ttl = nil,
|
|
cached_response = nil
|
|
}
|
|
end
|
|
|
|
local shared_dict = cache_dicts[cache_type]
|
|
|
|
-- Check for cached response
|
|
local cached_response = shared_dict:get(cache_key)
|
|
local stats_dict = ngx.shared.cache_stats
|
|
|
|
if cached_response then
|
|
ngx.log(ngx.INFO, "Cache hit (", cache_type, ") for key: ", cache_key)
|
|
-- Increment cache hit counter
|
|
stats_dict:incr("cache_hits_" .. cache_type, 1, 0)
|
|
stats_dict:incr("cache_hits_total", 1, 0)
|
|
else
|
|
ngx.log(ngx.INFO, "Cache miss (", cache_type, ") for key: ", cache_key)
|
|
-- Increment cache miss counter
|
|
stats_dict:incr("cache_misses_" .. cache_type, 1, 0)
|
|
stats_dict:incr("cache_misses_total", 1, 0)
|
|
end
|
|
|
|
-- Increment total requests counter
|
|
stats_dict:incr("total_requests_" .. cache_type, 1, 0)
|
|
stats_dict:incr("total_requests_all", 1, 0)
|
|
|
|
return {
|
|
cache_type = cache_type,
|
|
cache_key = cache_key,
|
|
ttl = ttl,
|
|
cached_response = cached_response
|
|
}
|
|
end
|
|
|
|
-- Save function that uses cache_info
|
|
function _M.save_to_cache(cache_info, response_body)
|
|
if not cache_info.cache_type or not cache_info.cache_key or not cache_info.ttl then
|
|
return false
|
|
end
|
|
|
|
local shared_dict = cache_dicts[cache_info.cache_type]
|
|
if not shared_dict then
|
|
ngx.log(ngx.ERR, "Invalid cache_type: ", cache_info.cache_type)
|
|
return false
|
|
end
|
|
|
|
local success, err = shared_dict:set(cache_info.cache_key, response_body, cache_info.ttl)
|
|
if success then
|
|
ngx.log(ngx.INFO, "Cached response (", cache_info.cache_type, ") for key: ", cache_info.cache_key, " with TTL: ", cache_info.ttl, " seconds")
|
|
else
|
|
ngx.log(ngx.ERR, "Failed to cache response (", cache_info.cache_type, "): ", err)
|
|
end
|
|
return success
|
|
end
|
|
|
|
return _M |