Stream Processing Flink Topology
blockdevopsApache Flink & RisingWave stateful streaming graph: real-time DAG topology, window throughput, checkpointing duration, RocksDB state inspector, and backpressure heat monitor.
Installation
$pnpm dlx uipkge-ng@latest add stream-processing-flink-topology$npx uipkge-ng@latest add stream-processing-flink-topology$yarn dlx uipkge-ng@latest add stream-processing-flink-topology$bunx uipkge-ng@latest add stream-processing-flink-topologyManifest:
https://uipkge.dev/r/angular/stream-processing-flink-topology.jsonInstalls to:src/app/components/blocks/stream-processing-flink-topology/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
FlinkSubtaskinterface FlinkSubtask {
id: number
host: string
status: 'RUNNING' | 'DEPLOYING' | 'RECONCILING'
inRate: string
outRate: string
cpu: string
heapMemory: string
backpressure: number
watermarkLag: string
checkpointAckMs: number
}FlinkOperatorNodeinterface FlinkOperatorNode {
id: string
name: string
shortTitle: string
stageNumber: number
category: 'source' | 'window' | 'inference' | 'sink'
operatorClass: string
parallelism: number
status: 'RUNNING' | 'CANCELED' | 'FAILING'
backpressure: number
inputRecordsRate: string
inputBytesRate: string
outputRecordsRate: string
outputBytesRate: string
totalRecordsIn: number
totalRecordsOut: number
managedStateSize: string
stateBackend: string
heapMemory: string
managedMemory: string
gcTime: string
cpuPeak: string
watermarkLag: string
currentWatermark: string
description: string
config: Record<string, string>
subtasks: FlinkSubtask[]
}Files installed (6)
src/app/components/blocks/stream-processing-flink-topology/stream-processing-flink-topology.component.ts21.6 kB
// Apache Flink & RisingWave stateful streaming graph: real-time DAG topology, window // throughput, checkpointing duration, RocksDB state inspector, and backpressure heat // monitor. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { AlertTriangle, ArrowRight, Camera, Check, CheckCircle2, Clock, Copy, Cpu, Database, Loader2, LucideAngularModule, Radio, Square, Workflow, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiStreamOperatorDetailComponent } from './stream-operator-detail.component' import { UiStreamTopologyTelemetryComponent } from './stream-topology-telemetry.component' import type { FlinkOperatorNode, FlinkSubtask } from './stream-topology-types' import { defaultFlinkOperators } from './stream-topology-data' export type { FlinkOperatorNode, FlinkSubtask } @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-stream-processing-flink-topology, [ui-stream-processing-flink-topology]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiStreamOperatorDetailComponent, UiStreamTopologyTelemetryComponent, ], template: ` <div data-slot="stream-processing-flink-topology" [class]="rootClass"> <!-- 1. Header Section --> <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"> <div class="space-y-1.5"> <div class="flex flex-wrap items-center gap-2.5"> <div class="bg-muted text-muted-foreground border-border flex size-9 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Workflow" class="text-primary size-4.5" /> </div> <div class="flex flex-wrap items-center gap-2"> <h1 class="text-foreground font-mono text-xl font-bold tracking-tight break-all sm:text-2xl"> realtime-fraud-scoring-pipeline </h1> <span ui-badge variant="outline" class="font-mono text-xs">Flink 1.19 on Kubernetes</span> </div> <span ui-badge variant="success" class="gap-1.5 text-xs font-medium"> <span class="relative flex size-2 shrink-0"> <span class="bg-success absolute inline-flex h-full w-full rounded-full opacity-75"></span> <span class="bg-success relative inline-flex size-2 rounded-full"></span> </span> <span>Job Running · Checkpointing Healthy</span> </span> </div> <p class="text-muted-foreground font-mono text-xs"> Checkpoint Interval: Every 10s · RocksDB StateBackend · SLA Target: < 50ms Lag · Job ID: flink_job_992014 </p> </div> <!-- Action Buttons --> <div class="flex flex-wrap items-center gap-2"> <button ui-button variant="outline" size="sm" class="gap-1.5 text-xs font-medium" [disabled]="isTriggeringSavepoint()" (click)="handleTriggerSavepoint()" > @if (isTriggeringSavepoint()) { <lucide-icon [img]="Loader2" class="size-3.5 animate-spin" /> } @else { <lucide-icon [img]="Camera" class="text-info size-3.5" /> } <span>{{ isTriggeringSavepoint() ? 'Creating Savepoint...' : 'Trigger Savepoint' }}</span> </button> <button ui-button variant="outline" size="sm" class="text-destructive hover:border-destructive/40 hover:bg-destructive/10 gap-1.5 text-xs font-medium" [disabled]="isCanceling()" (click)="handleCancelJob()" > @if (isCanceling()) { <lucide-icon [img]="Loader2" class="size-3.5 animate-spin" /> } @else { <lucide-icon [img]="Square" class="size-3.5 fill-current" /> } <span>{{ isCanceling() ? 'Canceling with Savepoint...' : 'Cancel Job with Savepoint' }}</span> </button> </div> </div> <!-- Active Savepoint Notice Banner --> @if (savepointNotice(); as notice) { <div class="text-foreground border-info/30 bg-info/10 flex items-center justify-between rounded-lg border p-3 text-xs shadow-xs" role="status" > <div class="flex items-center gap-2.5"> <lucide-icon [img]="CheckCircle2" class="text-info size-4 shrink-0" /> <div> <span class="text-info font-semibold">Savepoint completed successfully:</span> <span class="ml-1 font-mono text-xs">{{ notice }}</span> </div> </div> <button ui-button variant="ghost" size="xs" class="h-6 gap-1 px-2 text-xs" (click)="copyText('savepoint', notice)" > @if (copiedKey() === 'savepoint') { <lucide-icon [img]="Check" class="text-success size-3" /> } @else { <lucide-icon [img]="Copy" class="size-3" /> } <span>{{ copiedKey() === 'savepoint' ? 'Copied' : 'Copy URI' }}</span> </button> </div> } <!-- Active Cancellation Notice Banner --> @if (cancelConfirmed()) { <div class="text-foreground border-warning/30 bg-warning/10 flex items-center justify-between rounded-lg border p-3 text-xs shadow-xs" role="status" > <div class="flex items-center gap-2.5"> <lucide-icon [img]="AlertTriangle" class="text-warning size-4 shrink-0" /> <div> <span class="text-warning font-semibold">Graceful Job Drain Initiated:</span> <span class="ml-1">Stopping topology sources and committing final RocksDB savepoint to S3 storage.</span> </div> </div> <span ui-badge variant="outline" class="font-mono text-xs">Drain Status: 100%</span> </div> } <!-- 2. 4 Stream Telemetry KPI Cards --> <ui-stream-topology-telemetry /> <!-- 3. Interactive 4-Operator Streaming Topology DAG --> <div ui-card class="shadow-xs"> <div ui-card-header class="pb-3"> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div> <div class="flex items-center gap-2"> <h3 ui-card-title class="text-base font-semibold">Streaming Execution Topology DAG</h3> <span ui-badge variant="outline" class="font-mono text-xs">4 Stateful Operators</span> </div> <p ui-card-description class="text-xs"> Live dataflow graph with event-time watermarking, state sizes, and backpressure telemetry. Click any operator to inspect metrics and subtasks. </p> </div> <div class="flex flex-wrap items-center gap-2"> <div class="border-border bg-muted/40 text-muted-foreground flex items-center gap-1.5 rounded-md border px-2.5 py-1 font-mono text-xs" > <span class="bg-success size-2 rounded-full"></span> <span>Backpressure Heat: 0.0% (Green)</span> </div> <div class="border-border bg-muted/40 text-muted-foreground flex items-center gap-1.5 rounded-md border px-2.5 py-1 font-mono text-xs" > <lucide-icon [img]="Database" class="size-3" /> <span>Total Managed State: 57.0 MB</span> </div> </div> </div> </div> <div ui-card-content class="p-4 pt-1 sm:p-6"> <div class="border-border bg-muted/15 rounded-xl border p-4 sm:p-5"> <!-- 4 Connected Nodes Grid --> <div class="grid grid-cols-1 gap-4 lg:grid-cols-4"> <!-- Node 1: Kafka Ingestion Source --> <div role="button" tabindex="0" [class]="nodeClass('op-kafka-source')" (click)="selectOperator('op-kafka-source')" (keydown.enter)="selectOperator('op-kafka-source')" (keydown.space)="$event.preventDefault(); selectOperator('op-kafka-source')" > <div class="space-y-3"> <div class="flex items-start justify-between gap-2"> <div class="flex items-center gap-2"> <div class="border-info/30 bg-info/10 text-info flex size-7 items-center justify-center rounded-md border" > <lucide-icon [img]="Radio" class="size-3.5" /> </div> <span ui-badge variant="outline" class="border-info/30 text-info font-mono text-xs">SOURCE</span> </div> <span ui-badge variant="outline" class="font-mono text-xs">p=8</span> </div> <div> <div class="text-foreground font-mono text-xs leading-snug font-bold">Kafka Ingestion Source</div> <p class="text-muted-foreground mt-1 font-mono text-xs">42.5k rec/s · Backpressure: 0%</p> </div> <div class="border-border/60 space-y-1.5 border-t pt-2.5 font-mono text-xs"> <div class="text-muted-foreground flex items-center justify-between"> <span>Throughput:</span> <span class="text-foreground font-semibold tabular-nums">8.4 MB/s</span> </div> <div class="text-muted-foreground flex items-center justify-between"> <span>State Size:</span> <span class="text-foreground tabular-nums">0 MB (Stateless)</span> </div> </div> </div> <div class="border-border/60 mt-3 flex items-center justify-between border-t pt-2 text-xs"> <span class="text-muted-foreground font-mono">events.fraud.transactions</span> <lucide-icon [img]="ArrowRight" class="text-muted-foreground group-hover:text-primary size-3.5 shrink-0 transition-colors" /> </div> </div> <!-- Node 2: Keyed Sliding Window --> <div role="button" tabindex="0" [class]="nodeClass('op-keyed-window')" (click)="selectOperator('op-keyed-window')" (keydown.enter)="selectOperator('op-keyed-window')" (keydown.space)="$event.preventDefault(); selectOperator('op-keyed-window')" > <div class="space-y-3"> <div class="flex items-start justify-between gap-2"> <div class="flex items-center gap-2"> <div class="border-chart-1/30 bg-chart-1/10 text-chart-1 flex size-7 items-center justify-center rounded-md border" > <lucide-icon [img]="Clock" class="size-3.5" /> </div> <span ui-badge variant="outline" class="border-chart-1/30 text-chart-1 font-mono text-xs" >WINDOW</span > </div> <span ui-badge variant="outline" class="font-mono text-xs">p=16</span> </div> <div> <div class="text-foreground font-mono text-xs leading-snug font-bold">Keyed Sliding Window</div> <p class="text-muted-foreground mt-1 font-mono text-xs">5-min window, 10s slide · 12,400 keys</p> </div> <div class="border-border/60 space-y-1.5 border-t pt-2.5 font-mono text-xs"> <div class="text-muted-foreground flex items-center justify-between"> <span>Output Rate:</span> <span class="text-foreground font-semibold tabular-nums">38.2k rec/s</span> </div> <div class="text-muted-foreground flex items-center justify-between"> <span>RocksDB State:</span> <span class="text-foreground text-chart-1 font-semibold tabular-nums">18.4 MB</span> </div> </div> </div> <div class="border-border/60 mt-3 flex items-center justify-between border-t pt-2 text-xs"> <span class="text-muted-foreground font-mono">Cardholder Velocity Agg</span> <lucide-icon [img]="ArrowRight" class="text-muted-foreground group-hover:text-primary size-3.5 shrink-0 transition-colors" /> </div> </div> <!-- Node 3: ML Fraud Scoring Pattern Matcher --> <div role="button" tabindex="0" [class]="nodeClass('op-ml-scoring')" (click)="selectOperator('op-ml-scoring')" (keydown.enter)="selectOperator('op-ml-scoring')" (keydown.space)="$event.preventDefault(); selectOperator('op-ml-scoring')" > <div class="space-y-3"> <div class="flex items-start justify-between gap-2"> <div class="flex items-center gap-2"> <div class="border-chart-2/30 bg-chart-2/10 text-chart-2 flex size-7 items-center justify-center rounded-md border" > <lucide-icon [img]="Cpu" class="size-3.5" /> </div> <span ui-badge variant="outline" class="border-chart-2/30 text-chart-2 font-mono text-xs" >CEP & ML</span > </div> <span ui-badge variant="outline" class="font-mono text-xs">p=16</span> </div> <div> <div class="text-foreground font-mono text-xs leading-snug font-bold">ML Fraud Scoring Matcher</div> <p class="text-muted-foreground mt-1 font-mono text-xs">Inference: 1.2ms · RocksDB: 38.2 MB</p> </div> <div class="border-border/60 space-y-1.5 border-t pt-2.5 font-mono text-xs"> <div class="text-muted-foreground flex items-center justify-between"> <span>ONNX Inference:</span> <span class="text-success text-success font-semibold tabular-nums">p99 < 1.2ms</span> </div> <div class="text-muted-foreground flex items-center justify-between"> <span>Managed State:</span> <span class="text-foreground text-chart-2 font-semibold tabular-nums">38.2 MB</span> </div> </div> </div> <div class="border-border/60 mt-3 flex items-center justify-between border-t pt-2 text-xs"> <span class="text-muted-foreground font-mono">xgboost_v4.onnx</span> <lucide-icon [img]="ArrowRight" class="text-muted-foreground group-hover:text-primary size-3.5 shrink-0 transition-colors" /> </div> </div> <!-- Node 4: Sink: Alert Dispatcher & Redis Cache --> <div role="button" tabindex="0" [class]="nodeClass('op-sink-dispatcher')" (click)="selectOperator('op-sink-dispatcher')" (keydown.enter)="selectOperator('op-sink-dispatcher')" (keydown.space)="$event.preventDefault(); selectOperator('op-sink-dispatcher')" > <div class="space-y-3"> <div class="flex items-start justify-between gap-2"> <div class="flex items-center gap-2"> <div class="border-success/30 bg-success/10 text-success flex size-7 items-center justify-center rounded-md border" > <lucide-icon [img]="Database" class="size-3.5" /> </div> <span ui-badge variant="outline" class="border-success/30 text-success font-mono text-xs" >SINK</span > </div> <span ui-badge variant="outline" class="font-mono text-xs">p=8</span> </div> <div> <div class="text-foreground font-mono text-xs leading-snug font-bold">Sink: Alert Dispatcher</div> <p class="text-muted-foreground mt-1 font-mono text-xs">Redis Cache & Webhooks · 42.5k rec/s</p> </div> <div class="border-border/60 space-y-1.5 border-t pt-2.5 font-mono text-xs"> <div class="text-muted-foreground flex items-center justify-between"> <span>Semantics:</span> <span class="text-foreground font-semibold">2PC Exactly-Once</span> </div> <div class="text-muted-foreground flex items-center justify-between"> <span>Fan-Out:</span> <span class="text-foreground font-semibold tabular-nums">42.5k rec/s</span> </div> </div> </div> <div class="border-border/60 mt-3 flex items-center justify-between border-t pt-2 text-xs"> <span class="text-muted-foreground font-mono">redis:6379 + alerts.queue</span> <lucide-icon [img]="CheckCircle2" class="text-success size-3.5 shrink-0" /> </div> </div> </div> </div> </div> </div> <!-- 4. Operator Detailed Performance & State Inspector Panel --> <ui-stream-operator-detail [operator]="selectedOperator()" [copiedKey]="copiedKey()" (copy)="copyText($event.key, $event.value)" /> </div> `, }) export class UiStreamProcessingFlinkTopologyComponent { protected readonly AlertTriangle = AlertTriangle protected readonly ArrowRight = ArrowRight protected readonly Camera = Camera protected readonly Check = Check protected readonly CheckCircle2 = CheckCircle2 protected readonly Clock = Clock protected readonly Copy = Copy protected readonly Cpu = Cpu protected readonly Database = Database protected readonly Loader2 = Loader2 protected readonly Radio = Radio protected readonly Square = Square protected readonly Workflow = Workflow @Input() operators: FlinkOperatorNode[] = defaultFlinkOperators @Input('class') className?: string readonly selectedOperatorId = signal<string>('op-ml-scoring') readonly isTriggeringSavepoint = signal<boolean>(false) readonly savepointNotice = signal<string | null>(null) readonly isCanceling = signal<boolean>(false) readonly cancelConfirmed = signal<boolean>(false) readonly copiedKey = signal<string | null>(null) readonly selectedOperator = computed(() => { return this.operators.find((op) => op.id === this.selectedOperatorId()) ?? this.operators[0]! }) get rootClass(): string { return cn('w-full space-y-6', this.className) } nodeClass(id: string): string { return cn( 'group focus-visible:ring-ring relative flex cursor-pointer flex-col justify-between rounded-xl border p-4 transition-colors duration-200 focus-visible:ring-2 focus-visible:outline-none', this.selectedOperatorId() === id ? 'border-primary bg-primary/[0.04] ring-primary/30 shadow-xs ring-2' : 'border-border bg-card hover:border-primary/50 hover:bg-muted/30', ) } selectOperator(id: string): void { this.selectedOperatorId.set(id) } handleTriggerSavepoint(): void { if (this.isTriggeringSavepoint()) return this.isTriggeringSavepoint.set(true) this.savepointNotice.set(null) setTimeout(() => { this.isTriggeringSavepoint.set(false) this.savepointNotice.set('s3://flink-savepoints/prod/savepoint-fraud-90412-20260821-143245/') setTimeout(() => { if (this.savepointNotice()?.includes('savepoint-fraud-90412')) this.savepointNotice.set(null) }, 6500) }, 1800) } handleCancelJob(): void { if (this.isCanceling()) return this.isCanceling.set(true) setTimeout(() => { this.isCanceling.set(false) this.cancelConfirmed.set(true) setTimeout(() => { this.cancelConfirmed.set(false) }, 4500) }, 2000) } copyText(key: string, value: string): void { try { if (typeof navigator !== 'undefined' && navigator.clipboard) { void navigator.clipboard.writeText(value) this.copiedKey.set(key) setTimeout(() => { if (this.copiedKey() === key) this.copiedKey.set(null) }, 2000) } } catch { // Clipboard fallback } } }src/app/components/blocks/stream-processing-flink-topology/stream-operator-detail.component.ts18.2 kB
// Operator detailed performance and state inspector panel: subtask distribution table, // RocksDB managed state, JVM and GC telemetry, and runtime configuration parameters. // Port of the Vue/React operator detail panel 1:1. import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, signal } from '@angular/core' import { Activity, Check, Copy, LucideAngularModule, Server, ShieldCheck } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiProgressComponent } from '@/ui/progress/progress.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' import type { FlinkOperatorNode } from './stream-topology-types' export type InspectorTab = 'subtasks' | 'state' | 'resources' | 'config' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-stream-operator-detail, [ui-stream-operator-detail]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiProgressComponent, UiSeparatorComponent, UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div ui-card class="shadow-xs"> <div ui-card-header class="pb-3"> <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <div class="flex flex-wrap items-center gap-2"> <h3 ui-card-title class="font-mono text-base font-bold">{{ operator.name }}</h3> <span ui-badge variant="success" class="gap-1 font-mono text-xs"> <span class="bg-success size-1.5 rounded-full"></span> {{ operator.status }} </span> <span ui-badge variant="outline" class="font-mono text-xs"> Parallelism: {{ operator.parallelism }} subtasks </span> </div> <p ui-card-description class="text-xs">{{ operator.description }}</p> </div> <!-- Inspector Tab Navigation Buttons --> <div class="bg-muted/60 flex flex-wrap items-center gap-1 rounded-lg border p-1"> <button type="button" [class]="tabClass('subtasks')" (click)="activeInspectorTab.set('subtasks')"> Subtasks & Distribution </button> <button type="button" [class]="tabClass('state')" (click)="activeInspectorTab.set('state')"> RocksDB Managed State </button> <button type="button" [class]="tabClass('resources')" (click)="activeInspectorTab.set('resources')"> JVM & GC Telemetry </button> <button type="button" [class]="tabClass('config')" (click)="activeInspectorTab.set('config')"> Configuration Parameters </button> </div> </div> </div> <!-- Key Operator Telemetry Bar --> <div class="bg-muted/20 divide-border/60 grid grid-cols-2 divide-x border-y sm:grid-cols-4 lg:grid-cols-6"> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">Input Rate</div> <div class="text-foreground mt-0.5 font-mono text-sm font-bold tabular-nums"> {{ operator.inputRecordsRate }} </div> <div class="text-muted-foreground font-mono text-xs tabular-nums">{{ operator.inputBytesRate }}</div> </div> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">Output Rate</div> <div class="text-foreground mt-0.5 font-mono text-sm font-bold tabular-nums"> {{ operator.outputRecordsRate }} </div> <div class="text-muted-foreground font-mono text-xs tabular-nums">{{ operator.outputBytesRate }}</div> </div> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">Managed State</div> <div class="text-primary mt-0.5 font-mono text-sm font-bold tabular-nums"> {{ operator.managedStateSize }} </div> <div class="text-muted-foreground truncate font-mono text-xs">{{ operator.stateBackend }}</div> </div> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">Watermark Lag</div> <div class="text-success text-success mt-0.5 font-mono text-sm font-bold tabular-nums"> {{ operator.watermarkLag }} </div> <div class="text-muted-foreground font-mono text-xs">Event Time OK</div> </div> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">CPU Core Load</div> <div class="text-foreground mt-0.5 font-mono text-sm font-bold tabular-nums">{{ operator.cpuPeak }}</div> <div class="text-muted-foreground font-mono text-xs">Across {{ operator.parallelism }} cores</div> </div> <div class="p-3"> <div class="text-muted-foreground text-xs font-medium">GC Pause Time</div> <div class="text-foreground mt-0.5 font-mono text-sm font-bold tabular-nums">{{ operator.gcTime }}</div> <div class="text-muted-foreground font-mono text-xs">G1 Garbage Collector</div> </div> </div> <div ui-card-content class="p-0"> <!-- TAB 1: Subtasks Table --> @if (activeInspectorTab() === 'subtasks') { <div class="overflow-x-auto"> <table ui-table> <thead ui-table-header> <tr ui-table-row> <th ui-table-head class="text-xs font-semibold">Subtask Index</th> <th ui-table-head class="text-xs font-semibold">Host / Pod</th> <th ui-table-head class="text-right text-xs font-semibold">Input Rate</th> <th ui-table-head class="text-right text-xs font-semibold">Output Rate</th> <th ui-table-head class="text-right text-xs font-semibold">CPU %</th> <th ui-table-head class="text-right text-xs font-semibold">Heap Usage</th> <th ui-table-head class="text-right text-xs font-semibold">Watermark Lag</th> <th ui-table-head class="text-right text-xs font-semibold">Checkpoint Ack</th> <th ui-table-head class="text-center text-xs font-semibold">Status</th> </tr> </thead> <tbody ui-table-body> @for (subtask of operator.subtasks; track subtask.id) { <tr ui-table-row class="group"> <td ui-table-cell class="py-2.5"> <div class="flex items-center gap-2"> <span class="bg-success size-2 rounded-full" aria-hidden="true"></span> <span class="font-mono text-xs font-semibold">Subtask #{{ subtask.id }}</span> </div> </td> <td ui-table-cell class="text-muted-foreground py-2.5 font-mono text-xs">{{ subtask.host }}</td> <td ui-table-cell class="text-foreground py-2.5 text-right font-mono text-xs font-medium tabular-nums" > {{ subtask.inRate }} </td> <td ui-table-cell class="text-foreground py-2.5 text-right font-mono text-xs font-medium tabular-nums" > {{ subtask.outRate }} </td> <td ui-table-cell class="py-2.5 text-right font-mono text-xs tabular-nums"> <span ui-badge variant="outline" class="font-mono text-xs">{{ subtask.cpu }}</span> </td> <td ui-table-cell class="text-muted-foreground py-2.5 text-right font-mono text-xs tabular-nums"> {{ subtask.heapMemory }} </td> <td ui-table-cell class="text-success text-success py-2.5 text-right font-mono text-xs font-medium tabular-nums" > {{ subtask.watermarkLag }} </td> <td ui-table-cell class="text-muted-foreground py-2.5 text-right font-mono text-xs tabular-nums"> {{ subtask.checkpointAckMs }}ms </td> <td ui-table-cell class="py-2.5 text-center"> <span ui-badge variant="success" class="font-mono text-xs">{{ subtask.status }}</span> </td> </tr> } </tbody> </table> </div> } @else if (activeInspectorTab() === 'state') { <!-- TAB 2: RocksDB Managed State --> <div class="space-y-5 p-5"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-3"> <div class="bg-muted/10 space-y-2 rounded-lg border p-4"> <div class="text-muted-foreground text-xs font-medium">State Backend Engine</div> <div class="text-foreground font-mono text-base font-bold">{{ operator.stateBackend }}</div> <p class="text-muted-foreground text-xs"> Off-heap SSD persistence with block caching and incremental checkpoints </p> </div> <div class="bg-muted/10 space-y-2 rounded-lg border p-4"> <div class="text-muted-foreground text-xs font-medium">Total Live State Size</div> <div class="text-primary font-mono text-base font-bold">{{ operator.managedStateSize }}</div> <p class="text-muted-foreground text-xs">Incremental checkpoint delta: ~2.4 MB / snapshot</p> </div> <div class="bg-muted/10 space-y-2 rounded-lg border p-4"> <div class="text-muted-foreground text-xs font-medium">Key Eviction & TTL</div> <div class="text-foreground font-mono text-base font-bold">10-min Sliding TTL</div> <p class="text-muted-foreground text-xs"> Automatic compaction filter evicts expired cardholder keys on disk </p> </div> </div> <!-- RocksDB Performance Metrics Grid --> <div class="bg-card space-y-3 rounded-lg border p-4"> <h4 class="text-foreground text-xs font-semibold">RocksDB Native Engine Statistics</h4> <div class="grid grid-cols-2 gap-4 font-mono text-xs sm:grid-cols-4"> <div> <span class="text-muted-foreground">Block Cache Hit Ratio:</span> <div class="text-foreground mt-0.5 font-bold tabular-nums">99.42% (512 MB cache)</div> </div> <div> <span class="text-muted-foreground">MemTable Flush Rate:</span> <div class="text-foreground mt-0.5 font-bold tabular-nums">14.2 MB/min</div> </div> <div> <span class="text-muted-foreground">SST Files Count:</span> <div class="text-foreground mt-0.5 font-bold tabular-nums">128 active SSTs</div> </div> <div> <span class="text-muted-foreground">Level-0 Compaction Stall:</span> <div class="text-success text-success mt-0.5 font-bold tabular-nums">0.0ms (No stalls)</div> </div> </div> </div> </div> } @else if (activeInspectorTab() === 'resources') { <!-- TAB 3: JVM & Resource Metrics --> <div class="space-y-5 p-5"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="bg-muted/10 space-y-3 rounded-lg border p-4"> <div class="flex items-center justify-between text-xs"> <span class="text-foreground font-medium">JVM Heap Memory Utilization</span> <span class="text-muted-foreground font-mono">{{ operator.heapMemory }} / 4,096 MB</span> </div> <ui-progress [value]="34" class="h-2" /> <div class="text-muted-foreground flex justify-between font-mono text-xs"> <span>Survivor: 64 MB</span> <span>Tenured / Old: 348 MB</span> </div> </div> <div class="bg-muted/10 space-y-3 rounded-lg border p-4"> <div class="flex items-center justify-between text-xs"> <span class="text-foreground font-medium">Managed Off-Heap & Native Memory</span> <span class="text-muted-foreground font-mono">{{ operator.managedMemory }} Allocated</span> </div> <ui-progress [value]="50" class="h-2" /> <div class="text-muted-foreground flex justify-between font-mono text-xs"> <span>RocksDB Cache: 512 MB</span> <span>Network Buffers: 256 MB</span> </div> </div> </div> <div class="bg-card space-y-2 rounded-lg border p-4"> <div class="text-foreground text-xs font-semibold">Garbage Collection Profile</div> <p class="text-muted-foreground font-mono text-xs"> Collector: Garbage-First (G1) GC · Max Pause Target: 20ms · Current Observed Pause: {{ operator.gcTime }} </p> </div> </div> } @else { <!-- TAB 4: Configuration Parameters --> <div class="space-y-4 p-5"> <div class="overflow-hidden rounded-lg border"> <div class="bg-muted/50 text-foreground flex items-center justify-between border-b p-3 text-xs font-semibold" > <span>Runtime Operator Configuration</span> <span ui-badge variant="outline" class="font-mono text-xs">{{ operator.operatorClass }}</span> </div> <div class="divide-y font-mono text-xs"> @for (entry of configEntries(); track entry.key) { <div class="hover:bg-muted/20 flex flex-col justify-between gap-2 p-3 transition-colors sm:flex-row sm:items-center" > <span class="text-muted-foreground font-medium">{{ entry.key }}</span> <div class="flex items-center gap-2"> <span class="text-foreground font-semibold">{{ entry.value }}</span> <button ui-button [attr.aria-label]="'Copy ' + entry.key" variant="ghost" size="xs" class="text-muted-foreground hover:text-foreground h-6 w-6 p-0" (click)="copy.emit({ key: entry.key, value: entry.value })" > @if (copiedKey === entry.key) { <lucide-icon [img]="Check" class="text-success size-3" /> } @else { <lucide-icon [img]="Copy" class="size-3" /> } </button> </div> </div> } </div> </div> </div> } </div> <!-- Bottom Engine Metadata Strip --> <div class="bg-muted/40 flex flex-col gap-2.5 border-t px-4 py-3 font-mono text-xs sm:flex-row sm:items-center sm:justify-between" > <div class="text-muted-foreground flex flex-wrap items-center gap-4"> <div class="text-foreground flex items-center gap-1.5 font-medium"> <lucide-icon [img]="Activity" class="text-success size-3.5" /> <span>Pipeline Ingestion:</span> <span>42,500 records/sec (8.4 MB/s)</span> </div> <ui-separator orientation="vertical" class="hidden h-3.5 sm:block" /> <div class="flex items-center gap-1.5"> <lucide-icon [img]="Server" class="text-info size-3.5" /> <span>TaskManagers:</span> <span class="text-foreground">8 Kubernetes Pods</span> </div> <ui-separator orientation="vertical" class="hidden h-3.5 sm:block" /> <div class="flex items-center gap-1.5"> <lucide-icon [img]="ShieldCheck" class="text-success size-3.5" /> <span>State Consistency:</span> <span class="text-foreground">Exactly-Once (2PC)</span> </div> </div> <div class="text-muted-foreground flex items-center gap-2"> <span class="bg-success size-1.5 rounded-full"></span> <span>Watermark Alignment: 42ms · Checkpoint #1,420 OK</span> </div> </div> </div> `, }) export class UiStreamOperatorDetailComponent { protected readonly Activity = Activity protected readonly Check = Check protected readonly Copy = Copy protected readonly Server = Server protected readonly ShieldCheck = ShieldCheck @Input({ required: true }) operator!: FlinkOperatorNode @Input() copiedKey: string | null = null @Output() copy = new EventEmitter<{ key: string; value: string }>() readonly activeInspectorTab = signal<InspectorTab>('subtasks') tabClass(tab: InspectorTab): string { return cn( 'focus-visible:ring-ring rounded-md px-2.5 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none', this.activeInspectorTab() === tab ? 'bg-card text-foreground font-semibold shadow-xs' : 'text-muted-foreground hover:text-foreground', ) } configEntries(): { key: string; value: string }[] { return Object.entries(this.operator.config ?? {}).map(([key, value]) => ({ key, value })) } }src/app/components/blocks/stream-processing-flink-topology/stream-topology-telemetry.component.ts7.6 kB
// Real-time stream telemetry KPI cards: ingestion throughput, checkpoint duration, // watermark alignment latency, and backpressure heat. Port of the Vue/React telemetry // card grid 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { Activity, Clock, Flame, LucideAngularModule, Zap } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiCardComponent, UiCardContentComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiProgressComponent } from '@/ui/progress/progress.component' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-stream-topology-telemetry, [ui-stream-topology-telemetry]', standalone: true, host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiCardComponent, UiCardContentComponent, UiCardHeaderComponent, UiCardTitleComponent, UiProgressComponent, ], template: ` <div [class]="rootClass"> <!-- Card 1: Current Ingestion Throughput --> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Activity" class="text-primary size-4" /> </div> <h3 ui-card-title class="truncate text-xs font-medium">Current Ingestion Throughput</h3> </div> <span ui-badge variant="outline" class="shrink-0 text-xs font-normal tabular-nums">+4.2% 10m</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums"> 42,500 records/sec </div> <p class="text-muted-foreground font-mono text-xs tabular-nums">8.4 MB/s ingress bandwidth</p> </div> <div class="space-y-1.5"> <div class="text-muted-foreground flex items-center justify-between text-xs"> <span>Ingress Capacity</span> <span class="text-foreground font-mono font-medium tabular-nums">68% allocated</span> </div> <ui-progress [value]="68" class="h-1.5" /> </div> </div> </div> <!-- Card 2: Checkpoint Duration --> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Clock" class="text-success size-4" /> </div> <h3 ui-card-title class="truncate text-xs font-medium">Checkpoint Duration</h3> </div> <span ui-badge variant="success" class="shrink-0 font-mono text-xs">100% Succeeded</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums">185ms duration</div> <p class="text-muted-foreground font-mono text-xs tabular-nums">42 MB state size · Checkpoint #1,420</p> </div> <div class="space-y-1.5"> <div class="text-muted-foreground flex items-center justify-between text-xs"> <span>SLA Budget (1,000ms)</span> <span class="text-foreground font-mono font-medium tabular-nums">18.5% of interval</span> </div> <ui-progress [value]="18.5" class="h-1.5" /> </div> </div> </div> <!-- Card 3: Watermark Alignment Latency --> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Zap" class="text-warning size-4" /> </div> <h3 ui-card-title class="truncate text-xs font-medium">Watermark Alignment</h3> </div> <span ui-badge variant="outline" class="border-success/30 text-success font-mono text-xs">Event Time</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums">Lag: 42ms</div> <p class="text-muted-foreground font-mono text-xs tabular-nums">Event Time Alignment · 0 Late Drops</p> </div> <div class="space-y-1.5"> <div class="text-muted-foreground flex items-center justify-between text-xs"> <span>Skew Drift</span> <span class="text-foreground font-mono font-medium tabular-nums">Max 12ms cross-partition</span> </div> <ui-progress [value]="8.4" class="h-1.5" /> </div> </div> </div> <!-- Card 4: Max Backpressure Heat Ratio --> <div ui-card class="flex flex-col justify-between shadow-xs"> <div ui-card-header class="pb-2"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Flame" class="text-success size-4" /> </div> <h3 ui-card-title class="truncate text-xs font-medium">Max Backpressure Heat</h3> </div> <span ui-badge variant="success" class="shrink-0 font-mono text-xs">Low Risk</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums"> 0.0% Backpressure </div> <p class="text-muted-foreground font-mono text-xs tabular-nums">All 4 operators running at nominal load</p> </div> <div class="space-y-1.5"> <div class="text-muted-foreground flex items-center justify-between text-xs"> <span>Choked Subtasks</span> <span class="text-success text-success font-mono font-medium tabular-nums">0 / 48 active</span> </div> <ui-progress [value]="0" class="h-1.5" /> </div> </div> </div> </div> `, }) export class UiStreamTopologyTelemetryComponent { protected readonly Activity = Activity protected readonly Clock = Clock protected readonly Flame = Flame protected readonly Zap = Zap @Input('class') className?: string get rootClass(): string { return cn('grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4', this.className) } }src/app/components/blocks/stream-processing-flink-topology/stream-topology-types.ts0.9 kB
export interface FlinkSubtask { id: number host: string status: 'RUNNING' | 'DEPLOYING' | 'RECONCILING' inRate: string outRate: string cpu: string heapMemory: string backpressure: number watermarkLag: string checkpointAckMs: number } export interface FlinkOperatorNode { id: string name: string shortTitle: string stageNumber: number category: 'source' | 'window' | 'inference' | 'sink' operatorClass: string parallelism: number status: 'RUNNING' | 'CANCELED' | 'FAILING' backpressure: number inputRecordsRate: string inputBytesRate: string outputRecordsRate: string outputBytesRate: string totalRecordsIn: number totalRecordsOut: number managedStateSize: string stateBackend: string heapMemory: string managedMemory: string gcTime: string cpuPeak: string watermarkLag: string currentWatermark: string description: string config: Record<string, string> subtasks: FlinkSubtask[] }src/app/components/blocks/stream-processing-flink-topology/stream-topology-data.ts19.2 kB
import type { FlinkOperatorNode } from './stream-topology-types' export const defaultFlinkOperators: FlinkOperatorNode[] = [ { id: 'op-kafka-source', name: 'Source: Kafka Ingestion (events.fraud.transactions)', shortTitle: 'Kafka Ingestion Source', stageNumber: 1, category: 'source', operatorClass: 'org.apache.flink.connector.kafka.source.KafkaSource', parallelism: 8, status: 'RUNNING', backpressure: 0.0, inputRecordsRate: '42,500 rec/s', inputBytesRate: '8.4 MB/s', outputRecordsRate: '42,500 rec/s', outputBytesRate: '8.4 MB/s', totalRecordsIn: 184290410, totalRecordsOut: 184290410, managedStateSize: '0 MB (Stateless)', stateBackend: 'None (Stateless Source)', heapMemory: '412 MB', managedMemory: '0 MB', gcTime: '0.8 ms/min', cpuPeak: '14%', watermarkLag: '28ms', currentWatermark: '2026-08-21 14:32:45.892 UTC', description: 'Consumes high-throughput payment transaction stream with snappy decompression and bounded out-of-orderness watermark generation.', config: { topic: 'events.fraud.transactions', 'consumer.group.id': 'flink-fraud-scoring-v2', 'scan.startup.mode': 'latest-offset', 'watermark.strategy': 'BoundedOutOfOrderness(Duration.ofMillis(200))', 'partition.discovery.interval.ms': '30000', 'deserializer.format': 'AvroConfluentSchemaRegistry', }, subtasks: [ { id: 0, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '5,320 rec/s', outRate: '5,320 rec/s', cpu: '14%', heapMemory: '52 MB', backpressure: 0.0, watermarkLag: '22ms', checkpointAckMs: 42, }, { id: 1, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '5,310 rec/s', outRate: '5,310 rec/s', cpu: '13%', heapMemory: '51 MB', backpressure: 0.0, watermarkLag: '24ms', checkpointAckMs: 44, }, { id: 2, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '5,315 rec/s', outRate: '5,315 rec/s', cpu: '15%', heapMemory: '53 MB', backpressure: 0.0, watermarkLag: '26ms', checkpointAckMs: 41, }, { id: 3, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '5,305 rec/s', outRate: '5,305 rec/s', cpu: '14%', heapMemory: '50 MB', backpressure: 0.0, watermarkLag: '23ms', checkpointAckMs: 45, }, { id: 4, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '5,325 rec/s', outRate: '5,325 rec/s', cpu: '14%', heapMemory: '52 MB', backpressure: 0.0, watermarkLag: '25ms', checkpointAckMs: 40, }, { id: 5, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '5,310 rec/s', outRate: '5,310 rec/s', cpu: '13%', heapMemory: '51 MB', backpressure: 0.0, watermarkLag: '24ms', checkpointAckMs: 43, }, { id: 6, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '5,308 rec/s', outRate: '5,308 rec/s', cpu: '15%', heapMemory: '52 MB', backpressure: 0.0, watermarkLag: '28ms', checkpointAckMs: 42, }, { id: 7, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '5,307 rec/s', outRate: '5,307 rec/s', cpu: '14%', heapMemory: '51 MB', backpressure: 0.0, watermarkLag: '25ms', checkpointAckMs: 43, }, ], }, { id: 'op-keyed-window', name: 'Keyed Sliding Window (5-min window, 10s slide)', shortTitle: 'Keyed Sliding Window', stageNumber: 2, category: 'window', operatorClass: 'org.apache.flink.streaming.runtime.operators.windowing.WindowOperator', parallelism: 16, status: 'RUNNING', backpressure: 0.0, inputRecordsRate: '42,500 rec/s', inputBytesRate: '8.4 MB/s', outputRecordsRate: '38,200 rec/s', outputBytesRate: '6.9 MB/s', totalRecordsIn: 184290410, totalRecordsOut: 165861369, managedStateSize: '18.4 MB (RocksDB)', stateBackend: 'EmbeddedRocksDBStateBackend', heapMemory: '780 MB', managedMemory: '1,024 MB', gcTime: '1.2 ms/min', cpuPeak: '28%', watermarkLag: '34ms', currentWatermark: '2026-08-21 14:32:45.886 UTC', description: 'Aggregates velocity, cumulative transaction volumes, and rapid card-testing frequency per cardholder ID across 5m windows triggered every 10s.', config: { 'window.assigner': 'SlidingEventTimeWindows.of(Time.minutes(5), Time.seconds(10))', 'allowed.lateness': '0ms', 'state.backend.ttl': '600000ms (10m TTL)', 'state.active.cardholders': '12,400 keys in state', trigger: 'EventTimeTrigger.create()', evictor: 'TimeEvictor.of(Time.minutes(5))', }, subtasks: [ { id: 0, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '2,660 rec/s', outRate: '2,390 rec/s', cpu: '28%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '32ms', checkpointAckMs: 65, }, { id: 1, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '2,655 rec/s', outRate: '2,385 rec/s', cpu: '27%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 68, }, { id: 2, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '2,662 rec/s', outRate: '2,392 rec/s', cpu: '29%', heapMemory: '50 MB', backpressure: 0.0, watermarkLag: '33ms', checkpointAckMs: 64, }, { id: 3, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '2,650 rec/s', outRate: '2,380 rec/s', cpu: '28%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '35ms', checkpointAckMs: 66, }, { id: 4, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '2,658 rec/s', outRate: '2,388 rec/s', cpu: '27%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 65, }, { id: 5, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '2,654 rec/s', outRate: '2,386 rec/s', cpu: '29%', heapMemory: '50 MB', backpressure: 0.0, watermarkLag: '33ms', checkpointAckMs: 67, }, { id: 6, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '2,660 rec/s', outRate: '2,390 rec/s', cpu: '28%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '35ms', checkpointAckMs: 66, }, { id: 7, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '2,652 rec/s', outRate: '2,384 rec/s', cpu: '28%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 64, }, { id: 8, host: 'taskmanager-pod-4.k8s', status: 'RUNNING', inRate: '2,659 rec/s', outRate: '2,389 rec/s', cpu: '29%', heapMemory: '50 MB', backpressure: 0.0, watermarkLag: '33ms', checkpointAckMs: 65, }, { id: 9, host: 'taskmanager-pod-4.k8s', status: 'RUNNING', inRate: '2,655 rec/s', outRate: '2,385 rec/s', cpu: '27%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '36ms', checkpointAckMs: 68, }, { id: 10, host: 'taskmanager-pod-5.k8s', status: 'RUNNING', inRate: '2,661 rec/s', outRate: '2,391 rec/s', cpu: '28%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 63, }, { id: 11, host: 'taskmanager-pod-5.k8s', status: 'RUNNING', inRate: '2,650 rec/s', outRate: '2,380 rec/s', cpu: '28%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '35ms', checkpointAckMs: 66, }, { id: 12, host: 'taskmanager-pod-6.k8s', status: 'RUNNING', inRate: '2,657 rec/s', outRate: '2,387 rec/s', cpu: '27%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '33ms', checkpointAckMs: 67, }, { id: 13, host: 'taskmanager-pod-6.k8s', status: 'RUNNING', inRate: '2,653 rec/s', outRate: '2,383 rec/s', cpu: '29%', heapMemory: '50 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 65, }, { id: 14, host: 'taskmanager-pod-7.k8s', status: 'RUNNING', inRate: '2,658 rec/s', outRate: '2,388 rec/s', cpu: '28%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '35ms', checkpointAckMs: 66, }, { id: 15, host: 'taskmanager-pod-7.k8s', status: 'RUNNING', inRate: '2,651 rec/s', outRate: '2,382 rec/s', cpu: '28%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '34ms', checkpointAckMs: 64, }, ], }, { id: 'op-ml-scoring', name: 'ML Fraud Scoring Pattern Matcher (CEP + XGBoost ONNX)', shortTitle: 'ML Fraud Scoring Pattern Matcher', stageNumber: 3, category: 'inference', operatorClass: 'com.uipkge.fraud.engine.AsyncFraudScoringKeyedProcessFunction', parallelism: 16, status: 'RUNNING', backpressure: 0.0, inputRecordsRate: '38,200 rec/s', inputBytesRate: '6.9 MB/s', outputRecordsRate: '38,200 rec/s', outputBytesRate: '7.1 MB/s', totalRecordsIn: 165861369, totalRecordsOut: 165861369, managedStateSize: '38.2 MB (RocksDB)', stateBackend: 'EmbeddedRocksDBStateBackend', heapMemory: '1,420 MB', managedMemory: '2,048 MB', gcTime: '1.4 ms/min', cpuPeak: '38%', watermarkLag: '38ms', currentWatermark: '2026-08-21 14:32:45.882 UTC', description: 'Evaluates stateful complex event patterns (CEP) and executes low-latency XGBoost model scoring via embedded ONNX native runtime.', config: { 'model.runtime': 'ONNX Runtime 1.18 (AVX-512)', 'model.artifact': 's3://ml-models-registry/fraud/xgboost_v4_18.onnx', 'inference.latency.p99': '1.2ms', 'rocksdb.block.cache.size': '512 MB', 'rocksdb.compaction.style': 'LEVEL', 'alert.confidence.threshold': '0.85', }, subtasks: [ { id: 0, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '2,390 rec/s', outRate: '2,390 rec/s', cpu: '38%', heapMemory: '89 MB', backpressure: 0.0, watermarkLag: '37ms', checkpointAckMs: 110, }, { id: 1, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '2,385 rec/s', outRate: '2,385 rec/s', cpu: '37%', heapMemory: '88 MB', backpressure: 0.0, watermarkLag: '38ms', checkpointAckMs: 114, }, { id: 2, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '2,392 rec/s', outRate: '2,392 rec/s', cpu: '39%', heapMemory: '90 MB', backpressure: 0.0, watermarkLag: '36ms', checkpointAckMs: 108, }, { id: 3, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '2,380 rec/s', outRate: '2,380 rec/s', cpu: '38%', heapMemory: '87 MB', backpressure: 0.0, watermarkLag: '39ms', checkpointAckMs: 112, }, { id: 4, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '2,388 rec/s', outRate: '2,388 rec/s', cpu: '37%', heapMemory: '88 MB', backpressure: 0.0, watermarkLag: '38ms', checkpointAckMs: 111, }, { id: 5, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '2,386 rec/s', outRate: '2,386 rec/s', cpu: '39%', heapMemory: '90 MB', backpressure: 0.0, watermarkLag: '37ms', checkpointAckMs: 115, }, { id: 6, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '2,390 rec/s', outRate: '2,390 rec/s', cpu: '38%', heapMemory: '89 MB', backpressure: 0.0, watermarkLag: '39ms', checkpointAckMs: 110, }, { id: 7, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '2,384 rec/s', outRate: '2,384 rec/s', cpu: '37%', heapMemory: '87 MB', backpressure: 0.0, watermarkLag: '38ms', checkpointAckMs: 112, }, { id: 8, host: 'taskmanager-pod-4.k8s', status: 'RUNNING', inRate: '2,389 rec/s', outRate: '2,389 rec/s', cpu: '38%', heapMemory: '89 MB', backpressure: 0.0, watermarkLag: '37ms', checkpointAckMs: 109, }, { id: 9, host: 'taskmanager-pod-4.k8s', status: 'RUNNING', inRate: '2,385 rec/s', outRate: '2,385 rec/s', cpu: '37%', heapMemory: '88 MB', backpressure: 0.0, watermarkLag: '39ms', checkpointAckMs: 113, }, { id: 10, host: 'taskmanager-pod-5.k8s', status: 'RUNNING', inRate: '2,391 rec/s', outRate: '2,391 rec/s', cpu: '39%', heapMemory: '90 MB', backpressure: 0.0, watermarkLag: '37ms', checkpointAckMs: 108, }, { id: 11, host: 'taskmanager-pod-5.k8s', status: 'RUNNING', inRate: '2,380 rec/s', outRate: '2,380 rec/s', cpu: '38%', heapMemory: '87 MB', backpressure: 0.0, watermarkLag: '38ms', checkpointAckMs: 112, }, { id: 12, host: 'taskmanager-pod-6.k8s', status: 'RUNNING', inRate: '2,387 rec/s', outRate: '2,387 rec/s', cpu: '37%', heapMemory: '88 MB', backpressure: 0.0, watermarkLag: '36ms', checkpointAckMs: 111, }, { id: 13, host: 'taskmanager-pod-6.k8s', status: 'RUNNING', inRate: '2,383 rec/s', outRate: '2,383 rec/s', cpu: '39%', heapMemory: '90 MB', backpressure: 0.0, watermarkLag: '38ms', checkpointAckMs: 114, }, { id: 14, host: 'taskmanager-pod-7.k8s', status: 'RUNNING', inRate: '2,388 rec/s', outRate: '2,388 rec/s', cpu: '38%', heapMemory: '89 MB', backpressure: 0.0, watermarkLag: '39ms', checkpointAckMs: 110, }, { id: 15, host: 'taskmanager-pod-7.k8s', status: 'RUNNING', inRate: '2,382 rec/s', outRate: '2,382 rec/s', cpu: '38%', heapMemory: '88 MB', backpressure: 0.0, watermarkLag: '37ms', checkpointAckMs: 112, }, ], }, { id: 'op-sink-dispatcher', name: 'Sink: Alert Dispatcher & Redis Cache', shortTitle: 'Sink: Alert Dispatcher & Redis Cache', stageNumber: 4, category: 'sink', operatorClass: 'com.uipkge.fraud.sink.TwoPhaseCommitRedisAlertSink', parallelism: 8, status: 'RUNNING', backpressure: 0.0, inputRecordsRate: '38,200 rec/s', inputBytesRate: '7.1 MB/s', outputRecordsRate: '42,500 rec/s', outputBytesRate: '9.2 MB/s', totalRecordsIn: 165861369, totalRecordsOut: 165861369, managedStateSize: '0.4 MB (2PC WAL)', stateBackend: 'Transactional Two-Phase Commit', heapMemory: '380 MB', managedMemory: '128 MB', gcTime: '0.9 ms/min', cpuPeak: '16%', watermarkLag: '42ms', currentWatermark: '2026-08-21 14:32:45.878 UTC', description: 'Commits risk scoring states to Redis Cluster for zero-latency checkout authorization gates and pushes flagged transactions to incident queues.', config: { 'sink.semantics': 'EXACTLY_ONCE (2PC Transactional)', 'redis.cluster.nodes': 'redis-cluster.internal:6379 (6 nodes)', 'redis.ttl': '86400s (24-hour key expiration)', 'webhook.alert.queue': 'https://alerts.security.internal/v1/fraud', 'batch.flush.interval.ms': '50', }, subtasks: [ { id: 0, host: 'taskmanager-pod-0.k8s', status: 'RUNNING', inRate: '4,775 rec/s', outRate: '5,312 rec/s', cpu: '16%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '41ms', checkpointAckMs: 52, }, { id: 1, host: 'taskmanager-pod-1.k8s', status: 'RUNNING', inRate: '4,770 rec/s', outRate: '5,308 rec/s', cpu: '15%', heapMemory: '47 MB', backpressure: 0.0, watermarkLag: '43ms', checkpointAckMs: 55, }, { id: 2, host: 'taskmanager-pod-2.k8s', status: 'RUNNING', inRate: '4,780 rec/s', outRate: '5,315 rec/s', cpu: '17%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '40ms', checkpointAckMs: 51, }, { id: 3, host: 'taskmanager-pod-3.k8s', status: 'RUNNING', inRate: '4,765 rec/s', outRate: '5,305 rec/s', cpu: '16%', heapMemory: '46 MB', backpressure: 0.0, watermarkLag: '44ms', checkpointAckMs: 56, }, { id: 4, host: 'taskmanager-pod-4.k8s', status: 'RUNNING', inRate: '4,778 rec/s', outRate: '5,314 rec/s', cpu: '16%', heapMemory: '48 MB', backpressure: 0.0, watermarkLag: '42ms', checkpointAckMs: 53, }, { id: 5, host: 'taskmanager-pod-5.k8s', status: 'RUNNING', inRate: '4,772 rec/s', outRate: '5,310 rec/s', cpu: '15%', heapMemory: '47 MB', backpressure: 0.0, watermarkLag: '43ms', checkpointAckMs: 54, }, { id: 6, host: 'taskmanager-pod-6.k8s', status: 'RUNNING', inRate: '4,782 rec/s', outRate: '5,318 rec/s', cpu: '17%', heapMemory: '49 MB', backpressure: 0.0, watermarkLag: '41ms', checkpointAckMs: 50, }, { id: 7, host: 'taskmanager-pod-7.k8s', status: 'RUNNING', inRate: '4,768 rec/s', outRate: '5,308 rec/s', cpu: '16%', heapMemory: '47 MB', backpressure: 0.0, watermarkLag: '43ms', checkpointAckMs: 55, }, ], }, ]src/app/components/blocks/stream-processing-flink-topology/index.ts0.4 kB
export { UiStreamProcessingFlinkTopologyComponent } from './stream-processing-flink-topology.component' export { UiStreamOperatorDetailComponent, type InspectorTab } from './stream-operator-detail.component' export { UiStreamTopologyTelemetryComponent } from './stream-topology-telemetry.component' export type { FlinkOperatorNode, FlinkSubtask } from './stream-topology-types' export { defaultFlinkOperators } from './stream-topology-data'
Raw manifest:https://uipkge.dev/r/angular/stream-processing-flink-topology.json