In this lesson, you will learn how to leverage Keyed Process Functions to maintain granular, long-term historical context for individual entities within a high-velocity stream.
Why Stateless Isn't Enough
Up to this point, you have performed stateful aggregations (like sums or counts) using windowing. However, windowing is ephemeral—it eventually closes and clears its state. Often, you need to track the "state of the world" for an entity (like a specific user or device) that persists indefinitely, regardless of time-window boundaries. Keyed Process Functions allow you to treat a stream not just as a sequence of events, but as a sequence of state transitions for specific keys.
The Mechanism of Keyed State
When you "key" a stream, the system partitions your data so that all events for a specific ID (e.g., user_123) are routed to the same worker. A Keyed Process Function sits on this partition, holding a private "memory" (State) that it updates every time a new event for that key arrives.
For example, imagine you are monitoring a sensor. You want to trigger an alert only if the current temperature is 10 degrees higher than the last recorded temperature.
// Conceptual logic within a Keyed Process Function
val lastTempState = getRuntimeContext.getState(new ValueStateDescriptor[Double]("lastTemp", classOf[Double]))
override def processElement(event: SensorEvent, ctx: Context, out: Collector[Alert]): Unit = {
val lastTemp = lastTempState.value()
if (lastTemp != null && event.temp > (lastTemp + 10)) {
out.collect(Alert(event.id, "Sudden spike detected!"))
}
// Update the state with the current event's data for the next arrival
lastTempState.update(event.temp)
}
Why This Matters for CDC
In Change Data Capture (CDC) pipelines, this is vital. If a database row changes from "Active" to "Inactive," you might need to compare the incoming log entry against the previous known status stored in your keyed state. By maintaining this historical context, you can implement complex business logic—such as identifying state transitions or calculating time-in-state—that stateless transformations simply cannot capture. Because this state is keyed, it scales horizontally across your cluster as the number of entities grows.
Key Takeaway: Keyed Process Functions enable persistent, per-entity memory that survives beyond fixed time windows, allowing your stream processor to make complex decisions based on the full history of an individual record.