如何防止 Promise.race 失去的手臂激活?
How can I prevent the losing arm of a Promise.race from activating?
我正在向大量(好吧,n=5
)相同的端点(一些 kubernetes 集群)发送相同的查询并将结果整理在一起。我希望请求并行发出并在一段时间后超时;我希望在不妨碍进一步进展的情况下向用户报告失败。
作为所需输出的示例:
$ node find_broken_pods.js
tomato: error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
$ node find_broken_pods.js
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato what-bat Insufficient jQuery
第一次,无论出于何种原因,我们未能从番茄集群中获得答案,但我们能够从其他集群中枚举资源。第二次,没有超时,我们能够列出所有内容。
我以前想过这个:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
这 运行 并行执行所有操作,但单个故障会导致整个功能崩溃和燃烧。我想我可以把它改成这样:
export async function queryAll(): Promise<{cluster: string; deployments?: V1Deployment[]; error?: string}[]> {
const out: {cluster: string; result?: V1Pod[]; error?: string}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((resolve, _) =>
setTimeout(() => {
resolve(out.push({cluster: cluster, error: 'timed out'}));
}, 5000)
),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
但是,我现在正在见证 承诺 运行 完成,即 out
数组包含(一些)集群报告数据all 或 none 的集群报告超时:
- 如果没有达到超时,
Promise.all
不会等待 setTimeout
秒,但节点进程会(即,如果我将超时设置为 60 秒,那么节点进程只会60 秒后退出)
- 如果达到任何超时,
Promise.all
将等待超时发生...意味着 all setTimeout
s 最终触发。
我反而期望 Promise.race
失去的手臂会以某种方式被杀死或被阻止 运行ning。
有些事情告诉我,我的方法从根本上是错误的...我怎样才能提高我的容错能力?
看起来这已经足够满足我的需求了,但还是不行:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})),
error => {
process.stderr.write(`${cluster}: ${error}\n`);
resolve(0);
})
),
]).catch(error => {
process.stderr.write(`${cluster}: ${error}\n`);
return 0;
})
);
}
await Promise.all(promises);
return out;
}
由于重复的错误处理,这可能会导致此输出,我不太介意;例如,如果我用 /etc/hosts 强制失败,我得到:
$ node find_broken_pods.js
tomato: Error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato: Error: connect ECONNREFUSED 0.0.0.0:443
我不太高兴的是,节点特别等待了足够长的时间让任何库抛出 ECONNREFUSED,这发生在我的程序完成生成其输出几秒钟之后;我首先这样做的部分原因是存在故障模式,在这种模式下,进程只是挂起等待服务器响应被防火墙吃掉的请求。
是的,您已经将任务 运行 和 push
ing 对象都添加到了 out
数组。没有什么能阻止他们 - Promise.race
不会神奇地撤消为创建传递给它的承诺而采取的步骤。
无论是否已经报告超时或结果,您都可以通过为每个集群设置一个标志来解决这个问题,这样比赛的失败者就不会报告他们的状态。
但是,只使用承诺实现的结果值要简单得多,Promise.race
已经转发了:
interface QueryResult {cluster: string; deployments?: V1Deployment[]; error?: string}
export async function queryAll(): Promise<QueryResult[]> {
const promises: Promise<QueryResult>[] = Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
])
);
const out = await Promise.all(promises);
return out;
}
使用这种方法,您甚至可以按照与 CLUSTERS
相同的顺序获得结果,而不是按照它们返回的顺序。如果您不想那样,您仍然可以使用 push
的原始方法 - 只需根据每场比赛的结果来做!
export async function queryAll(): Promise<QueryResult[]> {
const out: QueryResult[] = [];
await Promise.all(Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
]).then(result => {
out.push(result);
})
));
return out;
}
顺便说一句,还可以考虑 Promise.allSettled
而不是 Promise.all
- 你也可以 reject
从你的超时承诺中得到。
我正在向大量(好吧,n=5
)相同的端点(一些 kubernetes 集群)发送相同的查询并将结果整理在一起。我希望请求并行发出并在一段时间后超时;我希望在不妨碍进一步进展的情况下向用户报告失败。
作为所需输出的示例:
$ node find_broken_pods.js
tomato: error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
$ node find_broken_pods.js
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato what-bat Insufficient jQuery
第一次,无论出于何种原因,我们未能从番茄集群中获得答案,但我们能够从其他集群中枚举资源。第二次,没有超时,我们能够列出所有内容。
我以前想过这个:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
这 运行 并行执行所有操作,但单个故障会导致整个功能崩溃和燃烧。我想我可以把它改成这样:
export async function queryAll(): Promise<{cluster: string; deployments?: V1Deployment[]; error?: string}[]> {
const out: {cluster: string; result?: V1Pod[]; error?: string}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((resolve, _) =>
setTimeout(() => {
resolve(out.push({cluster: cluster, error: 'timed out'}));
}, 5000)
),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})))
),
])
);
}
await Promise.all(promises);
return out;
}
但是,我现在正在见证 承诺 运行 完成,即 out
数组包含(一些)集群报告数据all 或 none 的集群报告超时:
- 如果没有达到超时,
Promise.all
不会等待setTimeout
秒,但节点进程会(即,如果我将超时设置为 60 秒,那么节点进程只会60 秒后退出) - 如果达到任何超时,
Promise.all
将等待超时发生...意味着 allsetTimeout
s 最终触发。
我反而期望 Promise.race
失去的手臂会以某种方式被杀死或被阻止 运行ning。
有些事情告诉我,我的方法从根本上是错误的...我怎样才能提高我的容错能力?
看起来这已经足够满足我的需求了,但还是不行:
export async function queryAll(): Promise<{cluster: string; deployments: V1Pod[]}[]> {
const out: {cluster: string; result: V1Pod[]}[] = [];
const promises: Promise<number>[] = [];
for (const cluster of Object.values(CLUSTERS)) {
promises.push(
Promise.race([
new Promise<number>((_, reject) => setTimeout(() => reject(new Error(`${cluster}: timed out`)), 5000)),
new Promise<number>((resolve, _) =>
getAllPods(cluster)
.then(pods => resolve(out.push({cluster: cluster, result: pods})),
error => {
process.stderr.write(`${cluster}: ${error}\n`);
resolve(0);
})
),
]).catch(error => {
process.stderr.write(`${cluster}: ${error}\n`);
return 0;
})
);
}
await Promise.all(promises);
return out;
}
由于重复的错误处理,这可能会导致此输出,我不太介意;例如,如果我用 /etc/hosts 强制失败,我得到:
$ node find_broken_pods.js
tomato: Error: timed out
Cluster Pod Reason
potato bar-foos Invalid PVC
potato foo-eggnog Invalid image
yoghurt spam-baz Invalid name
tomato: Error: connect ECONNREFUSED 0.0.0.0:443
我不太高兴的是,节点特别等待了足够长的时间让任何库抛出 ECONNREFUSED,这发生在我的程序完成生成其输出几秒钟之后;我首先这样做的部分原因是存在故障模式,在这种模式下,进程只是挂起等待服务器响应被防火墙吃掉的请求。
是的,您已经将任务 运行 和 push
ing 对象都添加到了 out
数组。没有什么能阻止他们 - Promise.race
不会神奇地撤消为创建传递给它的承诺而采取的步骤。
无论是否已经报告超时或结果,您都可以通过为每个集群设置一个标志来解决这个问题,这样比赛的失败者就不会报告他们的状态。
但是,只使用承诺实现的结果值要简单得多,Promise.race
已经转发了:
interface QueryResult {cluster: string; deployments?: V1Deployment[]; error?: string}
export async function queryAll(): Promise<QueryResult[]> {
const promises: Promise<QueryResult>[] = Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
])
);
const out = await Promise.all(promises);
return out;
}
使用这种方法,您甚至可以按照与 CLUSTERS
相同的顺序获得结果,而不是按照它们返回的顺序。如果您不想那样,您仍然可以使用 push
的原始方法 - 只需根据每场比赛的结果来做!
export async function queryAll(): Promise<QueryResult[]> {
const out: QueryResult[] = [];
await Promise.all(Object.values(CLUSTERS).map(cluster =>
Promise.race([
new Promise(resolve => {
setTimeout(() => {
resolve({cluster: cluster, error: 'timed out'});
}, 5000)
}),
getAllPods(cluster).then(pods => ({cluster: cluster, result: pods}))
]).then(result => {
out.push(result);
})
));
return out;
}
顺便说一句,还可以考虑 Promise.allSettled
而不是 Promise.all
- 你也可以 reject
从你的超时承诺中得到。