Skip to content

Commit

Permalink
Account for provider config weight when kicking off a request in Fall…
Browse files Browse the repository at this point in the history
…backProvider (ethers-io#4298).
  • Loading branch information
ricmoo authored and Woodpile37 committed Jan 14, 2024
1 parent 256e59a commit 57cfa5a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
66 changes: 66 additions & 0 deletions src.ts/_tests/test-providers-fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isError, makeError,

AbstractProvider, FallbackProvider, Network,
ZeroAddress
} from "../index.js";

import type {
Expand Down Expand Up @@ -93,3 +94,68 @@ describe("Test Fallback broadcast", function() {
});
});
});

describe("Test Inflight Quorum", function() {
// Fires the %%actions%% as providers which will delay before returning,
// and returns an array of arrays, where each sub-array indicates which
// providers were inflight at once.
async function test(actions: Array<{ delay: number, stallTimeout: number, priority: number, weight: number }>, quorum: number): Promise<Array<Array<number>>> {
const inflights: Array<Array<number>> = [ [ ] ];

const configs = actions.map(({ delay, stallTimeout, priority, weight }, index) => ({
provider: new MockProvider(async (r) => {
if (r.method === "getBlockNumber") { return 1; }
if (r.method === "getBalance") {
// Add this as inflight
let last = inflights.pop();
if (last == null) { throw new Error("no elements"); }
inflights.push(last);
last = last.slice();
last.push(index);
inflights.push(last);

// Do the thing
await stall(delay);

// Remove as inflight
last = inflights.pop();
if (last == null) { throw new Error("no elements"); }
inflights.push(last);
last = last.filter((v) => (v !== index));
inflights.push(last);

return 0;
}
console.log(r);
throw new Error(`unhandled method: ${ r.method }`);
}),
stallTimeout, priority, weight
}));

const provider = new FallbackProvider(configs, network, {
cacheTimeout: -1, pollingInterval: 100,
quorum
});
await provider.getBalance(ZeroAddress);

return inflights;
}

// See: #4298
it("applies weights against inflight requests", async function() {
this.timeout(2000);

const inflights = await test([
{ delay: 50, stallTimeout: 1000, priority: 1, weight: 2 },
{ delay: 50, stallTimeout: 1000, priority: 1, weight: 2 },
], 2);

// Make sure there is never more than 1 inflight provider at once
for (const running of inflights) {
assert.ok(running.length <= 1, `too many inflight requests: ${ JSON.stringify(inflights) }`);
}
});

// @TODO: add lots more tests, checking on priority, weight and stall
// configurations
});
10 changes: 7 additions & 3 deletions src.ts/providers/provider-fallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ export class FallbackProvider extends AbstractProvider {
// Add any new runners, because a staller timed out or a result
// or error response came in.
for (let i = 0; i < newRunners; i++) {
this.#addRunner(running, req)
this.#addRunner(running, req);
}

// All providers have returned, and we have no result
Expand Down Expand Up @@ -759,8 +759,12 @@ export class FallbackProvider extends AbstractProvider {

// Bootstrap enough runners to meet quorum
const running: Set<RunnerState> = new Set();
for (let i = 0; i < this.quorum; i++) {
this.#addRunner(running, req);
let inflightQuorum = 0;
while (true) {
const runner = this.#addRunner(running, req);
if (runner == null) { break; }
inflightQuorum += runner.config.weight;
if (inflightQuorum >= this.quorum) { break; }
}

const result = await this.#waitForQuorum(running, req);
Expand Down

0 comments on commit 57cfa5a

Please sign in to comment.