-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathbenchmark.ts
190 lines (163 loc) · 4.13 KB
/
benchmark.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
export interface PageInfo {
hasNextPage: boolean;
hasPreviousPage: boolean;
endCursor: string | null;
startCursor: string | null;
}
export type BenchmarkParams = {
paginateForwards: boolean;
limit: number;
numPages: number;
};
export type PaginationParams = {
first?: number;
after?: string;
last?: number;
before?: string;
};
export type ReportStatus =
| "COMPLETED"
| "TIMED OUT"
| "BOTH DATA AND ERRORS ARE UNDEFINED"
| string;
export class PaginationV2 {
paginateForwards: boolean;
limit: number;
cursor?: string;
constructor(paginateForwards: boolean, limit: number) {
this.paginateForwards = paginateForwards;
this.limit = limit;
}
getParams(): PaginationParams {
if (this.paginateForwards) {
return {
first: this.limit,
after: this.cursor,
};
}
return {
last: this.limit,
before: this.cursor,
};
}
getCursor(): string | undefined {
return this.cursor;
}
setCursor(pageInfo: PageInfo) {
if (this.paginateForwards) {
if (pageInfo.hasNextPage) {
this.cursor = pageInfo.endCursor!;
}
} else {
if (pageInfo.hasPreviousPage) {
this.cursor = pageInfo.startCursor!;
}
}
}
}
/// Caller is responsible for providing a `testFn` that returns the `PageInfo` for the benchmark to
/// paginate through. Tries to paginate `numPages` times, or repeats `numPages` times if there is no
/// next page.
export async function benchmark_connection_query(
benchmarkParams: BenchmarkParams,
testFn: (
cursor: PaginationParams,
) => Promise<{ pageInfo: PageInfo | ReportStatus; variables: any }>,
): Promise<Report> {
let { paginateForwards, limit, numPages } = benchmarkParams;
const cursors: Array<string> = [];
let hasNextPage = true;
let durations: number[] = [];
let pagination = new PaginationV2(paginateForwards, limit);
let queryParams;
let reportStatus: string | undefined = undefined;
for (let i = 0; i < numPages && hasNextPage; i++) {
let start = performance.now();
let { pageInfo: result, variables } = await testFn(pagination.getParams());
let duration = performance.now() - start;
durations.push(duration);
if (i == 0) {
queryParams = variables;
}
if (typeof result === "string") {
console.log(result);
// allow up to 3 retries
if (i == 2) {
reportStatus = result;
break;
}
continue;
}
let cursor = pagination.getCursor();
if (cursor) {
cursors.push(cursor);
}
// Defer to pagination to update cursor
pagination.setCursor(result);
}
// sleep for 1 second
await new Promise((r) => setTimeout(r, 1000));
if (reportStatus === undefined) {
reportStatus = "COMPLETED";
}
return report(
reportStatus as ReportStatus,
queryParams,
cursors,
metrics(durations),
);
}
type Metrics = {
min: number;
p50: number;
p90: number;
p95: number;
mean: number;
max: number;
durations: number[];
};
export function metrics(durations: number[]): Metrics {
// consider the initial 3 entries as warmup
const all_durations = durations;
durations = durations.slice(3);
const sorted = durations.sort((a, b) => a - b);
const p50 = sorted[Math.floor(durations.length * 0.5)];
const p90 = sorted[Math.floor(durations.length * 0.9)];
const p95 = sorted[Math.floor(durations.length * 0.95)];
const sum = sorted.reduce((a, b) => a + b, 0);
return {
min: sorted[0],
p50,
p90,
p95,
mean: sum / durations.length,
max: sorted[sorted.length - 1],
durations: all_durations,
};
}
type Report = {
variables: any;
cursors: string[];
status: ReportStatus;
metrics?: Metrics;
};
export function report<T>(
reportStatus: ReportStatus,
variables: T,
cursors: string[],
metrics: Metrics,
): Report {
// Set defaults and shared data
let reportObject: Report = {
status: "COMPLETED",
variables,
cursors,
};
if (reportStatus === "COMPLETED") {
reportObject.metrics = metrics;
}
reportObject.status = reportStatus;
return reportObject;
}