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

api: add Vercel KV cache for storing market data #1654

Merged
merged 8 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
name: Setup
description: Sets up a Node.js environment, installs the dependencies using Yarn,
description: Sets up a Node.js environment, installs the dependencies using Yarn,
and caches the installed dependencies for faster future builds.
runs:
using: composite
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org
cache: yarn

- uses: actions/cache@v3
- uses: actions/cache@v4
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}

- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts --ignore-engines
shell: bash
shell: bash
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: yarn lint

unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/setup
- run: yarn test:ci
72 changes: 56 additions & 16 deletions api/market_data.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { kv } from "@vercel/kv";

// Dia to Coingecko names
const tickers = {
"Tether USD": "tether",
Expand Down Expand Up @@ -42,13 +44,24 @@ const dia_xlsd = {

// retrieve Dia XLSD fair prices
const fetchDiaXLSD = async () => {
const cache_key = "diaxlsd"
const cached = await kv.get(cache_key)
if (cached) {
return JSON.parse(cached)
}

const url = 'https://api.diadata.org/xlsd'
const resp = await fetch(url, { headers: { "accept": "application/json" } })
if (!resp.ok) {
throw new Error(resp.status)
}
const json = await resp.json()
return new Map(json.map(x => [x.Token, x]))
const result = new Map(json.map(x => [x.Token, x]))

// cache the data for 120 seconds
await kv.set(cache_key, JSON.stringify(result), { ex: 120 })
.catch(err => console.error('Unable to cache Dia data', err))
return result;
}

const fetchDiaAsset = async (asset) => {
Expand Down Expand Up @@ -81,14 +94,19 @@ const fetchDiaAsset = async (asset) => {
}
}
} catch (error) {
console.log(error)
console.warn('Dia API error for asset: ', asset, error)
throw error;
}
}

const dia = async (args) => {
const assets = args.ids.split(',')
const cache_key = "dia_" + args.ids
const cached = await kv.get(cache_key)
if (cached) {
return JSON.parse(cached)
}

const assets = args.ids.split(',')
return Promise
.all(assets.map(x => fetchDiaAsset(x)))
.then(x => x.reduce((map, obj) => {
Expand All @@ -97,12 +115,34 @@ const dia = async (args) => {
map[k] = obj[k]
return map
}, {}))
.then(async x => {
// cache the data for 120 seconds
kv.set(cache_key, JSON.stringify(x), { ex: 120 })
.catch(err => console.error('Unable to cache Dia data', err))
return x
})
}

const coingecko = async (args) => {
const cache_key = "coingecko_" + args.ids
const cached = await kv.get(cache_key)
if (cached) {
console.log('Cached', cache_key, cached)
return JSON.parse(cached)
}

const url = 'https://api.coingecko.com/api/v3/simple/price?' + new URLSearchParams(args)
const response = await fetch(url, { headers: { "accept": "application/json" } })
return await response.json()
const data = await response.json()
if (!response.ok) {
throw new Error(data)
}

// cache the data for 120 seconds
console.log('Caching', cache_key, JSON.stringify(data))
kv.set(cache_key, JSON.stringify(data), { ex: 120 })
.catch(err => console.error('Unable to cache coingecko data', err))
return data;
}

const fetchPrices = (priceSource, args) => {
Expand All @@ -111,23 +151,23 @@ const fetchPrices = (priceSource, args) => {
} else if (priceSource === 'dia') {
return dia(args)
} else {
try {
return dia(args)
} catch (error) {
console.log(error)
return coingecko(args)
}
return dia(args).catch(() => coingecko(args))
}
}

export default async function (request, response) {
const args = request.query
const priceSource = args['price-source']

const resp = await fetchPrices(priceSource, args)
return response
.status(200)
.setHeader("content-type", "application/json")
.setHeader("cache-control", "public, maxage=0, s-maxage=300")
.json(resp)
try {
const resp = await fetchPrices(priceSource, args)
return response
.status(200)
.setHeader("content-type", "application/json")
.setHeader("cache-control", "public, maxage=0, s-maxage=120")
.json(resp)
} catch (err) {
console.error('Unable to fetch prices', err)
return response.status(500);
}
}
46 changes: 46 additions & 0 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@interlay/interbtc-api": "2.6.0",
"@interlay/monetary-js": "0.7.3",
"@polkadot/util-crypto": "12.6.1",
"@vercel/kv": "^1.0.1",
"big.js": "6.1.1",
"pg": "^8.10.0"
}
Expand Down
2 changes: 1 addition & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"api/market_data.js": {
"memory": 128,
"maxDuration": 5
"maxDuration": 10
},
"api/tvl_dex.js": {
"memory": 256,
Expand Down
Loading