This is an automated email from the ASF dual-hosted git repository.

mjsax pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new c31c9215e13 MINOR: Improve the Kafka Streams state listener JavaDocs 
(#23011)
c31c9215e13 is described below

commit c31c9215e131f8c17e79f8901b48c13ee6aa8e7a
Author: Matthias J. Sax <[email protected]>
AuthorDate: Sun Aug 2 20:06:48 2026 -0700

    MINOR: Improve the Kafka Streams state listener JavaDocs (#23011)
    
    Updates outdate and incorrect JavaDocs, and aligns the information
    provided across both listeners.
    
    Reviewers: Andrew Schofield <[email protected]>
---
 .../streams/processor/StandbyUpdateListener.java   | 164 +++++++++++++++------
 .../streams/processor/StateRestoreListener.java    | 148 ++++++++++++-------
 2 files changed, 208 insertions(+), 104 deletions(-)

diff --git 
a/streams/src/main/java/org/apache/kafka/streams/processor/StandbyUpdateListener.java
 
b/streams/src/main/java/org/apache/kafka/streams/processor/StandbyUpdateListener.java
index 882747b47dc..0cc38136d8e 100644
--- 
a/streams/src/main/java/org/apache/kafka/streams/processor/StandbyUpdateListener.java
+++ 
b/streams/src/main/java/org/apache/kafka/streams/processor/StandbyUpdateListener.java
@@ -20,69 +20,139 @@ package org.apache.kafka.streams.processor;
 import org.apache.kafka.common.TopicPartition;
 import org.apache.kafka.common.annotation.InterfaceAudience;
 
+/**
+ * Class for listening to the progress of the standby and warm-up tasks of a 
Kafka Streams client.
+ *
+ * <p>When calling {@link 
org.apache.kafka.streams.KafkaStreams#setStandbyUpdateListener(StandbyUpdateListener)}
+ * the passed instance is expected to be stateless since the {@code 
StandbyUpdateListener} is shared across all state
+ * updater threads, of which there is one per stream thread ({@code 
num.stream.threads}).
+ *
+ * <p>Users desiring stateful operations will need to provide synchronization 
internally in
+ * the {@code StandbyUpdateListener} implementation.
+ *
+ * <p>Note that this listener is only registered at the per-client level and 
users can use the {@code storeName}
+ * parameter to define specific monitoring for different {@link StateStore 
StateStores}.
+ *
+ * <p>Warm-up tasks are reported through this interface as well as standby 
tasks.
+ * A warm-up task is one that the group hands out so that this client can 
build a task's state up before taking the task
+ * over, and the runtime runs it as a standby task, so the two are 
indistinguishable to this listener.
+ * Relabelling a task between the two roles does not restart the task, and 
does not trigger a callback; a task might be
+ * converted from a warm-up task to a standby task between two calls of {@link 
#onBatchLoaded} transparently.
+ *
+ * <p>Also note that, unlike the restoration of an active task, the update of 
a standby task never finishes, since it
+ * keeps applying the changelog records written by the active task.
+ * There is therefore no callback corresponding to
+ * {@link StateRestoreListener#onRestoreEnd(TopicPartition, String, long)}; a 
standby task is reported only once it
+ * stops being updated, through {@link #onUpdateSuspended(TopicPartition, 
String, long, long, SuspendReason)}.
+ *
+ * <p>Even if warm-up tasks are expected to "complete" restoration, the same 
pattern as for standby tasks applies.
+ * Once a warm-up task is considered hot (based on {@code 
acceptable.recovery.lag}), its restoration will be
+ * suspended when it is promoted to an active task, and the active task will 
complete the restoration (if
+ * registered, the corresponding {@link StateRestoreListener} callback is 
invoked on the active task for this
+ * case).
+ *
+ * <p>Incremental updates are exposed so users can estimate how much progress 
has been made.
+ *
+ * @see StateRestoreListener
+ */
 @InterfaceAudience.Public
 public interface StandbyUpdateListener {
 
+    /**
+     * The reason why a standby or warm-up task stopped being updated.
+     */
     enum SuspendReason {
+        /**
+         * The task stopped being updated as a standby or warm-up task, and 
its state store was closed.
+         * This covers every reason other than a promotion: the task was 
revoked, or it was assigned to another instance,
+         * or this instance is shutting down.
+         */
         MIGRATED,
+
+        /**
+         * The task stopped being updated as a standby or warm-up task because 
this instance is taking it over as an
+         * active task.
+         * Its state store stays open and keeps its contents, so the active 
task resumes restoring where the update left
+         * off rather than from the beginning of the changelog.
+         * Restoring as an active task is reported through {@link 
StateRestoreListener}.
+         */
         PROMOTED
     }
 
     /**
-     * A callback that will be invoked after registering the changelogs for 
each state store in a standby
-     * task. It is guaranteed to always be invoked before any records are 
loaded into the standby store.
+     * Method called at the very beginning of {@link StateStore} updating.
      *
-     * @param topicPartition the changelog TopicPartition for this standby task
-     * @param storeName the name of the store being loaded
-     * @param startingOffset the offset from which the standby task begins 
consuming from the changelog
+     * @param topicPartition
+     *        the changelog {@link TopicPartition} for this task
+     * @param storeName
+     *        the name of the store being loaded
+     * @param startingOffset
+     *        the offset from which the task begins consuming from the 
changelog
      */
-    void onUpdateStart(final TopicPartition topicPartition,
-                       final String storeName,
-                       final long startingOffset);
+    void onUpdateStart(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long startingOffset
+    );
 
     /**
-     * Method called after loading a batch of records. In this case the 
maximum size of the batch is whatever
-     * the value of the MAX_POLL_RECORDS is set to.
-     * <n>
-     * This method is called after loading each batch and it is advised to 
keep processing to a minimum.
-     * Any heavy processing will block the state updater thread and slow down 
the rate of standby task 
-     * loading. Therefore, if you need to do any extended processing or 
connect to an external service,
-     * consider doing so asynchronously.
+     * Method called after loading a batch of records.
+     * In this case the maximum size of the batch is whatever the value of 
{@code max.poll.records} is set to.
+     *
+     * <p>This method is called after loading each batch and it is advised to 
keep processing to a minimum.
+     * Any heavy processing will block the state updater thread and slow down 
the rate of standby task loading.
+     * Therefore, if you need to do any extended processing or connect to an 
external service, consider doing so
+     * asynchronously.
      *
-     * @param topicPartition the changelog TopicPartition for this standby task
-     * @param storeName the name of the store being loaded
-     * @param batchEndOffset batchEndOffset the changelog end offset 
(inclusive) of the batch that was just loaded
-     * @param batchSize the total number of records in the batch that was just 
loaded
-     * @param currentEndOffset the current end offset of the changelog topic 
partition.
+     * @param topicPartition
+     *        the changelog {@link TopicPartition} for this task
+     * @param storeName
+     *        the name of the store being loaded
+     * @param taskId
+     *        the {@link TaskId} of the task that owns the store being loaded
+     * @param batchEndOffset
+     *        the changelog end offset (inclusive) of the batch that was just 
loaded
+     * @param batchSize
+     *        the total number of records in the batch that was just loaded
+     * @param currentEndOffset
+     *        the current end offset of the changelog topic partition
      */
-    void onBatchLoaded(final TopicPartition topicPartition,
-                       final String storeName,
-                       final TaskId taskId,
-                       final long batchEndOffset,
-                       final long batchSize,
-                       final long currentEndOffset);
+    void onBatchLoaded(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final TaskId taskId,
+        final long batchEndOffset,
+        final long batchSize,
+        final long currentEndOffset
+    );
 
     /**
-     * This method is called when the corresponding standby task stops 
updating, for the provided reason.
-     * <p>
-     * If the task was {@code MIGRATED} to another instance, this callback 
will be invoked after this
-     * state store (and the task itself) are closed (in which case the data 
will be cleaned up after 
-     * state.cleanup.delay.ms).
-     * If the task was {@code PROMOTED} to an active task, the state store 
will not be closed, and the 
-     * callback will be invoked after unregistering it as a standby task but 
before re-registering it as an active task 
-     * and beginning restoration. In other words, this will always called 
before the corresponding 
-     * {@link StateRestoreListener#onRestoreStart} call is made.
+     * Method called when the corresponding standby or warm-up task stops 
being updated, for the provided reason.
+     *
+     * <p>If the reason is {@link SuspendReason#MIGRATED}, this callback will 
be invoked after this state store (and the
+     * task itself) are closed, in which case the data will be cleaned up 
after {@code state.cleanup.delay.ms}.
+     * If the task was {@link SuspendReason#PROMOTED} to an active task, the 
state store will not be closed, and the
+     * callback will be invoked after unregistering it as a standby task but 
before re-registering it as an active task
+     * and beginning restoration.
+     * In other words, this will always be called before the corresponding
+     * {@link StateRestoreListener#onRestoreStart(TopicPartition, String, 
long, long)} call is made.
      *
-     * @param topicPartition the changelog TopicPartition for this standby task
-     * @param storeName the name of the store being loaded
-     * @param storeOffset is the offset of the last changelog record that was 
read and put into the store at the time
-     * of suspension.
-     * @param currentEndOffset the current end offset of the changelog topic 
partition.
-     * @param reason is the reason why the standby task was suspended.
+     * @param topicPartition
+     *        the changelog {@link TopicPartition} for this task
+     * @param storeName
+     *        the name of the store being loaded
+     * @param storeOffset
+     *        the offset of the last changelog record that was read and put 
into the store at the time of suspension
+     * @param currentEndOffset
+     *        the current end offset of the changelog topic partition
+     * @param reason
+     *        the reason why the task stopped being updated
      */
-    void onUpdateSuspended(final TopicPartition topicPartition,
-                           final String storeName,
-                           final long storeOffset,
-                           final long currentEndOffset,
-                           final SuspendReason reason);
-}
\ No newline at end of file
+    void onUpdateSuspended(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long storeOffset,
+        final long currentEndOffset,
+        final SuspendReason reason
+    );
+}
diff --git 
a/streams/src/main/java/org/apache/kafka/streams/processor/StateRestoreListener.java
 
b/streams/src/main/java/org/apache/kafka/streams/processor/StateRestoreListener.java
index adc0adc34e4..391d9631cbf 100644
--- 
a/streams/src/main/java/org/apache/kafka/streams/processor/StateRestoreListener.java
+++ 
b/streams/src/main/java/org/apache/kafka/streams/processor/StateRestoreListener.java
@@ -23,31 +23,30 @@ import org.apache.kafka.common.annotation.InterfaceAudience;
 /**
  * Class for listening to various states of the restoration process of a 
StateStore.
  *
- * <p>
- * When calling {@link 
org.apache.kafka.streams.KafkaStreams#setGlobalStateRestoreListener(StateRestoreListener)}
- * the passed instance is expected to be stateless since the {@code 
StateRestoreListener} is shared
- * across all {@link 
org.apache.kafka.streams.processor.internals.StreamThread} instances.
+ * <p>When calling {@link 
org.apache.kafka.streams.KafkaStreams#setGlobalStateRestoreListener(StateRestoreListener)}
+ * the passed instance is expected to be stateless since the {@code 
StateRestoreListener} is shared across all state
+ * updater threads, of which there is one per stream thread ({@code 
num.stream.threads}).
  *
- * <p>
- * Users desiring stateful operations will need to provide synchronization 
internally in
- * the {@code StateRestorerListener} implementation.
+ * <p>Users desiring stateful operations will need to provide synchronization 
internally in
+ * the {@code StateRestoreListener} implementation.
  *
- * <p>
- * Note that this listener is only registered at the per-client level and 
users can base on the {@code storeName}
- * parameter to define specific monitoring for different {@link StateStore}s. 
There is another
- * {@link StateRestoreCallback} interface which is registered via the
- * {@link StateStoreContext#register(StateStore, StateRestoreCallback, 
CommitCallback)}
- * function per-store, and it is used to apply the fetched changelog records 
into the local state store during restoration.
- * These two interfaces serve different restoration purposes and users should 
not try to implement both of them in a single
- * class during state store registration.
+ * <p>Note that this listener is only registered at the per-client level and 
users can use the {@code storeName}
+ * parameter to define specific monitoring for different {@link StateStore 
StateStores}.
  *
- * <p>
- * Also note that the update process of standby tasks is not monitored via 
this interface, since a standby task does
- * note actually <it>restore</it> state, but keeps updating its state from the 
changelogs written by the active task
+ * <p>{@code StateRestoreListener} must not be confused with the {@link 
StateRestoreCallback} interface.
+ * The {@code StateRestoreListener} is a callback to observe restoration 
progress.
+ * In contrast, the {@link StateRestoreCallback} interface is used by custom 
state store implementations to provide
+ * the actual state restoration functionality.
+ *
+ * <p>Also note that the update process of standby tasks is not monitored via 
this interface, since a standby task does
+ * not actually <em>restore</em> state, but keeps updating its state from the 
changelogs written by the active task
  * which does not ever finish.
+ * The same holds for warm-up tasks, which the runtime runs as standby tasks.
+ * Both are monitored via {@link StandbyUpdateListener} instead.
+ *
+ * <p>Incremental updates are exposed so users can estimate how much progress 
has been made.
  *
- * <p>
- * Incremental updates are exposed so users can estimate how much progress has 
been made.
+ * @see StandbyUpdateListener
  */
 @InterfaceAudience.Public
 public interface StateRestoreListener {
@@ -55,58 +54,93 @@ public interface StateRestoreListener {
     /**
      * Method called at the very beginning of {@link StateStore} restoration.
      *
-     * @param topicPartition the TopicPartition containing the values to 
restore
-     * @param storeName      the name of the store undergoing restoration
-     * @param startingOffset the starting offset of the entire restoration 
process for this TopicPartition
-     * @param endingOffset   the exclusive ending offset of the entire 
restoration process for this TopicPartition
+     * <p>Called for every active task that begins restoring, including one 
that has nothing to restore because its
+     * local state is already up to date -- a task whose state was just built 
up as a standby or warm-up task, for
+     * instance.
+     * In that case {@code startingOffset} equals {@code endingOffset} and
+     * {@link #onRestoreEnd(TopicPartition, String, long)} follows with a 
total of zero records restored, so an empty
+     * restoration is told apart by the offsets and the count rather than by 
the callbacks being skipped.
+     *
+     * @param topicPartition
+     *        the {@link TopicPartition} containing the values to restore
+     * @param storeName
+     *        the name of the store undergoing restoration
+     * @param startingOffset
+     *        the starting offset of the entire restoration process for this 
{@link TopicPartition}
+     * @param endingOffset
+     *        the exclusive ending offset of the entire restoration process 
for this {@link TopicPartition}
      */
-    void onRestoreStart(final TopicPartition topicPartition,
-                        final String storeName,
-                        final long startingOffset,
-                        final long endingOffset);
+    void onRestoreStart(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long startingOffset,
+        final long endingOffset
+    );
 
     /**
-     * Method called after restoring a batch of records.  In this case the 
maximum size of the batch is whatever
-     * the value of the MAX_POLL_RECORDS is set to.
-     *
-     * This method is called after restoring each batch and it is advised to 
keep processing to a minimum.
-     * Any heavy processing will hold up recovering the next batch, hence 
slowing down the restore process as a
-     * whole.
+     * Method called after restoring a batch of records.
+     * In this case the maximum size of the batch is whatever the value of 
{@code max.poll.records} is set to.
      *
+     * <p>This method is called after restoring each batch and it is advised 
to keep processing to a minimum.
+     * Any heavy processing will block the state updater thread and hold up 
recovering the next batch, hence slowing
+     * down the restore process as a whole.
      * If you need to do any extended processing or connecting to an external 
service consider doing so asynchronously.
      *
-     * @param topicPartition the TopicPartition containing the values to 
restore
-     * @param storeName the name of the store undergoing restoration
-     * @param batchEndOffset the inclusive ending offset for the current 
restored batch for this TopicPartition
-     * @param numRestored the total number of records restored in this batch 
for this TopicPartition
+     * @param topicPartition
+     *        the {@link TopicPartition} containing the values to restore
+     * @param storeName
+     *        the name of the store undergoing restoration
+     * @param batchEndOffset
+     *        the inclusive ending offset for the current restored batch for 
this {@link TopicPartition}
+     * @param numRestored
+     *        the total number of records restored in this batch for this 
{@link TopicPartition}
      */
-    void onBatchRestored(final TopicPartition topicPartition,
-                         final String storeName,
-                         final long batchEndOffset,
-                         final long numRestored);
+    void onBatchRestored(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long batchEndOffset,
+        final long numRestored
+    );
 
     /**
      * Method called when restoring the {@link StateStore} is complete.
      *
-     * @param topicPartition the TopicPartition containing the values to 
restore
-     * @param storeName the name of the store just restored
-     * @param totalRestored the total number of records restored for this 
TopicPartition
+     * @param topicPartition
+     *        the {@link TopicPartition} containing the values to restore
+     * @param storeName
+     *        the name of the store just restored
+     * @param totalRestored
+     *        the total number of records restored for this {@link 
TopicPartition}
      */
-    void onRestoreEnd(final TopicPartition topicPartition,
-                      final String storeName,
-                      final long totalRestored);
+    void onRestoreEnd(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long totalRestored
+    );
 
     /**
-     * Method called when restoring the {@link StateStore} is suspended due to 
the task being migrated out of the host.
-     * If the migrated task is recycled or re-assigned back to the current 
host, another
-     * {@link #onRestoreStart(TopicPartition, String, long, long)} would be 
called.
+     * Method called when restoring the {@link StateStore} is suspended before 
it finished, because the task stopped
+     * being an active task on this host: it was assigned elsewhere, demoted 
to a standby task, or this host is shutting
+     * down (the concrete reason is not reported).
+     * If the task becomes an active task on this host again -- assigned back 
to it, or promoted from the standby task
+     * it was demoted to -- another {@link #onRestoreStart(TopicPartition, 
String, long, long)} would be called.
+     *
+     * <p>A task whose restoration had already finished, and was reported 
through
+     * {@link #onRestoreEnd(TopicPartition, String, long)}, is not reported 
here when it is closed later on.
+     * In particular, an active task that had finished restoring and 
transitioned into the {@code RUNNING} state and
+     * is then demoted to a standby task is not reported here at all; it 
surfaces only as a
+     * {@link StandbyUpdateListener#onUpdateStart(TopicPartition, String, 
long)} for the standby task that replaces it.
      *
-     * @param topicPartition the {@link TopicPartition} containing the values 
to restore
-     * @param storeName the name of the store just restored
-     * @param totalRestored the total number of records restored for this 
TopicPartition before being paused
+     * @param topicPartition
+     *        the {@link TopicPartition} containing the values to restore
+     * @param storeName
+     *        the name of the store just restored
+     * @param totalRestored
+     *        the total number of records restored for this {@link 
TopicPartition} before being suspended
      */
-    default void onRestoreSuspended(final TopicPartition topicPartition,
-                                    final String storeName,
-                                    final long totalRestored) {
+    default void onRestoreSuspended(
+        final TopicPartition topicPartition,
+        final String storeName,
+        final long totalRestored) {
     }
 }

Reply via email to