From: Ardis Lu Date: Thu, 20 Feb 2025 07:49:23 +0000 (-0800) Subject: Add initial error strategies X-Git-Tag: v0.1.0~45 X-Git-Url: https://git.ardis.lu/?a=commitdiff_plain;h=0bddbb30906d25d68024dcf9a47a475084e1931c;p=csvio Add initial error strategies --- diff --git a/src/errorStrategies.js b/src/errorStrategies.js new file mode 100644 index 0000000..46dd805 --- /dev/null +++ b/src/errorStrategies.js @@ -0,0 +1,80 @@ +import { setTimeout } from 'node:timers/promises'; + +/** @import {TransformationErrorFunction} from './core.js'; */ + +/** + * Create a `TransformationErrorFunction` that handles errors by filling the output row with a given placeholder value. + * + * @param {string} value The placeholder value to fill the row with. This value will be used for each field in the row. + * @returns {TransformationErrorFunction} A `TransformationErrorFunction` that may be passed to a `CSVTransformer`'s + * `onError` option. + */ +function placeholder(value) { + return (row) => { + if (row.length === 0) { + return [value]; + } + else { + return Array(row.length).fill(value); + } + } +} + +/** + * Create a `TransformationErrorFunction` that handles errors by immediately retrying it `iterations` number of times. + * + * @param {number} iterations The number of times to retry the function. + * @param {string} [value] If provided, a placeholder value to set if the function still errors after the final iteration. + * If `value` is unset, the final error thrown by the function is re-thrown. + * @returns {TransformationErrorFunction} A `TransformationErrorFunction` that may be passed to a `CSVTransformer`'s + * `onError` option. + * @throws If `value` is unset and the function throws an error after the final iteration, the error is re-thrown. + */ +function retry(iterations, value) { + return async (row, e, fn) => { + while (iterations--) { + try { return await fn(row); } + catch { } + } + if (value !== undefined) { + return placeholder(value)(row, e, fn); + } + throw e; + } +} + +/** + * Create a `TransformationErrorFunction` that handles errors by retrying it an `iterations` number of times, with an + * exponentially increasing duration in between each retry (i.e., an exponential backoff). + * + * @param {number} iterations The number of times to retry the function. + * @param {number} [maxExponent] If provided, the maximum exponent used to calculate the retry duration. In other words, + * the `iterations` number at which the retry duration stops increasing. If `maxExponent` is unset, the retry duration + * is unbounded and will increase after each retry. + * @param {string} [value] If provided, a placeholder value to set if the function still errors after the final iteration. + * If `value` is unset, the final error thrown by the function is re-thrown. + * @returns {TransformationErrorFunction} A `TransformationErrorFunction` that may be passed to a `CSVTransformer`'s + * `onError` option. + * @throws If `value` is unset and the function throws an error after the final iteration, the error is re-thrown. + */ +function backoff(iterations, maxExponent, value) { + if (maxExponent === undefined) { + maxExponent = Infinity; + } + return async (row, e, fn) => { + for (let n = 0; n < iterations; n++) { + const exponent = n > maxExponent ? maxExponent : n; + const duration = (2 ** exponent) * 1000; // 1s, 2s, 4s, 8s, 16s, ... + const jitter = Math.random() * 1000; + await setTimeout(duration + jitter); + try { return await fn(row); } + catch { } + } + if (value !== undefined) { + return placeholder(value)(row, e, fn); + } + throw e; + } +} + +export const ErrorStrategies = { placeholder, retry, backoff }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 22441a5..0e82f90 100644 --- a/src/index.js +++ b/src/index.js @@ -1,2 +1,3 @@ export { CSVReader, CSVTransformer, CSVWriter } from './core.js'; export { CSVNormalizer, CSVDenormalizer } from './normalization.js'; +export { ErrorStrategies } from './errorStrategies.js';