Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 🐛 Reduce the cache ttl to 10-20s for gauge list #11196

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
9 changes: 2 additions & 7 deletions apps/web/src/pages/api/gauges/getAllGauges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import qs from 'qs'
import { getViemClients } from 'utils/viem.server'
import { stringify } from 'viem'

const MAX_CACHE_SECONDS = 60 * 5
const MAX_CACHE_SECONDS = 10

const handler: NextApiHandler = async (req, res) => {
const queryString = qs.stringify(req.query)
Expand All @@ -31,12 +31,7 @@ const handler: NextApiHandler = async (req, res) => {
},
)

if (blockNumber) {
// cache for long time if blockNumber is provided
res.setHeader('Cache-Control', `max-age=10800, s-maxage=31536000`)
} else {
res.setHeader('Cache-Control', `max-age=10, s-maxage=${MAX_CACHE_SECONDS}, stale-while-revalidate`)
}
res.setHeader('Cache-Control', `max-age=${MAX_CACHE_SECONDS}, s-maxage=${MAX_CACHE_SECONDS}`)
return res.status(200).json({
data: JSON.parse(stringify(gauges)),
lastUpdated: Number(Date.now()),
Expand Down
4 changes: 3 additions & 1 deletion packages/gauges/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"./package.json": "./package.json"
},
"devDependencies": {
"@pancakeswap/sdk": "workspace:^",
"@pancakeswap/position-managers": "workspace:^",
"@pancakeswap/sdk": "workspace:^",
"@types/lodash": "^4.14.202",
"lodash": "^4.17.21",
"tsup": "^6.7.0"
Expand All @@ -33,6 +33,8 @@
"@pancakeswap/chains": "workspace:^",
"@pancakeswap/tokens": "workspace:^",
"@pancakeswap/v3-sdk": "workspace:^",
"@types/lru-cache": "^7.10.10",
"lru-cache": "^11.0.2",
"viem": "catalog:"
}
}
69 changes: 69 additions & 0 deletions packages/gauges/src/cacheByLRU.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { LRUCache } from 'lru-cache'
import { keccak256 } from 'viem'

type AsyncFunction<T extends any[]> = (...args: T) => Promise<any>

// Type definitions for the cache.
type CacheOptions<T extends AsyncFunction<any>> = {
name?: string
ttl: number
key: (params: Parameters<T>) => any
}

