mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
use plimit + delay for k8s watch requests
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
03794e6c38
commit
b1fac0d727
6
src/common/utils/delay.ts
Normal file
6
src/common/utils/delay.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Create async delay for provided timeout in milliseconds
|
||||||
|
|
||||||
|
export async function delay(timeoutMs = 1000) {
|
||||||
|
if (!timeoutMs) return;
|
||||||
|
await new Promise(resolve => setTimeout(resolve, timeoutMs));
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ export * from "./autobind";
|
|||||||
export * from "./base64";
|
export * from "./base64";
|
||||||
export * from "./camelCase";
|
export * from "./camelCase";
|
||||||
export * from "./cloneJson";
|
export * from "./cloneJson";
|
||||||
|
export * from "./delay";
|
||||||
export * from "./debouncePromise";
|
export * from "./debouncePromise";
|
||||||
export * from "./defineGlobal";
|
export * from "./defineGlobal";
|
||||||
export * from "./getRandId";
|
export * from "./getRandId";
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import type { KubeJsonApiData, KubeJsonApiError } from "../../renderer/api/kube-json-api";
|
import type { KubeJsonApiData, KubeJsonApiError } from "../../renderer/api/kube-json-api";
|
||||||
|
|
||||||
|
import plimit from "p-limit";
|
||||||
|
import { delay } from "../../common/utils";
|
||||||
import { LensApiRequest } from "../router";
|
import { LensApiRequest } from "../router";
|
||||||
import { LensApi } from "../lens-api";
|
import { LensApi } from "../lens-api";
|
||||||
import { KubeConfig, Watch } from "@kubernetes/client-node";
|
import { KubeConfig, Watch } from "@kubernetes/client-node";
|
||||||
import { ServerResponse } from "http";
|
import { ServerResponse } from "http";
|
||||||
import { Request } from "request";
|
import { Request } from "request";
|
||||||
import { chunk } from "lodash";
|
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
|
|
||||||
export interface IKubeWatchEvent<T = KubeJsonApiData | KubeJsonApiError> {
|
export interface IKubeWatchEvent<T = KubeJsonApiData | KubeJsonApiError> {
|
||||||
@ -106,8 +107,6 @@ class WatchRoute extends LensApi {
|
|||||||
|
|
||||||
public async routeWatch(request: LensApiRequest<IWatchRoutePayload>) {
|
public async routeWatch(request: LensApiRequest<IWatchRoutePayload>) {
|
||||||
const { response, cluster, payload: { apis } = {} } = request;
|
const { response, cluster, payload: { apis } = {} } = request;
|
||||||
const watchers = new Map<string, ApiWatcher>();
|
|
||||||
let isWatchRequestEnded = false;
|
|
||||||
|
|
||||||
if (!apis?.length) {
|
if (!apis?.length) {
|
||||||
this.respondJson(response, {
|
this.respondJson(response, {
|
||||||
@ -123,52 +122,38 @@ class WatchRoute extends LensApi {
|
|||||||
response.setHeader("Connection", "keep-alive");
|
response.setHeader("Connection", "keep-alive");
|
||||||
logger.debug(`watch using kubeconfig:${JSON.stringify(cluster.getProxyKubeconfig(), null, 2)}`);
|
logger.debug(`watch using kubeconfig:${JSON.stringify(cluster.getProxyKubeconfig(), null, 2)}`);
|
||||||
|
|
||||||
// create watcher instances
|
// limit concurrent k8s requests to avoid possible ECONNRESET-error
|
||||||
|
const requests = plimit(5);
|
||||||
|
const watchers = new Map<string, ApiWatcher>();
|
||||||
|
let isWatchRequestEnded = false;
|
||||||
|
|
||||||
apis.forEach(apiUrl => {
|
apis.forEach(apiUrl => {
|
||||||
const watcher = new ApiWatcher(apiUrl, cluster.getProxyKubeconfig(), response);
|
const watcher = new ApiWatcher(apiUrl, cluster.getProxyKubeconfig(), response);
|
||||||
|
|
||||||
watchers.set(apiUrl, watcher);
|
watchers.set(apiUrl, watcher);
|
||||||
|
|
||||||
|
requests(async () => {
|
||||||
|
if (isWatchRequestEnded) return;
|
||||||
|
await watcher.start();
|
||||||
|
await delay(100);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// limit concurrent k8s requests to avoid possible ECONNRESET-error
|
function onRequestEnd() {
|
||||||
async function startWatches() {
|
|
||||||
let apiGroupCall: () => Promise<any>;
|
|
||||||
const apiGroupCalls = chunk(apis, 10).map(apis => {
|
|
||||||
return () => {
|
|
||||||
const startedWatches = apis.map(apiUrl => watchers.get(apiUrl).start());
|
|
||||||
|
|
||||||
return Promise.allSettled(startedWatches);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
while ((apiGroupCall = apiGroupCalls.shift())) {
|
|
||||||
try {
|
|
||||||
if (isWatchRequestEnded) break;
|
|
||||||
await apiGroupCall();
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 150)); // delay between watch group calls
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function endWatches() {
|
|
||||||
if (isWatchRequestEnded) return;
|
if (isWatchRequestEnded) return;
|
||||||
isWatchRequestEnded = true;
|
isWatchRequestEnded = true;
|
||||||
|
requests.clearQueue();
|
||||||
watchers.forEach(watcher => watcher.stop());
|
watchers.forEach(watcher => watcher.stop());
|
||||||
watchers.clear();
|
watchers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
startWatches();
|
|
||||||
|
|
||||||
request.raw.req.on("end", () => {
|
request.raw.req.on("end", () => {
|
||||||
logger.info("Watch request end");
|
logger.info("Watch request end");
|
||||||
endWatches();
|
onRequestEnd();
|
||||||
});
|
});
|
||||||
|
|
||||||
request.raw.req.on("close", () => {
|
request.raw.req.on("close", () => {
|
||||||
logger.info("Watch request close");
|
logger.info("Watch request close");
|
||||||
endWatches();
|
onRequestEnd();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user