Skip to content

Commit

Permalink
fix: retry Filecoin.StateMinerInfo requests (#96)
Browse files Browse the repository at this point in the history
* deps: add `retry` from Deno stdlib
* fix: retry `Filecoin.StateMinerInfo` requests

---------

Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
  • Loading branch information
bajtos authored Sep 24, 2024
1 parent 0082af5 commit 995e0bd
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 3 deletions.
2 changes: 2 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
export { encodeHex } from 'https://deno.land/std@0.203.0/encoding/hex.ts'
export { decodeBase64 } from 'https://deno.land/std@0.203.0/encoding/base64.ts'
export { decode as decodeVarint } from 'https://deno.land/x/varint@v2.0.0/varint.ts'
export { retry } from 'https://deno.land/std@0.203.0/async/retry.ts';


// Deno Bundle does not support npm dependencies, we have to load them via CDN
export { CarBlockIterator } from 'https://cdn.skypack.dev/@ipld/car@5.3.2/?dts'
Expand Down
18 changes: 16 additions & 2 deletions lib/miner-info.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import { retry } from '../vendor/deno-deps.js'
import { RPC_URL, RPC_AUTH } from './constants.js'

/**
* @param {string} minerId A miner actor id, e.g. `f0142637`
* @param {object} options
* @param {number} [options.maxAttempts]
* @returns {Promise<string>} Miner's PeerId, e.g. `12D3KooWMsPmAA65yHAHgbxgh7CPkEctJHZMeM3rAvoW8CZKxtpG`
*/
export async function getMinerPeerId (minerId) {
export async function getMinerPeerId (minerId, { maxAttempts = 5 } = {}) {
try {
const res = await rpc('Filecoin.StateMinerInfo', minerId, null)
const res = await retry(() => rpc('Filecoin.StateMinerInfo', minerId, null), {
// The maximum amount of attempts until failure.
maxAttempts,
// The initial and minimum amount of milliseconds between attempts.
minTimeout: 5_000,
// How much to backoff after each retry.
multiplier: 1.5
})
return res.PeerId
} catch (err) {
if (err.name === 'RetryError' && err.cause) {
// eslint-disable-next-line no-ex-assign
err = err.cause
}
err.message = `Cannot obtain miner info for ${minerId}: ${err.message}`
throw err
}
Expand Down
2 changes: 1 addition & 1 deletion test/miner-info.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('get peer id of a known miner', async () => {

test('get peer id of a miner that does not exist', async () => {
try {
const result = await getMinerPeerId('f010')
const result = await getMinerPeerId('f010', { maxAttempts: 1 })
throw new AssertionError(
`Expected "getMinerPeerId()" to fail, but it resolved with "${result}" instead.`
)
Expand Down
52 changes: 52 additions & 0 deletions vendor/deno-deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,57 @@ function decode(buf, offset = 0) {
}
throw new RangeError("malformed or overflow varint");
}
class AssertionError extends Error {
name = "AssertionError";
constructor(message){
super(message);
}
}
function assert(expr, msg = "") {
if (!expr) {
throw new AssertionError(msg);
}
}
class RetryError extends Error {
constructor(cause, attempts){
super(`Retrying exceeded the maxAttempts (${attempts}).`);
this.name = "RetryError";
this.cause = cause;
}
}
const defaultRetryOptions = {
multiplier: 2,
maxTimeout: 60000,
maxAttempts: 5,
minTimeout: 1000,
jitter: 1
};
async function retry(fn, opts) {
const options = {
...defaultRetryOptions,
...opts
};
assert(options.maxTimeout >= 0, "maxTimeout is less than 0");
assert(options.minTimeout <= options.maxTimeout, "minTimeout is greater than maxTimeout");
assert(options.jitter <= 1, "jitter is greater than 1");
let attempt = 0;
while(true){
try {
return await fn();
} catch (error) {
if (attempt + 1 >= options.maxAttempts) {
throw new RetryError(error, options.maxAttempts);
}
const timeout = _exponentialBackoffWithJitter(options.maxTimeout, options.minTimeout, attempt, options.multiplier, options.jitter);
await new Promise((r)=>setTimeout(r, timeout));
}
attempt++;
}
}
function _exponentialBackoffWithJitter(cap, base, attempt, multiplier, jitter) {
const exp = Math.min(cap, base * multiplier ** attempt);
return (1 - jitter * Math.random()) * exp;
}
const typeofs = [
"string",
"number",
Expand Down Expand Up @@ -11692,6 +11743,7 @@ var $t = o4.FastestNodeClient, Vt = o4.HttpCachingChain, qt = o4.HttpChain, zt =
export { encodeHex as encodeHex };
export { decodeBase64 as decodeBase64 };
export { decode as decodeVarint };
export { retry as retry };
export { CarBlockIterator as CarBlockIterator };
export { UnsupportedHashError as UnsupportedHashError, HashMismatchError as HashMismatchError, validateBlock as validateBlock };
export { ne1 as fetchBeaconByTime, zt as HttpChainClient, Vt as HttpCachingChain };

0 comments on commit 995e0bd

Please sign in to comment.