Skip to content

Commit bd209b1

Browse files
committed
support archive body and header caches
1 parent c0ba212 commit bd209b1

File tree

8 files changed

+163
-44
lines changed

8 files changed

+163
-44
lines changed

bench/network/archive.bench.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { printSummary, runBench, type Script } from "./base";
2+
3+
const PROVIDERS = [
4+
"ws://localhost:8181",
5+
"wss://polkadot-public-rpc.blockops.network/ws",
6+
"wss://polkadot.public.curie.radiumblock.co/ws",
7+
"wss://rockx-dot.w3node.com/polka-public-dot/ws",
8+
"wss://rpc-polkadot.luckyfriday.io",
9+
"wss://polkadot.api.onfinality.io/public-ws",
10+
"wss://rpc.ibp.network/polkadot",
11+
];
12+
13+
export const simpleArchive = (): Script => {
14+
let finalizedHeight: null | number = null;
15+
let hashByHeight: null | string = null;
16+
return {
17+
onOpen: (send) => {
18+
send("archive_v1_finalizedHeight");
19+
},
20+
21+
onResponse: (res, send) => {
22+
if (res != null) {
23+
if (finalizedHeight === null) {
24+
finalizedHeight = Number(res);
25+
send("archive_v1_hashByHeight", [finalizedHeight]);
26+
} else if (hashByHeight === null) {
27+
hashByHeight = res[0];
28+
send("archive_v1_header", [hashByHeight]);
29+
send("archive_v1_body", [hashByHeight]);
30+
}
31+
} else {
32+
console.error(`Error: null response`);
33+
}
34+
},
35+
36+
onError: (error) => {
37+
console.error(`Error: ${error.message}`);
38+
},
39+
};
40+
};
41+
42+
(async () => {
43+
const provider = PROVIDERS[0]!;
44+
const opts = {
45+
iterations: 100,
46+
warmup: 0,
47+
};
48+
const durationMs = 5_000;
49+
console.log(
50+
`Benchmarking ${provider} (iters=${opts.iterations}, duration=${durationMs / 1_000}s)`,
51+
);
52+
53+
const stats = runBench(provider, opts, simpleArchive);
54+
55+
setTimeout(() => {
56+
printSummary(stats);
57+
process.exit(0);
58+
}, durationMs);
59+
})();
Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { isSuccess } from "@/json-rpc/util";
22

3-
type Method = "follow" | "header" | "body";
4-
5-
type LatencyMap = Record<Method, { n: number; total: number; data: number[] }>;
3+
type LatencyMap = Record<string, { n: number; total: number; data: number[] }>;
64

