nevzheng commented on PR #12692:
URL: https://github.com/apache/gravitino/pull/12692#issuecomment-5446793101

   Adding a sequence diagram, since the gap this fills is easier to see than to 
describe.
   
   Both halves use the same participants and the same pipeline. Solid arrows 
are calls with their payload shown; dashed arrows are return values.
   
   ```mermaid
   sequenceDiagram
       autonumber
       participant IN as Inner dispatcher layer<br/>(policy / validation)
       participant OD as TableEventDispatcher<br/>(outer, builds the event)
       participant RC as RequestContext<br/>(thread-local)
       participant EB as EventBus
       participant AL as AuditLog + formatter
   
       Note over IN,AL: solid arrow = call, payload shown · dashed arrow = 
return value<br/>Participants and pipeline are identical in both halves
   
       rect rgb(253, 235, 235)
       Note over IN,AL: WITHOUT THIS CHANGE — the fact has no way out of the 
inner layer
       OD->>IN: createTable(ident, columns, properties, ...)
       Note over IN: derives an audit-worthy fact,<br/>e.g. {"policy.decision": 
"COMPLIANT"}
       IN-->>OD: Table
       Note over IN,OD: return type is Table — a single slot.<br/>the fact is 
dropped at this boundary
       Note over OD: builds CreateTableEvent(user, ident, TableInfo)<br/>no 
argument exists to carry the fact
       OD->>EB: dispatchEvent(CreateTableEvent)
       EB->>AL: onPostEvent(CreateTableEvent)
       Note over AL: writes 1 record<br/>"customInfo": {}  ← field exists, 
always empty
       end
   
       rect rgb(233, 246, 236)
       Note over IN,AL: WITH THIS CHANGE — the fact travels alongside the return
       OD->>IN: createTable(ident, columns, properties, ...)
       Note over IN: derives the same fact
       IN->>RC: setAuditExtras({"policy.decision": "COMPLIANT"})
       IN-->>OD: Table
       OD->>RC: takeAuditExtras()
       RC-->>OD: {"policy.decision": "COMPLIANT"}<br/>and clears the slot
       Note over OD: builds CreateTableEvent(user, ident, 
TableInfo,<br/>{"policy.decision": "COMPLIANT"})
       OD->>EB: dispatchEvent(CreateTableEvent)
       EB->>AL: onPostEvent(CreateTableEvent)
       Note over AL: writes 1 record<br/>"customInfo": {"policy.decision": 
"COMPLIANT"}
       end
   ```
   
   Three things the diagram makes concrete:
   
   - **The audit pipeline is not new and is not modified.** Everything from 
`dispatchEvent` rightward is identical in both halves, which is why no listener 
or formatter needed changing. `customInfo` is already a serialized field on the 
record today — for table events it is simply always empty.
   - **The gap is a scoping problem, not a capability one.** 
`TableDispatcher.createTable` returns `Table`, one slot, so when control 
returns to the outer dispatcher the inner layer's fact was never carried back. 
Since the event is constructed at that point, there is nothing in scope to 
populate `customInfo` with.
   - **Still one event per operation.** The change enriches the existing 
terminal event rather than publishing a sibling that consumers would have to 
correlate.
   
   The `takeAuditExtras()` read also clears the slot, which is what prevents a 
stashed fact from leaking onto the next operation that reuses the same pooled 
thread. `TestRequestContext` and `TestTableEventDispatcher` both pin that.
   
   Nevin
   Sent from my 🤖 (Cursor)
   


-- 
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