Skip to content

Commit

Permalink
Add retry logic to tBTC API calls
Browse files Browse the repository at this point in the history
We observed that the tBTC API calls are failing intermittently, which may
be related to unstable Cloudflare workers infrastructure. This PR adds retry
logic to the tBTC API calls to handle these failures.
Requests to the tBTC API will be retried up to 5 times with an exponential
backoff strategy.
  • Loading branch information
nkuba committed Nov 27, 2024
1 parent 254e90b commit 37b718a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 18 deletions.
39 changes: 38 additions & 1 deletion sdk/src/lib/api/HttpApi.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
import { backoffRetrier, RetryOptions } from "../utils"

/**
* Represents an abstract HTTP API.
*/
export default abstract class HttpApi {
#apiUrl: string

constructor(apiUrl: string) {
/**
* Retry options for API requests.
*/
#retryOptions: RetryOptions

constructor(
apiUrl: string,
retryOptions: RetryOptions = {
retries: 5,
backoffStepMs: 1000,
},
) {
this.#apiUrl = apiUrl
this.#retryOptions = retryOptions
}

/**
* Makes an HTTP request with retry logic.
* @param requestFn Function that returns a Promise of the HTTP response.
* @returns The HTTP response.
* @throws Error if the request fails after all retries.
*/
async requestWithRetry(
requestFn: () => Promise<Response>,
): Promise<Response> {
return backoffRetrier<Response>(
this.#retryOptions.retries,
this.#retryOptions.backoffStepMs,
)(async () => {
const response = await requestFn()

if (!response.ok) {
throw new Error(`Request failed: ${await response.text()}`)
}

return response
})
}

/**
Expand Down
32 changes: 15 additions & 17 deletions sdk/src/lib/api/TbtcApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ export default class TbtcApi extends HttpApi {
* otherwise.
*/
async saveReveal(revealData: SaveRevealRequest): Promise<boolean> {
const response = await this.postRequest("reveals", revealData)

if (!response.ok)
throw new Error(
`Reveal not saved properly in the database, response: ${response.status}`,
)
const response = await this.requestWithRetry(async () =>
this.postRequest("reveals", revealData),
).catch((error) => {
throw new Error(`Failed to save reveal: ${error}`)
})

const { success } = (await response.json()) as { success: boolean }

Expand All @@ -60,11 +59,11 @@ export default class TbtcApi extends HttpApi {
depositStatus: DepositStatus
fundingOutpoint: BitcoinTxOutpoint
}> {
const response = await this.postRequest("deposits", depositData)
if (!response.ok)
throw new Error(
`Bitcoin deposit creation failed, response: ${response.status}`,
)
const response = await this.requestWithRetry(async () =>
this.postRequest("deposits", depositData),
).catch((error) => {
throw new Error(`Failed to create deposit: ${error}`)
})

const responseData = (await response.json()) as CreateDepositResponse

Expand All @@ -83,12 +82,11 @@ export default class TbtcApi extends HttpApi {
* @returns All owner deposits, including queued deposits.
*/
async getDepositsByOwner(depositOwner: ChainIdentifier): Promise<Deposit[]> {
const response = await this.getRequest(
`deposits/${depositOwner.identifierHex}`,
)

if (!response.ok)
throw new Error(`Failed to fetch deposits: ${response.status}`)
const response = await this.requestWithRetry(async () =>
this.getRequest(`deposits/${depositOwner.identifierHex}`),
).catch((error) => {
throw new Error(`Failed to fetch deposits: ${error}`)
})

const responseData = (await response.json()) as Deposit[]

Expand Down

0 comments on commit 37b718a

Please sign in to comment.