72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
|
|
import { AcInfinityApiClient, AcInfinityApiError } from "../http/AcInfinityApiClient";
|
||
|
|
import { TyphonMetrics } from "../metrics/TyphonMetrics";
|
||
|
|
import { Logger } from "../observability/Logger";
|
||
|
|
|
||
|
|
export class ClimatePollingService {
|
||
|
|
private timer: NodeJS.Timeout | null = null;
|
||
|
|
private inFlight = false;
|
||
|
|
|
||
|
|
public constructor(
|
||
|
|
private readonly client: AcInfinityApiClient,
|
||
|
|
private readonly metrics: TyphonMetrics,
|
||
|
|
private readonly logger: Logger,
|
||
|
|
private readonly pollIntervalSeconds: number
|
||
|
|
) {}
|
||
|
|
|
||
|
|
public start(): void {
|
||
|
|
void this.pollOnce();
|
||
|
|
this.timer = setInterval(() => {
|
||
|
|
void this.pollOnce();
|
||
|
|
}, this.pollIntervalSeconds * 1000);
|
||
|
|
}
|
||
|
|
|
||
|
|
public stop(): void {
|
||
|
|
if (this.timer) {
|
||
|
|
clearInterval(this.timer);
|
||
|
|
this.timer = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private async pollOnce(): Promise<void> {
|
||
|
|
if (this.inFlight) {
|
||
|
|
this.logger.warn("skipping poll because previous poll is still running");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.inFlight = true;
|
||
|
|
try {
|
||
|
|
const snapshot = await this.client.fetchSnapshot();
|
||
|
|
this.metrics.updateFromSnapshot(snapshot);
|
||
|
|
this.logger.info("poll succeeded", {
|
||
|
|
controller_count: snapshot.controllers.length
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
const { reason, code } = this.classifyError(error);
|
||
|
|
this.metrics.markPollFailure(reason, code);
|
||
|
|
this.logger.error("poll failed", {
|
||
|
|
reason,
|
||
|
|
code,
|
||
|
|
error_message: error instanceof Error ? error.message : String(error)
|
||
|
|
});
|
||
|
|
} finally {
|
||
|
|
this.metrics.refreshDataAgeGauge();
|
||
|
|
this.inFlight = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private classifyError(error: unknown): { reason: string; code: string } {
|
||
|
|
if (error instanceof AcInfinityApiError) {
|
||
|
|
if (error.apiCode === "10001" || error.apiCode === "invalid_auth") {
|
||
|
|
return { reason: "auth", code: error.apiCode };
|
||
|
|
}
|
||
|
|
return { reason: "api", code: error.apiCode };
|
||
|
|
}
|
||
|
|
|
||
|
|
if (error instanceof Error && error.name === "AbortError") {
|
||
|
|
return { reason: "timeout", code: "timeout" };
|
||
|
|
}
|
||
|
|
|
||
|
|
return { reason: "unknown", code: "unknown" };
|
||
|
|
}
|
||
|
|
}
|