Skip to content

Commit db0c4ce

Browse files
author
jennychen
committed
[AURON #2189] Periodically publish native metrics for long-running Flink tasks
1 parent 3d52de0 commit db0c4ce

9 files changed

Lines changed: 355 additions & 73 deletions

File tree

auron-core/src/main/java/org/apache/auron/configuration/AuronConfiguration.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ public abstract class AuronConfiguration {
6565
+ "if not configured, the default value of 1 is used.")
6666
.withDefaultValue(1);
6767

68+
/**
69+
* How often the native runtime publishes DataFusion metrics to {@code MetricNode} while a task
70+
* is running. Java {@code MetricNode.add} is incremental, so the native side sends positive
71+
* deltas. {@code 0} disables the timer and publishes only when the native runtime finalizes
72+
* (the historical batch behavior).
73+
*/
74+
public static final ConfigOption<Long> METRICS_UPDATE_INTERVAL_MS = new ConfigOption<>(Long.class)
75+
.withKey("auron.metrics.update.interval.ms")
76+
.withCategory("Runtime Configuration")
77+
.withDescription("Interval in milliseconds for publishing native execution metrics to MetricNode "
78+
+ "during a running task. Set to 0 to publish only when the native runtime finalizes. "
79+
+ "Default is 1000ms so long-running Flink tasks report live counters.")
80+
.withDefaultValue(1000L);
81+
6882
public abstract <T> Optional<T> getOptional(ConfigOption<T> option);
6983

7084
public <T> T get(ConfigOption<T> option) {

auron-core/src/test/java/org/apache/auron/jni/AuronAdaptorTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public void testRetrieveConfigWithAuronAdaptor() {
3636
assertEquals(auronConfig.getInteger(AuronConfiguration.BATCH_SIZE), 10000);
3737
assertEquals(auronConfig.getDouble(AuronConfiguration.MEMORY_FRACTION), 0.6, 0.0);
3838
assertEquals(auronConfig.getString(AuronConfiguration.NATIVE_LOG_LEVEL), "info");
39+
assertEquals(auronConfig.getLong(AuronConfiguration.METRICS_UPDATE_INTERVAL_MS), 1000L);
3940
}
4041

4142
@Test

auron-flink-extension/auron-flink-runtime/src/main/java/org/apache/auron/flink/connector/kafka/AuronKafkaSourceFunction.java

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.auron.flink.arrow.FlinkArrowReader;
2828
import org.apache.auron.flink.arrow.FlinkArrowUtils;
2929
import org.apache.auron.flink.configuration.FlinkAuronConfiguration;
30+
import org.apache.auron.flink.metric.FlinkMetricNode;
3031
import org.apache.auron.flink.runtime.operator.AuronPlanTreeRewriter;
3132
import org.apache.auron.flink.runtime.operator.FlinkAuronFunction;
3233
import org.apache.auron.flink.table.data.AuronColumnarRowData;
@@ -56,7 +57,6 @@
5657
import org.apache.flink.api.common.typeinfo.TypeInformation;
5758
import org.apache.flink.api.java.tuple.Tuple2;
5859
import org.apache.flink.configuration.Configuration;
59-
import org.apache.flink.metrics.Counter;
6060
import org.apache.flink.metrics.MetricGroup;
6161
import org.apache.flink.runtime.state.FunctionInitializationContext;
6262
import org.apache.flink.runtime.state.FunctionSnapshotContext;
@@ -130,7 +130,6 @@ public class AuronKafkaSourceFunction extends RichParallelSourceFunction<RowData
130130
private volatile boolean isRunning;
131131
private transient String auronOperatorIdWithSubtaskIndex;
132132
private transient MetricNode nativeMetric;
133-
private transient MetricGroup metricGroup;
134133
private transient ObjectMapper mapper;
135134

136135
// Kafka Consumer for partition metadata discovery only (does NOT consume data)
@@ -338,22 +337,10 @@ PhysicalPlanNode applyMergedCalcPlan(PhysicalPlanNode sourcePlan) {
338337

339338
@Override
340339
public void run(SourceContext<RowData> sourceContext) throws Exception {
341-
metricGroup = getRuntimeContext().getMetricGroup();
342-
final Map<String, Counter> flinkCounters = new HashMap<>();
343-
344-
nativeMetric = new MetricNode(new ArrayList<>()) {
345-
@Override
346-
public void add(String name, long value) {
347-
// Integration with Flink metrics
348-
Counter counter = flinkCounters.get(name);
349-
if (counter == null) {
350-
counter = metricGroup.counter(name);
351-
flinkCounters.put(name, counter);
352-
}
353-
counter.inc(value);
354-
LOG.debug("Metric Auron Source: {} = {}", name, value);
355-
}
356-
};
340+
// Mirror physicalPlanNode (KafkaScan, or fused Project[Filter?[KafkaScan]]) so native
341+
// periodic metric walks via MetricNode.getChild(i) do not IndexOutOfBounds.
342+
nativeMetric =
343+
FlinkMetricNode.fromPlan(physicalPlanNode, getRuntimeContext().getMetricGroup());
357344
// The native output carries [meta, logical], where logical is the projected output when a
358345
// merged Calc plan is active and the original output otherwise. The metadata column count
359346
// and per-field positions both derive from KAFKA_AURON_META_FIELDS so adding or reordering

auron-flink-extension/auron-flink-runtime/src/main/java/org/apache/auron/flink/metric/FlinkMetricNode.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
*/
1717
package org.apache.auron.flink.metric;
1818

19+
import java.util.Collections;
1920
import java.util.List;
2021
import java.util.Objects;
2122
import java.util.concurrent.ConcurrentHashMap;
2223
import org.apache.auron.metric.MetricNode;
24+
import org.apache.auron.protobuf.PhysicalPlanNode;
2325
import org.apache.flink.metrics.Counter;
2426
import org.apache.flink.metrics.MetricGroup;
2527

@@ -54,6 +56,40 @@ public FlinkMetricNode(MetricGroup metricGroup, List<MetricNode> children) {
5456
this.metricGroup = Objects.requireNonNull(metricGroup, "metricGroup");
5557
}
5658

59+
/**
60+
* Recursively constructs a {@link FlinkMetricNode} whose shape mirrors {@code node}'s plan
61+
* tree. Native code walks the execution plan during periodic metric updates and at
62+
* finalization, indexing into the parallel metric tree via {@link MetricNode#getChild(int)};
63+
* an empty children list would throw {@link IndexOutOfBoundsException} on the first {@code
64+
* getChild(0)} call. All levels share the same Flink {@link MetricGroup} so named counters
65+
* aggregate at the operator scope.
66+
*
67+
* <p>Supported leaves are {@code FFIReader} (standalone Calc) and {@code KafkaScan} (Kafka
68+
* source, including after Calc fusion). Supported unary nodes are {@code Projection} and
69+
* {@code Filter}.
70+
*
71+
* @param node the physical plan whose shape the metric tree must match; must not be null
72+
* @param metricGroup the Flink metric group all tree levels register counters against; must
73+
* not be null
74+
* @return the root of the mirrored metric tree
75+
*/
76+
public static FlinkMetricNode fromPlan(PhysicalPlanNode node, MetricGroup metricGroup) {
77+
Objects.requireNonNull(node, "node");
78+
Objects.requireNonNull(metricGroup, "metricGroup");
79+
final List<MetricNode> children;
80+
if (node.hasFfiReader() || node.hasKafkaScan()) {
81+
children = Collections.emptyList();
82+
} else if (node.hasProjection()) {
83+
children = Collections.singletonList(fromPlan(node.getProjection().getInput(), metricGroup));
84+
} else if (node.hasFilter()) {
85+
children = Collections.singletonList(fromPlan(node.getFilter().getInput(), metricGroup));
86+
} else {
87+
throw new IllegalArgumentException(
88+
"Unexpected plan node type for metric tree: " + node.getPhysicalPlanTypeCase());
89+
}
90+
return new FlinkMetricNode(metricGroup, children);
91+
}
92+
5793
/**
5894
* Adds {@code value} to the Flink {@link Counter} named {@code name}, creating the counter on
5995
* first use. Non-positive values are ignored.

auron-flink-extension/auron-flink-runtime/src/main/java/org/apache/auron/flink/runtime/operator/FlinkAuronCalcOperator.java

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public void open() throws Exception {
153153
String opIdWithSubtask = rc.getOperatorUniqueID() + "-" + rc.getIndexOfThisSubtask();
154154

155155
this.childAllocator = FlinkArrowUtils.createChildAllocator("FlinkAuronCalc-" + opIdWithSubtask);
156-
this.metricNode = buildMetricTree(plan, getMetricGroup());
156+
this.metricNode = FlinkMetricNode.fromPlan(plan, getMetricGroup());
157157
this.exporter = new FlinkArrowFFIExporter(childAllocator, inputRowType, BATCH_ROW_LIMIT);
158158
// UUID disambiguates re-runs after operator restart so a stale registration cannot
159159
// collide with the new one.
@@ -318,31 +318,6 @@ static PhysicalPlanNode injectFfiReaderLeaf(PhysicalPlanNode node, String resour
318318
+ "Project[FFIReader] / Filter[FFIReader] / FFIReader shape; got: ");
319319
}
320320

321-
/**
322-
* Recursively constructs a {@link FlinkMetricNode} whose shape mirrors {@code node}'s plan
323-
* tree. Native code walks the plan tree at finalization time and indexes into the parallel
324-
* metric tree via {@link org.apache.auron.metric.MetricNode#getChild(int)}; an empty children
325-
* list would throw {@link IndexOutOfBoundsException} on the first {@code getChild(0)} call.
326-
* All levels share the same Flink {@link org.apache.flink.metrics.MetricGroup} so named
327-
* counters aggregate at the operator scope.
328-
*/
329-
private static FlinkMetricNode buildMetricTree(PhysicalPlanNode node, org.apache.flink.metrics.MetricGroup mg) {
330-
final List<org.apache.auron.metric.MetricNode> children;
331-
if (node.hasFfiReader()) {
332-
children = Collections.emptyList();
333-
} else if (node.hasProjection()) {
334-
children = Collections.singletonList(
335-
buildMetricTree(node.getProjection().getInput(), mg));
336-
} else if (node.hasFilter()) {
337-
children =
338-
Collections.singletonList(buildMetricTree(node.getFilter().getInput(), mg));
339-
} else {
340-
throw new IllegalArgumentException(
341-
"Unexpected plan node type for metric tree: " + node.getPhysicalPlanTypeCase());
342-
}
343-
return new FlinkMetricNode(mg, children);
344-
}
345-
346321
// ====================================================================
347322
// SupportsAuronNative
348323
// ====================================================================

auron-flink-extension/auron-flink-runtime/src/test/java/org/apache/auron/flink/connector/kafka/AuronKafkaSourceFunctionMergeTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
2020
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
2122
import static org.junit.jupiter.api.Assertions.assertThrows;
2223
import static org.junit.jupiter.api.Assertions.assertTrue;
2324

2425
import java.util.Properties;
26+
import org.apache.auron.flink.metric.FlinkMetricNode;
2527
import org.apache.auron.flink.utils.SchemaConverters;
28+
import org.apache.auron.metric.MetricNode;
2629
import org.apache.auron.protobuf.FFIReaderExecNode;
2730
import org.apache.auron.protobuf.FilterExecNode;
2831
import org.apache.auron.protobuf.KafkaScanExecNode;
@@ -31,6 +34,7 @@
3134
import org.apache.auron.protobuf.PhysicalPlanNode;
3235
import org.apache.auron.protobuf.ProjectionExecNode;
3336
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
37+
import org.apache.flink.metrics.groups.UnregisteredMetricsGroup;
3438
import org.apache.flink.table.data.RowData;
3539
import org.apache.flink.table.types.logical.IntType;
3640
import org.apache.flink.table.types.logical.LogicalType;
@@ -228,4 +232,54 @@ void testApplyMergedCalcPlanReturnsSourcePlanWhenNoPlanStaged() {
228232
fn.setWatermarkStrategy(WatermarkStrategy.<RowData>forMonotonousTimestamps());
229233
assertEquals(source, fn.applyMergedCalcPlan(source));
230234
}
235+
236+
@Test
237+
void testMetricTreeForKafkaScanLeafHasNoChildren() {
238+
FlinkMetricNode root = FlinkMetricNode.fromPlan(kafkaScan(), new UnregisteredMetricsGroup());
239+
assertLeaf(root);
240+
}
241+
242+
@Test
243+
void testMetricTreeForFusedProjectKafkaScan() {
244+
PhysicalPlanNode fused =
245+
AuronKafkaSourceFunction.buildMergedPlan(logicalProjection(ffiReaderPlaceholder()), kafkaScan());
246+
247+
FlinkMetricNode root = FlinkMetricNode.fromPlan(fused, new UnregisteredMetricsGroup());
248+
MetricNode scan = root.getChild(0);
249+
assertLeaf(scan);
250+
assertThrows(IndexOutOfBoundsException.class, () -> root.getChild(1));
251+
}
252+
253+
@Test
254+
void testMetricTreeForFusedProjectFilterKafkaScan() {
255+
PhysicalPlanNode filter = PhysicalPlanNode.newBuilder()
256+
.setFilter(FilterExecNode.newBuilder()
257+
.setInput(ffiReaderPlaceholder())
258+
.build())
259+
.build();
260+
PhysicalPlanNode fused = AuronKafkaSourceFunction.buildMergedPlan(logicalProjection(filter), kafkaScan());
261+
262+
FlinkMetricNode root = FlinkMetricNode.fromPlan(fused, new UnregisteredMetricsGroup());
263+
MetricNode filterNode = root.getChild(0);
264+
MetricNode scan = filterNode.getChild(0);
265+
assertLeaf(scan);
266+
assertThrows(IndexOutOfBoundsException.class, () -> root.getChild(1));
267+
assertThrows(IndexOutOfBoundsException.class, () -> filterNode.getChild(1));
268+
}
269+
270+
@Test
271+
void testMetricTreeForApplyMergedCalcPlanOutput() {
272+
AuronKafkaSourceFunction fn = newFunction();
273+
RowType projected = RowType.of(new LogicalType[] {new IntType()}, new String[] {"int"});
274+
fn.setMergedCalcPlan(logicalProjection(ffiReaderPlaceholder()), projected);
275+
276+
PhysicalPlanNode fused = fn.applyMergedCalcPlan(kafkaScan());
277+
FlinkMetricNode root = FlinkMetricNode.fromPlan(fused, new UnregisteredMetricsGroup());
278+
assertLeaf(root.getChild(0));
279+
}
280+
281+
private static void assertLeaf(MetricNode node) {
282+
assertNotNull(node);
283+
assertThrows(IndexOutOfBoundsException.class, () -> node.getChild(0));
284+
}
231285
}

native-engine/auron-jni-bridge/src/conf.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ define_conf!(BooleanConf, ORC_SCHEMA_CASE_SENSITIVE);
6363
define_conf!(IntConf, UDAF_FALLBACK_NUM_UDAFS_TRIGGER_SORT_AGG);
6464
define_conf!(BooleanConf, PARSE_JSON_ERROR_FALLBACK);
6565
define_conf!(StringConf, NATIVE_LOG_LEVEL);
66+
define_conf!(LongConf, METRICS_UPDATE_INTERVAL_MS);
6667

6768
pub trait BooleanConf {
6869
fn key(&self) -> &'static str;

0 commit comments

Comments
 (0)