function calcCacheKey(args: any[], epoch: number) {
const json = JSON.stringify(args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args is any type, use JSON.stringify would be broken if args with some complex data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use stringify from viem, it can resolve bigint, which JSON.stringify cannot , and wrap with try...catch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx guys. will try stringify

const r = keccak256(`0x${json}@${epoch}`)
return r
}

export const cacheByLRU = <T extends AsyncFunction<any>>(fn: T, { ttl, key }: CacheOptions<T>) => {
const cache = new LRUCache<string, Promise<any>>({
max: 1000,
ttl,
})

// function logger(...args: any[]) {
// const nameStr = `${name || 'def'}`
// console.log(`[${nameStr}]`, ...args)
// }

let startTime = 0
return async (...args: Parameters<T>): Promise<ReturnType<T>> => {
// Start Time
if (!startTime) {
startTime = Date.now()
}
const epoch = (Date.now() - startTime) / ttl
const halfTTS = epoch % 1 > 0.5
const epochId = Math.floor(epoch)

// Setup next epoch cache if halfTTS passed
if (halfTTS) {
const nextKey = calcCacheKey(key(args), epochId + 1)
if (!cache.has(nextKey)) {
const nextPromise = fn(...args)
cache.set(nextKey, nextPromise)
}
}

const cacheKey = calcCacheKey(key(args), epochId)
// logger(cacheKey, `exists=${cache.has(cacheKey)}`)
if (cache.has(cacheKey)) {
// logger('cache hit', cacheKey)
return cache.get(cacheKey)
}

const promise = fn(...args)

cache.set(cacheKey, promise)
// logger(`cache set`, cacheKey)

try {
return await promise
} catch (error) {
// logger('error', cacheKey, error)
cache.delete(cacheKey)
throw error
}
}
}
56 changes: 23 additions & 33 deletions packages/gauges/src/constants/config/getGauges.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,31 @@
import { cacheByLRU } from '../../cacheByLRU'
import { GaugeConfig } from '../../types'
import { GAUGES_API } from './endpoint'

function createGaugeConfigFetcher() {
let gauges: GaugeConfig[] | undefined
let fetchRequest: Promise<GaugeConfig[]> | undefined

return async function getGaugeConfig() {
if (fetchRequest) return fetchRequest
const fetchGaugeConfig = async () => {
if (gauges) {
return gauges
}
try {
const response = await fetch(GAUGES_API, {
signal: AbortSignal.timeout(3000),
})
if (response.ok) {
gauges = await response.json()
if (!gauges) {
throw new Error(`Unexpected empty gauges fetched from remote ${gauges}`)
}
return gauges
}
throw new Error(`Fetch failed with status: ${response.status}`)
} catch (e) {
if (e instanceof Error) {
throw new Error(`Fetch failed: ${e.message}`)
} else {
throw new Error(`Fetch failed: ${e}`)
}
} finally {
fetchRequest = undefined
const fetchGaugeConfig = async () => {
try {
const response = await fetch(GAUGES_API, {
signal: AbortSignal.timeout(3000),
})
if (response.ok) {
const gauges: GaugeConfig[] = await response.json()
if (!gauges) {
throw new Error(`Unexpected empty gauges fetched from remote`)
}
return gauges
}
throw new Error(`Fetch failed with status: ${response.status}`)
} catch (e) {
if (e instanceof Error) {
throw new Error(`Fetch failed: ${e.message}`)
} else {
throw new Error(`Fetch failed: ${e}`)
}
fetchRequest = fetchGaugeConfig()
return fetchRequest
}
}

export const getGauges = createGaugeConfigFetcher()
export const getGauges = cacheByLRU(fetchGaugeConfig, {
name: 'getGaugesConfig',
ttl: 10000,
key: () => [],
})
12 changes: 11 additions & 1 deletion packages/gauges/src/fetchAllGauges.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { AbiStateMutability, ContractFunctionReturnType, PublicClient } from 'viem'
import { gaugesVotingABI } from './abis/gaugesVoting'
import { cacheByLRU } from './cacheByLRU'
import { getContract } from './contract'
import { fetchGaugesCount } from './fetchGaugesCount'
import { getGaugeHash } from './getGaugeHash'
import { GaugeInfo } from './types'

export const fetchAllGauges = async (
const _fetchAllGauges = async (
client: PublicClient,
options?: {
blockNumber?: bigint
Expand Down Expand Up @@ -44,3 +45,12 @@ export const fetchAllGauges = async (
} as GaugeInfo
})
}

export const fetchAllGauges = cacheByLRU(_fetchAllGauges, {
name: 'fetchAllGauges',
ttl: 10000,
key: (params) => {
const [, options] = params
return [options?.blockNumber]
},
})
13 changes: 11 additions & 2 deletions packages/gauges/src/fetchGaugeVoting.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { PublicClient } from 'viem'
import { cacheByLRU } from './cacheByLRU'
import { getCalcContract } from './contract'
import { Gauge, GaugeInfoConfig } from './types'

export const fetchAllGaugesVoting = async (
const _fetchAllGaugesVoting = async (
client: PublicClient,
gaugeInfos: GaugeInfoConfig[],
inCap: boolean = true,
Expand All @@ -11,11 +12,19 @@ export const fetchAllGaugesVoting = async (
},
): Promise<Gauge[]> => {
const contract = getCalcContract(client)

const weights = await contract.read.massGetGaugeWeight([inCap], options)

return gaugeInfos.map((gauge) => ({
...gauge,
weight: weights[gauge.gid] ?? 0n,
}))
}

export const fetchAllGaugesVoting = cacheByLRU(_fetchAllGaugesVoting, {
name: 'fetchAllGaugesVoting',
ttl: 5000,
key: (params) => {
const [, gaugeInfos, inCap, options] = params
return [gaugeInfos, inCap, options?.blockNumber]
},
})
12 changes: 11 additions & 1 deletion packages/gauges/src/getAllGauges.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import keyBy from 'lodash/keyBy'
import { PublicClient } from 'viem'
import { cacheByLRU } from './cacheByLRU'
import { getGauges } from './constants/config/getGauges'
import { CONFIG_TESTNET } from './constants/config/testnet'
import { fetchAllGauges } from './fetchAllGauges'
Expand Down Expand Up @@ -60,11 +61,20 @@ export const getAllGauges = async (
}, [] as Gauge[])
}

async function fetchGaugesSC(client: PublicClient, killed?: boolean, blockNumber?: bigint) {
async function _fetchGaugesSC(client: PublicClient, killed?: boolean, blockNumber?: bigint) {
let gaugesSC = await fetchAllGauges(client, {
blockNumber,
})
gaugesSC = await fetchAllKilledGauges(client, gaugesSC, { blockNumber })
if (!killed) gaugesSC = gaugesSC.filter((gauge) => !gauge.killed)
return gaugesSC
}

const fetchGaugesSC = cacheByLRU(_fetchGaugesSC, {
name: 'gaugesSC',
ttl: 10000,
key: (params) => {
const [, killed, blockNumber] = params
return [killed, blockNumber]
},
})
Loading
Loading