Skip to content

Commit

Permalink
Merge pull request #582 from chatch/add-retry-to-server-requests
Browse files Browse the repository at this point in the history
Add retry to server requests
  • Loading branch information
jp-ryuji authored Jan 16, 2024
2 parents b848b94 + a0acd06 commit 4403d35
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 78 deletions.
155 changes: 80 additions & 75 deletions app/lib/stellar/server_request_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,24 @@ interface PageOptions {
limit?: number
}

const ledgers = (
const withRetry = async (run: () => Promise<any>): Promise<any> => {
// Dynamic import is used as p-retry is not compatible with cjs (see: remix.config.js)
const pRetry = await import('p-retry').then(({ default: pRetry }) => pRetry)

return pRetry(run, {
onFailedAttempt: (error) => {
console.log(
`Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left.`,
)
},
retries: 1,
factor: 1,
minTimeout: 1000,
maxTimeout: 2000,
})
}

const ledgers = async (
server: HorizonServer,
{ limit = 5, cursor, order = 'desc' }: PageOptions,
) => {
Expand All @@ -45,19 +62,19 @@ const ledgers = (
}
builder.limit(limit)
builder.order(order)
return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, ledgerRspRecToPropsRec),
)

const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, ledgerRspRecToPropsRec)
}

const ledger = (server: HorizonServer, ledgerSeq: string) => {
const ledger = async (server: HorizonServer, ledgerSeq: string) => {
const builder = server.ledgers().ledger(ledgerSeq)
return builder.call().then(ledgerRspRecToPropsRec)

const rspRec = await withRetry(async () => builder.call())
return ledgerRspRecToPropsRec(rspRec)
}

const transactions = (
const transactions = async (
server: HorizonServer,
{
ledgerSeq,
Expand All @@ -78,16 +95,15 @@ const transactions = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, transactionRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, transactionRspRecToPropsRec)
}

const transaction = (server: HorizonServer, txHash: string) => {
const transaction = async (server: HorizonServer, txHash: string) => {
const callBuilder = server.transactions().transaction(txHash)
return callBuilder.call().then(transactionRspRecToPropsRec)

const rspRec = await withRetry(async () => callBuilder.call())
return transactionRspRecToPropsRec(rspRec)
}

export interface LoadAccountResult {
Expand All @@ -113,44 +129,50 @@ const loadAccount = (
const loadAccountByKey = (server: HorizonServer, accountId: string) =>
loadAccountFromServer(server, accountId)

const loadAccountByFederatedAddress = (
const loadAccountByFederatedAddress = async (
server: HorizonServer,
address: string,
) => {
const [name, domain] = address.split('*')
return FederationServer.createForDomain(domain)
.then((fed) => fed.resolveAddress(name))
.then((acc) => loadAccountFromServer(server, acc.account_id))
.then((rsp) => ({
try {
const fed = await FederationServer.createForDomain(domain)
const acc = await fed.resolveAddress(name)
const rsp = await loadAccountFromServer(server, acc.account_id)
return {
account: rsp.account,
federatedAddress: address,
}))
.catch((e) => {
throw new NotFoundError(e.message, undefined)
})
}
} catch (e) {
throw new NotFoundError(e.message, undefined)
}
}

const loadAccountByMuxedAddress = (server: HorizonServer, address: string) => {
const loadAccountByMuxedAddress = async (
server: HorizonServer,
address: string,
) => {
const muxedAccount = MuxedAccount.fromAddress(address, '1')
const publicAddress = muxedAccount.accountId()
return loadAccountFromServer(server, publicAddress).then((rsp) => ({
const rsp = await loadAccountFromServer(server, publicAddress)
return {
account: rsp.account,
muxedAddress: address,
}))
}
}

const loadAccountFromServer = (
const loadAccountFromServer = async (
server: HorizonServer,
accountId: string,
): Promise<{ account: ServerApi.AccountRecord }> => {
const builder: AccountCallBuilder = server.accounts()
return builder
.accountId(accountId)
.call()
.then((account) => ({ account }))

const account = await withRetry(async () =>
builder.accountId(accountId).call(),
)
return { account }
}

const operations = (
const operations = async (
server: HorizonServer,
{
accountId,
Expand All @@ -171,14 +193,11 @@ const operations = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, operationRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, operationRspRecToPropsRec)
}

const effects = (
const effects = async (
server: HorizonServer,
{
accountId,
Expand Down Expand Up @@ -206,18 +225,14 @@ const effects = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then(
(serverRsp) =>
serverApiResponseToState(
serverRsp,
effectRspRecToPropsRec,
) as ReadonlyArray<ServerApi.EffectRecord>,
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(
serverRsp,
effectRspRecToPropsRec,
) as ReadonlyArray<ServerApi.EffectRecord>
}

const payments = (
const payments = async (
server: HorizonServer,
{
accountId,
Expand All @@ -236,14 +251,11 @@ const payments = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, paymentRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, paymentRspRecToPropsRec)
}

const offers = (
const offers = async (
server: HorizonServer,
{
accountId,
Expand All @@ -260,14 +272,11 @@ const offers = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, offersRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, offersRspRecToPropsRec)
}

const trades = (
const trades = async (
server: HorizonServer,
{
accountId,
Expand All @@ -285,14 +294,11 @@ const trades = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, tradeRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, tradeRspRecToPropsRec)
}

const liquidityPools = (
const liquidityPools = async (
server: HorizonServer,
{
id,
Expand All @@ -313,16 +319,15 @@ const liquidityPools = (
builder.limit(limit)
builder.order(order)

return builder
.call()
.then((serverRsp) =>
serverApiResponseToState(serverRsp, liquidityPoolRspRecToPropsRec),
)
const serverRsp = await withRetry(async () => builder.call())
return serverApiResponseToState(serverRsp, liquidityPoolRspRecToPropsRec)
}

const liquidityPool = (server: HorizonServer, poolId: string) => {
const liquidityPool = async (server: HorizonServer, poolId: string) => {
const builder = server.liquidityPools().liquidityPoolId(poolId)
return builder.call().then(liquidityPoolRspRecToPropsRec)

const rspRec = await withRetry(async () => builder.call())
return liquidityPoolRspRecToPropsRec(rspRec)
}

export {
Expand Down
41 changes: 41 additions & 0 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"express-rate-limit": "^7.0.0",
"isbot": "^3.6.13",
"lodash": "^4.17.21",
"p-retry": "^6.2.0",
"prismjs": "^1.29.0",
"react": "18.2.0",
"react-bootstrap": "2.8.0",
Expand Down Expand Up @@ -108,4 +109,4 @@
"lint-staged": {
"*.{js,jsx,ts,tsx}": "eslint --ignore-path .gitignore"
}
}
}
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},

// Remix takes care of building everything in `remix build`.
"noEmit": true
}
"noEmit": true,
"module": "commonjs", // NOTE: added to support dynamic import of p-retry (see: https://www.typescriptlang.org/ja/tsconfig#module)
},
}

0 comments on commit 4403d35

Please sign in to comment.