75
type Stats = {
86
host: string;
@@ -15,7 +13,7 @@ type Stats = {
1513
latency: LatencyMap;
1614
};
1715

18-
type Pending = { method: Method; sent: number };
16+
type Pending = { method: string; sent: number };
1917

2018
export type Script = {
2119
onOpen?: (send: Send) => void;
@@ -24,18 +22,7 @@ export type Script = {
2422
onEvent?: (subId: string | null, ev: any, send: Send) => void;
2523
};
2624

27-
type Send = (
28-
method: Method,
29-
rpcMethod: string,
30-
params: any[],
31-
inFollow?: boolean,
32-
) => void;
33-
34-
const emptyLatency = (): LatencyMap => ({
35-
follow: { n: 0, total: 0, data: [] },
36-
header: { n: 0, total: 0, data: [] },
37-
body: { n: 0, total: 0, data: [] },
38-
});
25+
type Send = (rpcMethod: string, params?: any[]) => number;
3926

4027
const createStats = (host: string): Stats => ({
4128
host,
@@ -45,10 +32,10 @@ const createStats = (host: string): Stats => ({
4532
exfiltrated: 0,
4633
msgs: 0,
4734
events: 0,
48-
latency: emptyLatency(),
35+
latency: {},
4936
});
5037

51-
export function runChainHeadBench(
38+
export function runBench(
5239
endpoint: string,
5340
{
5441
iterations,
@@ -70,12 +57,13 @@ export function runChainHeadBench(
7057
let nextId = 1;
7158
let subId: string | null = null;
7259

73-
const send: Send = (method, rpcMethod, params) => {
60+
const send: Send = (rpcMethod, params) => {
7461
const id = nextId++;
75-
pendings.set(id, { method, sent: performance.now() });
62+
pendings.set(id, { method: rpcMethod, sent: performance.now() });
7663
ws.send(
7764
JSON.stringify({ jsonrpc: "2.0", id, method: rpcMethod, params }),
7865
);
66+
return id;
7967
};
8068

8169
ws.onopen = () => {
@@ -96,7 +84,12 @@ export function runChainHeadBench(
9684
const ok = isSuccess(msg);
9785
if (!isWarm) {
9886
const dt = now - p.sent;
99-
const lat = stats.latency[p.method];
87+
88+
if (stats.latency[p.method] === undefined) {
89+
stats.latency[p.method] = { n: 0, total: 0, data: [] };
90+
}
91+
const lat = stats.latency[p.method]!;
92+
10093
lat.n++;
10194
lat.total += dt;
10295
lat.data.push(dt);
@@ -109,7 +102,7 @@ export function runChainHeadBench(
109102
} else {
110103
script.onError?.(msg.error, send);
111104
}
112-
if (p.method === "follow") subId = msg.result;
105+
if (p.method === "chainHead_v1_follow") subId = msg.result;
113106
} else {
114107
stats.exfiltrated++;
115108
}
@@ -203,7 +196,9 @@ export const printSummary = (s: Stats) => {
203196
);
204197
console.log("Latency (ms):");
205198

206-
for (const method of Object.keys(s.latency) as Method[]) {
199+
for (const method of Object.keys(s.latency) as string[]) {
200+
if (s.latency[method] === undefined) continue;
201+
207202
const xs = s.latency[method].data;
208203
const r = summarize(xs);
209204

bench/network/follow.bench.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { printSummary, runChainHeadBench, type Script } from "./chain-head";
1+
import { printSummary, runBench, type Script } from "./base";
22

33
const PROVIDERS = [
44
"ws://localhost:8181",
55
"wss://polkadot-public-rpc.blockops.network/ws",
6-
"wss://polkadot-rpc.dwellir.com",
76
"wss://polkadot.public.curie.radiumblock.co/ws",
87
"wss://rockx-dot.w3node.com/polka-public-dot/ws",
98
"wss://rpc-polkadot.luckyfriday.io",
@@ -21,7 +20,7 @@ export const simpleFollow = (): Script => {
2120
const ops = new Set<string>();
2221
return {
2322
onOpen: (send) => {
24-
send("follow", "chainHead_v1_follow", [true]);
23+
send("chainHead_v1_follow", [true]);
2524
},
2625

2726
onResponse: (res) => {
@@ -39,8 +38,8 @@ export const simpleFollow = (): Script => {
3938

4039
onEvent: (subId, ev, send) => {
4140
if (ev.event === "newBlock") {
42-
send("body", "chainHead_v1_body", [subId, ev.blockHash]);
43-
send("header", "chainHead_v1_header", [subId, ev.blockHash]);
41+
send("chainHead_v1_body", [subId, ev.blockHash]);
42+
send("chainHead_v1_header", [subId, ev.blockHash]);
4443
}
4544

4645
if (ev.operationId != null) {
@@ -66,15 +65,15 @@ export const simpleFollow = (): Script => {
6665
(async () => {
6766
const provider = PROVIDERS[0]!;
6867
const opts = {
69-
iterations: 1_000,
68+
iterations: 100,
7069
warmup: 0,
7170
};
7271
const durationMs = 10_000;
7372
console.log(
7473
`Benchmarking ${provider} (iters=${opts.iterations}, duration=${durationMs / 1_000}s)`,
7574
);
7675

77-
const stats = runChainHeadBench(provider, opts, simpleFollow);
76+
const stats = runBench(provider, opts, simpleFollow);
7877

7978
setTimeout(() => {
8079
printSummary(stats);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { CacheConfig } from "@/config";
2+
import { forwardRequestWithCache } from "../forward";
3+
4+
export const archive_v1_body = (config: CacheConfig) => {
5+
return forwardRequestWithCache(
6+
"archive_v1_body",
7+
config,
8+
(req) => req.params[0],
9+
);
10+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { CacheConfig } from "@/config";
2+
import { forwardRequestWithCache } from "../forward";
3+
4+
export const archive_v1_header = (config: CacheConfig) => {
5+
return forwardRequestWithCache(
6+
"archive_v1_header",
7+
config,
8+
(req) => req.params[0],
9+
);
10+
};

src/json-rpc/polkadot/forward.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,45 @@
1+
import type { CacheConfig } from "@/config";
2+
import { createLRUCache } from "@/util/cache";
13
import type { JSONRPCMethodHandler } from "../methods";
4+
import type { JSONRPCRequest, JSONRPCResponse } from "../types";
5+
import { isSuccess } from "../util";
6+
import { isCacheEnabledFor } from "./chain-head/ops/util";
27

38
export const forwardRequest: JSONRPCMethodHandler = {
49
handleRequest: async (upstream, downstream, req) => {
510
const response = await upstream.request(req);
611
downstream.send(response);
712
},
813
};
14+
15+
export const forwardRequestWithCache = (
16+
method: string,
17+
config: CacheConfig,
18+
keyOf: (req: JSONRPCRequest) => string,
19+
): JSONRPCMethodHandler => {
20+
if (isCacheEnabledFor(config, method)) {
21+
const cache = createLRUCache<JSONRPCResponse>(
22+
config.methods?.[method]?.maxSize ?? 100,
23+
);
24+
return {
25+
handleRequest: async (upstream, downstream, req) => {
26+
const key = keyOf(req);
27+
const cached = cache.get(key);
28+
if (cached) {
29+
downstream.send({
30+
jsonrpc: "2.0",
31+
id: req.id ?? null,
32+
result: cached,
33+
});
34+
} else {
35+
const response = await upstream.request(req);
36+
if (isSuccess(response) && response.result != null) {
37+
cache.set(key, response.result);
38+
}
39+
downstream.send(response);
40+
}
41+
},
42+
};
43+
}
44+
return forwardRequest;
45+
};

src/json-rpc/polkadot/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { CacheConfig } from "@/config";
22
import type { JSONRPCMethodHandler } from "../methods";
3+
import { archive_v1_body } from "./archive/body";
4+
import { archive_v1_header } from "./archive/header";
35
import { archive_v1_storage, archive_v1_storageDiff } from "./archive/storage";
46
import { chainHead_v1_body } from "./chain-head/body";
57
import { chainHead_v1_call } from "./chain-head/call";
@@ -40,6 +42,8 @@ export function polkadotMethods(config: CacheConfig) {
4042

4143
archive_v1_storage: archive_v1_storage(),
4244
archive_v1_storageDiff: archive_v1_storageDiff(),
45+
archive_v1_body: archive_v1_body(config),
46+
archive_v1_header: archive_v1_header(config),
4347

4448
transactionWatch_v1_submitAndWatch: transactionWatch_v1_submitAndWatch(),
4549

wsmux.config.yaml

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,12 @@ cache:
2121
chainHead_v1_call:
2222
enabled: true
2323
max_entries: 25
24-
25-
# -------------------------------------------------------------------
26-
# IP rate limiting
27-
# -------------------------------------------------------------------
28-
rate_limit:
29-
enabled: false
30-
window: 1m
31-
max_requests: 100
32-
# Trusted networks for forwarding/proxy headers.
33-
# CIDR notation is used to specify the network ranges.
34-
trusted_networks:
35-
- 192.168.0.0/16
36-
- 172.16.0.0/12
37-
- 127.0.0.1/32
24+
archive_v1_header:
25+
enabled: true
26+
max_entries: 100
27+
archive_v1_body:
28+
enabled: true
29+
max_entries: 25
3830

3931
# -------------------------------------------------------------------
4032
# Upstream Servers Settings
@@ -83,3 +75,16 @@ json_rpc:
8375
max_pending_requests_per_connection: 1500
8476
max_pending_requests: 5000
8577
max_requests_per_second: 5000
78+
# -------------------------------------------------------------------
79+
# IP rate limiting
80+
# -------------------------------------------------------------------
81+
#rate_limit:
82+
# enabled: false
83+
# window: 1m
84+
# max_requests: 100
85+
# # Trusted networks for forwarding/proxy headers.
86+
# # CIDR notation is used to specify the network ranges.
87+
# trusted_networks:
88+
# - 192.168.0.0/16
89+
# - 172.16.0.0/12
90+
# - 127.0.0.1/32

0 commit comments

Comments
 (0)