Skip to main content

beam-dofn-modernizer

Rewrite Apache Beam DoFn methods (@ProcessElement, @OnTimer, @OnWindowExpiration) to remove legacy ProcessContext or OnTimerContext usage. Use this skill when you encounter DoFn methods that use context.element(), context.output(), etc., and need to modernize them using parameter injection (@Element, @Timestamp, @Pane, OutputReceiver, MultiOutputReceiver).

Datos de origen

Repositorio
apache/beam
Última actividad en el origen
23 de abril de 2026 a las 15:03
Idioma detectado de SKILL.md
inglés
Estrellas
8684
Forks
4677

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name: beam-dofn-modernizer description: Rewrite Apache Beam DoFn methods (@ProcessElement, @OnTimer, @OnWindowExpiration) to remove legacy ProcessContext or OnTimerContext usage. Use this skill when you encounter DoFn methods that use context.element(), context.output(), etc., and need to modernize them using parameter injection (@Element, @Timestamp, @Pane, OutputReceiver, MultiOutputReceiver). --- # Modernizing Apache Beam DoFns Apache Beam has moved towards parameter injection in `DoFn` methods to improve readability and allow for more efficient execution. This skill helps you migrate legacy `ProcessContext` and `OnTimerContext` usage to modern annotated parameters. ## Core Mappings When rewriting a `@ProcessElement` or `@OnTimer` method, replace the context argument with the corresponding parameters based on the usage: | Legacy Context Usage (e.g. `ProcessContext c`) | Modern Parameter Replacement | | :--- | :--- | | `c.element()` | `@Element T element` | | `c.timestamp()` | `@Timestamp Instant timestamp` | | `c.pane()` | `PaneInfo pane` | | `c.window()` | `BoundedWindow window` | | `c.sideInput(PCollectionView<T> view)` | `@SideInput("viewName") T value` | | `c.getPipelineOptions()` | `PipelineOptions options` | | `c.output(value)` | `OutputReceiver<T> receiver` then `receiver.output(value)` | | `c.output(tag, value)` | `MultiOutputReceiver receiver` then `receiver.get(tag).output(value)` | | `c.outputWithTimestamp(value, ts)` | `OutputReceiver<T> receiver` then `receiver.outputWithTimestamp(value, ts)` | ## Method Signature Changes ### @ProcessElement **Legacy:** ```java @ProcessElement public void processElement(ProcessContext c) { T element = c.element(); c.output(transform(element)); } ``` **Modern:** ```java @ProcessElement public void processElement( @Element T element, @Timestamp Instant timestamp, OutputReceiver<V> receiver) { receiver.output(transform(element)); } ``` ### @OnTimer **Legacy:** ```java @OnTimer("timerId") public void onTimer(OnTimerContext c) { c.output(someValue); } ``` **Modern:** ```java @OnTimer("timerId") public void onTimer( @Timestamp Instant timestamp, BoundedWindow window, OutputReceiver<V> receiver) { receiver.output(someValue); } ``` ## Best Practices 1. **Specific OutputReceiver**: If the method only outputs to the main output, use `OutputReceiver<T>`. If it outputs to multiple tags, use `MultiOutputReceiver`. 2. **Element Type**: Ensure the `@Element` parameter type matches the input type of the `DoFn`. 3. **Imports**: Don't forget to add imports for: * `org.apache.beam.sdk.transforms.DoFn.Element` * `org.apache.beam.sdk.transforms.DoFn.Timestamp` * `org.apache.beam.sdk.transforms.DoFn.OutputReceiver` * `org.apache.beam.sdk.transforms.DoFn.MultiOutputReceiver` (if needed) * `org.apache.beam.sdk.values.PCollectionView` (if using `@SideInput`) * `org.apache.beam.sdk.transforms.DoFn.SideInput` * `org.apache.beam.sdk.transforms.windowing.PaneInfo` 4. **Side Inputs**: When using `@SideInput`, make sure to use the correct name that matches the one passed to `ParDo.withSideInput("name", view)`. 5. **Parameter Naming and Redundant Variables**: Use descriptive names for the `@Element` parameter (e.g., `record`, `line`, `row`) instead of a generic `element` if it improves readability. Do not create a redundant local variable to copy the element (e.g., `MyType elm = element;`), use the parameter directly. ## Example Conversion ### Before: ```java @ProcessElement public void processElement(ProcessContext c) { KV<String, Integer> element = c.element(); Instant ts = c.timestamp(); if (element.getValue() > threshold) { c.output(element.getKey()); c.output(specialTag, element.getValue()); } } ``` ### After: ```java @ProcessElement public void processElement( @Element KV<String, Integer> element, @Timestamp Instant timestamp, MultiOutputReceiver receiver) { if (element.getValue() > threshold) { receiver.get(mainTag).output(element.getKey()); receiver.get(specialTag).output(element.getValue()); } } ``` > [!NOTE] > If you only have one output, use `OutputReceiver<String> receiver` and `receiver.output(element.getKey())`. ## Side Input Conversion Modernizing side inputs involves removing the `PCollectionView` from the `DoFn` constructor and using `@SideInput` parameter injection instead. ### Before (Legacy): **PTransform/Pipeline side:** ```java PCollectionView<String> myView = ...; input.apply(ParDo.of(new MyFn(myView)).withSideInputs(myView)); ``` **DoFn side:** ```java class MyFn extends DoFn<T, V> { private final PCollectionView<String> view; MyFn(PCollectionView<String> view) { this.view = view; } @ProcessElement public void processElement(ProcessContext c) { String value = c.sideInput(view); // ... } } ``` ### Nullable Side Inputs If a side input is optional and a `DoFn` has conditional logic based on whether the side input is present, it is best to split the `DoFn` into two separate classes: one that requires the side input and one that does not. This avoids creating complex, conditional `DoFn`s and ensures type safety. **PTransform/Pipeline side:** ```java PCollectionView<String> myView = ...; input.apply(ParDo.of(new MyFn(myView)).withSideInputs(myView)); //or input.apply(ParDo.of(new MyFn(null))); // to introduce null ``` **DoFn side:** ```java class MyFn extends DoFn<T, V> { private final PCollectionView<String> view; MyFn(PCollectionView<String> view) { this.view = view; } @ProcessElement public void processElement(ProcessContext c) { String value = null; if (this.view != null) { // can do conditional side input value = c.sideInput(this.view); } // ... } } ``` ### After (Modern): **PTransform/Pipeline side:** ```java PCollectionView<String> myView = ...; input.apply(ParDo.of(new MyFn()).withSideInput("sideInputName", myView)); ``` **DoFn side:** ```java class MyFn extends DoFn<T, V> { @ProcessElement public void processElement( @Element T element, @SideInput("sideInputName") String value) { // value is injected directly } } ```
Ver en GitHub