Skip to content

Commit da34e35

Browse files
committed
Account for provider config weight when kicking off a request in FallbackProvider (#4298).
1 parent e2485b8 commit da34e35

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src.ts/_tests/test-providers-fallback.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
isError, makeError,
55

66
AbstractProvider, FallbackProvider, Network,
7+
ZeroAddress
78
} from "../index.js";
89

910
import type {
@@ -93,3 +94,68 @@ describe("Test Fallback broadcast", function() {
9394
});
9495
});
9596
});
97+
98+
describe("Test Inflight Quorum", function() {
99+
// Fires the %%actions%% as providers which will delay before returning,
100+
// and returns an array of arrays, where each sub-array indicates which
101+
// providers were inflight at once.
102+
async function test(actions: Array<{ delay: number, stallTimeout: number, priority: number, weight: number }>, quorum: number): Promise<Array<Array<number>>> {
103+
const inflights: Array<Array<number>> = [ [ ] ];
104+
105+
const configs = actions.map(({ delay, stallTimeout, priority, weight }, index) => ({
106+
provider: new MockProvider(async (r) => {
107+
if (r.method === "getBlockNumber") { return 1; }
108+
if (r.method === "getBalance") {
109+
// Add this as inflight
110+
let last = inflights.pop();
111+
if (last == null) { throw new Error("no elements"); }
112+
inflights.push(last);
113+
last = last.slice();
114+
last.push(index);
115+
inflights.push(last);
116+
117+
// Do the thing
118+
await stall(delay);
119+
120+
// Remove as inflight
121+
last = inflights.pop();
122+
if (last == null) { throw new Error("no elements"); }
123+
inflights.push(last);
124+
last = last.filter((v) => (v !== index));
125+
inflights.push(last);
126+
127+
return 0;
128+
}
129+
console.log(r);
130+
throw new Error(`unhandled method: ${ r.method }`);
131+
}),
132+
stallTimeout, priority, weight
133+
}));
134+
135+
const provider = new FallbackProvider(configs, network, {
136+
cacheTimeout: -1, pollingInterval: 100,
137+
quorum
138+
});
139+
await provider.getBalance(ZeroAddress);
140+
141+
return inflights;
142+
}
143+
144+
// See: #4298
145+
it("applies weights against inflight requests", async function() {
146+
this.timeout(2000);
147+
148+
const inflights = await test([
149+
{ delay: 50, stallTimeout: 1000, priority: 1, weight: 2 },
150+
{ delay: 50, stallTimeout: 1000, priority: 1, weight: 2 },
151+
], 2);
152+
153+
// Make sure there is never more than 1 inflight provider at once
154+
for (const running of inflights) {
155+
assert.ok(running.length <= 1, `too many inflight requests: ${ JSON.stringify(inflights) }`);
156+
}
157+
});
158+
159+
// @TODO: add lots more tests, checking on priority, weight and stall
160+
// configurations
161+
});

src.ts/providers/provider-fallback.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ export class FallbackProvider extends AbstractProvider {
684684
// Add any new runners, because a staller timed out or a result
685685
// or error response came in.
686686
for (let i = 0; i < newRunners; i++) {
687-
this.#addRunner(running, req)
687+
this.#addRunner(running, req);
688688
}
689689

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

760760
// Bootstrap enough runners to meet quorum
761761
const running: Set<RunnerState> = new Set();
762-
for (let i = 0; i < this.quorum; i++) {
763-
this.#addRunner(running, req);
762+
let inflightQuorum = 0;
763+
while (true) {
764+
const runner = this.#addRunner(running, req);
765+
if (runner == null) { break; }
766+
inflightQuorum += runner.config.weight;
767+
if (inflightQuorum >= this.quorum) { break; }
764768
}
765769

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

0 commit comments

Comments
 (0)