Files
Edu/services/parent-bff/src/aggregation/orchestrator.ts
SpecialX 2229309a1e feat: initialize parent-bff service with full core features
add complete parent-bff implementation including:
- GraphQL endpoint with depth/cost validation
- ChildGuard越权校验 with redis cache and singleflight
- parallel orchestration with partial failure fallback
- three-level cache fallback strategy (Redis + LRU + downstream)
- Kafka consumer for cache invalidation and notification push
- opossum circuit breaker for downstream services
- Prometheus metrics and SLO alerts
- Helm chart for k8s deployment with multi-environment configs
- Grafana dashboard for observability
- complete unit and integration tests
2026-07-10 18:49:06 +08:00

78 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { logger } from "../shared/observability/logger.js";
/**
* Orchestrator并行下游编排 + 降级标记(对齐 02-architecture-design.md §1.2 + §9 #6
*
* 核心能力:
* - Promise.allSettled 并行执行多个下游调用
* - 任一失败时记录 warn 日志 + 标记 partial=true
* - 失败的字段返回 null其他字段正常返回
*
* P4 用 Promise.allSettled 降级P6 引入 opossum 熔断器后,
* Orchestrator 将先检查熔断器状态再决定是否调用。
*/
export interface OrchestrationFailure {
key: string;
error: unknown;
}
export interface OrchestrationResult {
/** 各任务结果(失败的 key 值为 null */
results: Record<string, unknown>;
/** 是否有部分失败 */
partial: boolean;
/** 失败详情 */
failures: OrchestrationFailure[];
}
/**
* 并行执行多个下游调用,部分失败容忍。
*
* @param tasks - key → Promise 的映射
* @returns OrchestrationResult
*
* @example
* ```ts
* const result = await orchestrate({
* parent: iamClient.getUserInfo(id),
* children: iamClient.getChildrenByParent(id),
* });
* const parent = result.results.parent as UserInfoDto | null;
* const children = result.results.children as ChildDto[] | null;
* if (result.partial) { // 有部分失败 }
* ```
*/
export async function orchestrate(
tasks: Record<string, Promise<unknown>>,
): Promise<OrchestrationResult> {
const entries = Object.entries(tasks);
const settled = await Promise.allSettled(
entries.map(([, promise]) => promise),
);
const results: Record<string, unknown> = {};
const failures: OrchestrationFailure[] = [];
entries.forEach(([key], index) => {
const settledResult = settled[index];
if (settledResult && settledResult.status === "fulfilled") {
results[key] = settledResult.value;
} else if (settledResult && settledResult.status === "rejected") {
results[key] = null;
const error = settledResult.reason;
failures.push({ key, error });
logger.warn(
{ key, err: error },
"Orchestrator: downstream call failed",
);
}
});
return {
results,
partial: failures.length > 0,
failures,
};
}