Skip to content
This repository was archived by the owner on Mar 18, 2025. It is now read-only.

Commit d35be02

Browse files
will pankiewiczwill pankiewicz
authored andcommitted
bump version to v3.1.10
1 parent 9123d3b commit d35be02

File tree

1 file changed

+170
-117
lines changed

1 file changed

+170
-117
lines changed
Lines changed: 170 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ApiPromise, WsProvider } from "@polkadot/api";
22
import EventEmitter from "eventemitter3";
33

4-
import logger from "./logger";
5-
import { sleep } from "./util";
6-
import { POLKADOT_API_TIMEOUT } from "./constants";
4+
import logger from "../logger";
5+
import { sleep } from "../utils/util";
6+
import { API_PROVIDER_TIMEOUT, POLKADOT_API_TIMEOUT } from "../constants";
77

88
export const apiLabel = { label: "ApiHandler" };
99

@@ -12,166 +12,219 @@ export const apiLabel = { label: "ApiHandler" };
1212
* to a different provider if one proves troublesome.
1313
*/
1414
class ApiHandler extends EventEmitter {
15-
private _api: ApiPromise;
16-
private _endpoints: string[];
17-
private _reconnectLock: boolean;
18-
private _reconnectTries = 0;
19-
static isConnected: any;
20-
static _reconnect: any;
21-
22-
constructor(api: ApiPromise, endpoints?: string[]) {
15+
private _wsProvider?: WsProvider;
16+
private _api: ApiPromise | null = null;
17+
private readonly _endpoints: string[] = [];
18+
static isConnected = false;
19+
private healthCheckInProgress = false;
20+
private _currentEndpoint?: string;
21+
public upSince: number = Date.now();
22+
constructor(endpoints: string[]) {
2323
super();
24-
this._api = api;
25-
// this._endpoints = endpoints.sort(() => Math.random() - 0.5);
26-
this._registerEventHandlers(api);
24+
this._endpoints = endpoints.sort(() => Math.random() - 0.5);
25+
this.upSince = Date.now();
2726
}
2827

29-
static async createApi(endpoints, reconnectTries = 0) {
30-
const timeout = 12;
31-
let api, wsProvider;
32-
const healthCheck = async (api) => {
33-
logger.info(`Performing health check for WS Provider for rpc.`, apiLabel);
28+
async healthCheck(retries = 0): Promise<boolean> {
29+
if (retries < 10) {
30+
try {
31+
this.healthCheckInProgress = true;
32+
let chain;
3433

35-
await sleep(timeout * 1000);
36-
if (api && api?.isConnected) {
37-
logger.info(`All good. Connected`, apiLabel);
38-
return true;
39-
} else {
40-
logger.info(
41-
`rpc endpoint still disconnected after ${timeout} seconds. Disconnecting `,
34+
const isConnected = this._wsProvider?.isConnected;
35+
if (isConnected && !this._api?.isConnected) {
36+
try {
37+
chain = await this._api?.rpc.system.chain();
38+
} catch (e) {
39+
await sleep(API_PROVIDER_TIMEOUT);
40+
}
41+
}
42+
chain = await this._api?.rpc.system.chain();
43+
44+
if (isConnected && chain) {
45+
this.healthCheckInProgress = false;
46+
return true;
47+
} else {
48+
await sleep(API_PROVIDER_TIMEOUT);
49+
logger.info(`api still disconnected, disconnecting.`, apiLabel);
50+
await this._wsProvider?.disconnect();
51+
await this.getProvider(this._endpoints);
52+
await this.getAPI();
53+
return false;
54+
}
55+
} catch (e: unknown) {
56+
const errorMessage =
57+
e instanceof Error ? e.message : "An unknown error occurred";
58+
logger.error(
59+
`Error in health check for WS Provider for rpc. ${errorMessage}`,
4260
apiLabel,
4361
);
44-
await api.disconnect();
45-
46-
throw new Error(
47-
`rpc endpoint still disconnected after ${timeout} seconds.`,
48-
);
62+
this.healthCheckInProgress = false;
63+
return false;
4964
}
50-
};
65+
}
66+
return false;
67+
}
5168

52-
try {
53-
wsProvider = new WsProvider(
69+
public currentEndpoint() {
70+
return this._currentEndpoint;
71+
}
72+
73+
async getProvider(endpoints: string[]): Promise<WsProvider> {
74+
return await new Promise<WsProvider>((resolve, reject) => {
75+
const wsProvider = new WsProvider(
5476
endpoints,
55-
undefined,
77+
5000,
5678
undefined,
5779
POLKADOT_API_TIMEOUT,
5880
);
5981

60-
api = new ApiPromise({
61-
provider: new WsProvider(
62-
endpoints,
63-
undefined,
64-
undefined,
65-
POLKADOT_API_TIMEOUT,
66-
),
67-
// throwOnConnect: true,
82+
wsProvider.on("disconnected", async () => {
83+
try {
84+
const isHealthy = await this.healthCheck();
85+
logger.info(
86+
`[Disconnection] ${this._currentEndpoint}} Health check result: ${isHealthy}`,
87+
apiLabel,
88+
);
89+
resolve(wsProvider);
90+
} catch (error: any) {
91+
logger.warn(
92+
`WS provider for rpc ${endpoints[0]} disconnected!`,
93+
apiLabel,
94+
);
95+
reject(error);
96+
}
6897
});
98+
wsProvider.on("connected", () => {
99+
logger.info(`WS provider for rpc ${endpoints[0]} connected`, apiLabel);
100+
this._currentEndpoint = endpoints[0];
101+
resolve(wsProvider);
102+
});
103+
wsProvider.on("error", async () => {
104+
try {
105+
const isHealthy = await this.healthCheck();
106+
logger.info(
107+
`[Error] ${this._currentEndpoint} Health check result: ${isHealthy}`,
108+
apiLabel,
109+
);
110+
resolve(wsProvider);
111+
} catch (error: any) {
112+
logger.error(`Error thrown for rpc ${this._endpoints[0]}`, apiLabel);
113+
reject(error);
114+
}
115+
});
116+
});
117+
}
69118

70-
api
71-
.on("connected", () => {
72-
logger.info(`Connected to chain ${endpoints[0]}`, apiLabel);
73-
})
74-
.on("disconnected", async () => {
75-
logger.warn(`Disconnected from chain`, apiLabel);
76-
try {
77-
await healthCheck(wsProvider);
78-
await Promise.resolve(api);
79-
} catch (error: any) {
80-
await Promise.reject(error);
81-
}
82-
})
83-
.on("ready", () => {
84-
logger.info(`API connection ready ${endpoints[0]}`, apiLabel);
85-
})
86-
.on("error", async (error) => {
87-
logger.warn("The API has an error", apiLabel);
88-
logger.error(error, apiLabel);
89-
logger.warn(`attempting to reconnect to ${endpoints[0]}`, apiLabel);
90-
try {
91-
await healthCheck(wsProvider);
92-
await Promise.resolve(api);
93-
} catch (error: any) {
94-
await Promise.reject(error);
95-
}
96-
});
97-
98-
if (api) {
99-
await api.isReadyOrError.catch(logger.error);
119+
async getAPI(retries = 0): Promise<ApiPromise> {
120+
if (this._wsProvider && this._api && this._api?.isConnected) {
121+
return this._api;
122+
}
123+
const endpoints = this._endpoints.sort(() => Math.random() - 0.5);
100124

101-
return api;
102-
}
125+
try {
126+
logger.info(
127+
`[getAPI]: try ${retries} creating provider with endpoint ${endpoints[0]}`,
128+
apiLabel,
129+
);
130+
const provider = await this.getProvider(endpoints);
131+
this._wsProvider = provider;
132+
logger.info(
133+
`[getAPI]: provider created with endpoint: ${endpoints[0]}`,
134+
apiLabel,
135+
);
136+
const api = await ApiPromise.create({
137+
provider: provider,
138+
noInitWarn: true,
139+
});
140+
await api.isReadyOrError;
141+
logger.info(`[getApi] Api is ready`, apiLabel);
142+
return api;
103143
} catch (e) {
104-
logger.error(`there was an error: `, apiLabel);
105-
logger.error(e, apiLabel);
106-
if (reconnectTries < 10) {
107-
return await this.createApi(
108-
endpoints.sort(() => Math.random() - 0.5),
109-
reconnectTries + 1,
110-
);
144+
if (retries < 15) {
145+
return await this.getAPI(retries + 1);
111146
} else {
112-
return api;
147+
const provider = await this.getProvider(endpoints);
148+
return await ApiPromise.create({
149+
provider: provider,
150+
noInitWarn: true,
151+
});
113152
}
114153
}
115154
}
116155

117-
static async create(endpoints: string[]): Promise<ApiHandler> {
118-
try {
119-
const api = await this.createApi(
120-
endpoints.sort(() => Math.random() - 0.5),
121-
);
122-
123-
return new ApiHandler(api, endpoints);
124-
} catch (e) {
125-
logger.info(`there was an error: `, apiLabel);
126-
logger.error(e, apiLabel);
127-
}
156+
async setAPI() {
157+
const api = await this.getAPI(0);
158+
this._api = api;
159+
this._registerEventHandlers(this._api);
160+
return api;
128161
}
129162

130163
isConnected(): boolean {
131-
return this._api.isConnected;
164+
return this._wsProvider?.isConnected || false;
132165
}
133166

134-
getApi(): ApiPromise {
135-
return this._api;
167+
getApi(): ApiPromise | null {
168+
if (!this._api) {
169+
return null;
170+
} else {
171+
return this._api;
172+
}
136173
}
137174

138175
_registerEventHandlers(api: ApiPromise): void {
176+
if (!api) {
177+
logger.warn(`API is null, cannot register event handlers.`, apiLabel);
178+
return;
179+
}
180+
181+
logger.info(`Registering event handlers...`, apiLabel);
182+
139183
api.query.system.events((events) => {
140-
// Loop through the Vec<EventRecord>
141184
events.forEach((record) => {
142-
// Extract the phase, event and the event types
143185
const { event } = record;
144186

145-
if (event.section == "session" && event.method == "NewSession") {
146-
const [session_index] = event.data;
147-
148-
this.emit("newSession", {
149-
sessionIndex: session_index.toString(),
150-
});
187+
if (event.section === "session" && event.method === "NewSession") {
188+
const sessionIndex = Number(event?.data[0]?.toString()) || 0;
189+
this.handleNewSessionEvent(sessionIndex);
151190
}
152191

153192
if (
154-
event.section == "staking" &&
155-
(event.method == "Reward" || event.method == "Rewarded")
193+
event.section === "staking" &&
194+
(event.method === "Reward" || event.method === "Rewarded")
156195
) {
157196
const [stash, amount] = event.data;
158-
159-
this.emit("reward", {
160-
stash: stash.toString(),
161-
amount: amount.toString(),
162-
});
197+
this.handleRewardEvent(stash.toString(), amount.toString());
163198
}
164199

165200
if (event.section === "imOnline" && event.method === "SomeOffline") {
166-
const offlineVals = event.data.toJSON()[0].map((val) => val[0]);
167-
168-
this.emit("someOffline", {
169-
offlineVals: offlineVals,
170-
});
201+
const data = event.data.toJSON();
202+
const offlineVals =
203+
Array.isArray(data) && Array.isArray(data[0])
204+
? data[0].reduce((acc: string[], val) => {
205+
if (Array.isArray(val) && typeof val[0] === "string") {
206+
acc.push(val[0]);
207+
}
208+
return acc;
209+
}, [])
210+
: [];
211+
this.handleSomeOfflineEvent(offlineVals as string[]);
171212
}
172213
});
173214
});
174215
}
216+
217+
handleNewSessionEvent(sessionIndex: number): void {
218+
this.emit("newSession", { sessionIndex });
219+
}
220+
221+
handleRewardEvent(stash: string, amount: string): void {
222+
this.emit("reward", { stash, amount });
223+
}
224+
225+
handleSomeOfflineEvent(offlineVals: string[]): void {
226+
this.emit("someOffline", { offlineVals });
227+
}
175228
}
176229

177230
export default ApiHandler;

0 commit comments

Comments
 (0)