Skip to content

Commit

Permalink
Timeout and retry for backend requests
Browse files Browse the repository at this point in the history
  • Loading branch information
keichan34 committed Oct 1, 2024
1 parent 4253098 commit 0d8d23d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 5 deletions.
35 changes: 34 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const defaultEndpoint =
export const currentConfig: Config = {
japaneseAddressesApi: defaultEndpoint,
cacheSize: 1_000,
backendTimeout: 600,
backendTries: 3,
}

export type FetchOptions = {
Expand All @@ -24,6 +26,37 @@ export type FetchLike = (
options?: FetchOptions,
) => Promise<FetchResponseLike>

const timeoutableFetch = async (
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
input: RequestInfo,
init: RequestInit | undefined,
timeout: number,
) => {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
const response = await fetch(input, { ...init, signal: controller.signal })
clearTimeout(timeoutId)
return response
}

export async function fetchWithTimeoutRetry(
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>,
input: RequestInfo,
init?: RequestInit,
) {
let tries = 0
while (true) {
try {
return timeoutableFetch(fetch, input, init, currentConfig.backendTimeout)
} catch (error) {
tries++
if (tries >= currentConfig.backendTries) {
throw error
}
}
}
}

/**
* @internal
*/
Expand All @@ -41,7 +74,7 @@ export const __internals: { fetch: FetchLike } = {
if (typeof o.length !== 'undefined' && typeof o.offset !== 'undefined') {
headers['Range'] = `bytes=${o.offset}-${o.offset + o.length - 1}`
}
return window.fetch(url, {
return fetchWithTimeoutRetry(window.fetch, url, {
headers,
})
},
Expand Down
18 changes: 14 additions & 4 deletions src/main-node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import * as Normalize from './normalize'
import { __internals, FetchOptions, FetchResponseLike } from './config'
import {
__internals,
fetchWithTimeoutRetry,
FetchOptions,
FetchResponseLike,
} from './config'
import { promises as fs } from 'node:fs'
import { fetch } from 'undici'

Expand Down Expand Up @@ -43,9 +48,14 @@ export const requestHandlers = {
if (typeof o.length !== 'undefined' && typeof o.offset !== 'undefined') {
headers['Range'] = `bytes=${o.offset}-${o.offset + o.length - 1}`
}
return fetch(fileURL.toString(), {
headers,
})
return fetchWithTimeoutRetry(
// 私達が使う場所の undici fetch インタフェースはDOMのfetchと同等なので、型キャストしても問題ない
fetch as (typeof Window.prototype)['fetch'],
fileURL.toString(),
{
headers,
},
)
},
}

Expand Down
9 changes: 9 additions & 0 deletions src/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export interface Config {
/** 内部キャッシュの最大サイズ。デフォルトでは 1,000 件 */
cacheSize: number

/**
* バックエンドへのリクエストのタイムアウト。
* デフォルト 600ms
* タイムアウトした場合、数回リトライを行います。
* リトライ回数は {@link backendTries} で設定します。
*/
backendTimeout: number
backendTries: number

geoloniaApiKey?: string
}
export const config: Config = currentConfig
Expand Down

0 comments on commit 0d8d23d

Please sign in to comment.