wenjin272 commented on code in PR #1342:
URL: https://github.com/apache/flink-agents/pull/1342#discussion_r4452644496


##########
runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateStore.java:
##########
@@ -69,6 +69,30 @@ void put(Object key, long seqNum, Action action, Event 
event, ActionState state)
      */
     ActionState get(Object key, long seqNum, Action action, Event event) 
throws Exception;
 
+    /**
+     * Returns the business-key identity segment this store's key encoder 
would embed in a composite
+     * action-state key for {@code key}. Used by the runtime to derive a 
replay-stable occurrence
+     * identity for root events (FLINK-AGENTS-1084); unrelated to storing or 
retrieving state.
+     *
+     * @param key the key associated with the message
+     */
+    String generateBusinessKeyIdentity(Object key);
+
+    /**
+     * Returns the full composite action-state key this store's key encoder 
would use for {@code
+     * (key, seqNum, action, event)} — the deterministic identity of one 
action execution. Used by
+     * the runtime to derive occurrence identities for that execution's output 
events
+     * (FLINK-AGENTS-1084); unrelated to storing or retrieving state.
+     *
+     * @param key the key associated with the message
+     * @param seqNum the sequence number of the key
+     * @param action the action the agent is taking
+     * @param event the event that triggered the action
+     * @throws IOException when key generation failed
+     */
+    String generateActionExecutionId(Object key, long seqNum, Action action, 
Event event)

Review Comment:
   Could we move identity generation into a shared runtime component instead of 
adding these methods to `ActionStateStore`? Kafka and Fluss only delegate to 
the same encoder, and execution identity should be available independently of 
durable storage.
   
   An action execution ID can be derived as `hash(triggeringEventOccurrenceId, 
actionIdentity)`, with business key and sequence number already represented in 
the root occurrence ID. Content-based divergence checks should remain in place 
separately.



##########
runtime/src/main/java/org/apache/flink/agents/runtime/actionstate/ActionStateUtil.java:
##########
@@ -342,8 +342,62 @@ private static String describeKey(@Nullable String 
stateKey) {
     }
 
     private static String generateUUIDForEvent(Event event) throws IOException 
{
-        return String.valueOf(
-                
UUID.nameUUIDFromBytes(MAPPER.writeValueAsBytes(event.getAttributes())));
+        byte[] attributesBytes = 
MAPPER.writeValueAsBytes(event.getAttributes());
+        String occurrenceId = event.getOccurrenceId();
+        if (occurrenceId == null) {
+            // Legacy fallback for events the runtime has not assigned an 
occurrence identity to
+            // (for example, state written before this fix by an older 
version). Distinct
+            // occurrences with identical attributes still collide here; this 
path exists only for
+            // mixed-version recovery continuity, not as the steady-state 
behavior.
+            return String.valueOf(UUID.nameUUIDFromBytes(attributesBytes));

Review Comment:
   We do not require compatibility with old persisted state for this change, so 
please remove the legacy attributes-only fallback and make missing occurrence 
identity explicit on paths that require it. Keeping this fallback preserves the 
collision behavior the change is intended to eliminate.



##########
runtime/src/main/java/org/apache/flink/agents/runtime/operator/ActionExecutionOperator.java:
##########
@@ -616,6 +617,44 @@ private void processInputEvent(Object key, Event 
inputEvent) throws Exception {
         }
     }
 
+    /**
+     * Assigns a root (input) event's replay-stable occurrence identity, 
derived from its business
+     * key and sequence number. No-op when durable execution is disabled, 
since nothing keys action
+     * state on it in that case. See FLINK-AGENTS-1084.
+     */
+    private void assignRootOccurrenceId(Object key, Event event) throws 
Exception {
+        String businessKeyIdentity = 
durableExecManager.generateBusinessKeyIdentity(key);
+        if (businessKeyIdentity != null) {
+            event.setOccurrenceId(
+                    ActionStateUtil.deriveRootEventOccurrenceId(
+                            businessKeyIdentity, 
stateManager.getSequenceNumber()));
+        }
+    }
+
+    /**
+     * Assigns each of an action execution's output events its lineage-based 
occurrence identity
+     * before dispatch, so that distinct event occurrences with identical 
attributes never collide
+     * in the durable action-state key. No-op when durable execution is 
disabled. See
+     * FLINK-AGENTS-1084.
+     */
+    private void assignOutputOccurrenceIds(
+            Object key, long sequenceNumber, ActionTask actionTask, 
List<Event> outputEvents)
+            throws Exception {
+        String producerExecutionId =
+                durableExecManager.generateActionExecutionId(
+                        key, sequenceNumber, actionTask.action, 
actionTask.event);
+        if (producerExecutionId == null) {
+            return;

Review Comment:
   Please also wire the occurrence identity into chat/internal-subagent call 
IDs, including when durable execution is disabled. This was part of the scope 
discussed in #1084 and #1220.
   
   Specifically, `DeterministicIdAllocator.Namespace` still derives IDs from 
key, sequence number, action name, event type/attributes, and target name 
without using occurrence identity. Please update it and its caller setup in 
`ActionTaskContextManager` to consume the shared action execution identity, 
while retaining target/session/call scoping. Otherwise, two identical event 
occurrences can still produce the same call ID and fail registration in 
`SubgraphCallManager`.



##########
api/src/main/java/org/apache/flink/agents/api/Event.java:
##########
@@ -60,6 +60,17 @@ public class Event {
      */
     private Long sourceTimestamp;
 
+    /**
+     * Runtime-internal lineage identity for this specific event occurrence. 
Not part of the
+     * cross-language event contract and not {@link #id}: {@link #id} is a 
fresh random value on
+     * every construction (including on replay of the same logical input), 
while this field is
+     * assigned deterministically by the runtime from the occurrence's 
position in the causal tree
+     * (business key and sequence number for a root event; producing action 
execution plus output
+     * ordinal for an action's output event). It lets durable action-state 
keys distinguish sibling
+     * event occurrences that carry identical attributes. See FLINK-AGENTS 
issue #1084.

Review Comment:
   Could you clean up the comments throughout this patch to describe the 
current identity and recovery contracts directly? Repeated issue references 
such as `FLINK-AGENTS-1084` and historical wording like “before this fix” are 
unnecessary in production code; that context belongs in the PR description or 
commit message.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to