fix: fence command events by dispatch generation

This commit is contained in:
2026-09-06 09:18:37 +00:00
parent 981cd441fd
commit 94c3f6dcb4
3 changed files with 23 additions and 18 deletions
+3 -3
View File
@@ -244,7 +244,7 @@ func (server *AgentServer) serveConnection(parent context.Context, connection *w
continue continue
} }
if event := envelope.GetCommandEvent(); event != nil { if event := envelope.GetCommandEvent(); event != nil {
appendEvent, eventErr := eventAppendFromWire(event, hello.GetClientId(), server.now()) appendEvent, eventErr := eventAppendFromWire(event, hello.GetClientId(), registration.Generation, server.now())
if eventErr != nil { if eventErr != nil {
server.close(connection, websocket.StatusPolicyViolation, "invalid command event") server.close(connection, websocket.StatusPolicyViolation, "invalid command event")
return return
@@ -263,7 +263,7 @@ func (server *AgentServer) serveConnection(parent context.Context, connection *w
} }
} }
func eventAppendFromWire(event *rvboxv1.CommandEvent, clientID string, receipt time.Time) (store.EventAppend, error) { func eventAppendFromWire(event *rvboxv1.CommandEvent, clientID string, generation uint64, receipt time.Time) (store.EventAppend, error) {
issue, err := domain.ParseUUIDv7(event.GetIssueUuid()) issue, err := domain.ParseUUIDv7(event.GetIssueUuid())
if err != nil { if err != nil {
return store.EventAppend{}, err return store.EventAppend{}, err
@@ -274,7 +274,7 @@ func eventAppendFromWire(event *rvboxv1.CommandEvent, clientID string, receipt t
} }
var immutable [32]byte var immutable [32]byte
copy(immutable[:], event.GetImmutableEventSha256()) copy(immutable[:], event.GetImmutableEventSha256())
result := store.EventAppend{IssueUUID: [16]byte(issue), ClientID: clientID, EventSeq: event.GetEventSeq(), ObservedUnixNano: event.GetObservedAt().AsTime().UnixNano(), ReceiptUnixNano: receipt.UnixNano(), EventType: eventType(event), Compression: 1, RawLength: uint64(len(payload)), Payload: payload, ImmutableSHA256: immutable} result := store.EventAppend{IssueUUID: [16]byte(issue), ClientID: clientID, SessionGeneration: generation, EventSeq: event.GetEventSeq(), ObservedUnixNano: event.GetObservedAt().AsTime().UnixNano(), ReceiptUnixNano: receipt.UnixNano(), EventType: eventType(event), Compression: 1, RawLength: uint64(len(payload)), Payload: payload, ImmutableSHA256: immutable}
if output := event.GetOutput(); output != nil { if output := event.GetOutput(); output != nil {
result.Stream = uint16(output.GetStream()) result.Stream = uint16(output.GetStream())
result.Output = true result.Output = true
+2 -2
View File
@@ -116,8 +116,8 @@ func TestWireEventAppendCarriesClientBinding_HP_EVENT_01(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
event.ImmutableEventSha256 = digest[:] event.ImmutableEventSha256 = digest[:]
appendEvent, err := eventAppendFromWire(event, "client-a", time.Now()) appendEvent, err := eventAppendFromWire(event, "client-a", 7, time.Now())
if err != nil || appendEvent.ClientID != "client-a" || appendEvent.EventSeq != 1 || appendEvent.EventType != 4 || appendEvent.ImmutableSHA256 != digest { if err != nil || appendEvent.ClientID != "client-a" || appendEvent.SessionGeneration != 7 || appendEvent.EventSeq != 1 || appendEvent.EventType != 4 || appendEvent.ImmutableSHA256 != digest {
t.Fatalf("wire event append = %#v, %v", appendEvent, err) t.Fatalf("wire event append = %#v, %v", appendEvent, err)
} }
} }
+5
View File
@@ -34,6 +34,7 @@ func (function FaultInjectorFunc) Checkpoint(name string) error { return functio
type EventAppend struct { type EventAppend struct {
IssueUUID [16]byte IssueUUID [16]byte
ClientID string ClientID string
SessionGeneration uint64
EventSeq uint64 EventSeq uint64
ObservedUnixNano int64 ObservedUnixNano int64
ReceiptUnixNano int64 ReceiptUnixNano int64
@@ -91,6 +92,10 @@ JOIN storage_counters ON storage_counters.singleton = 1 WHERE commands.issue_uui
query += ` AND commands.client_id = ?` query += ` AND commands.client_id = ?`
arguments = append(arguments, event.ClientID) arguments = append(arguments, event.ClientID)
} }
if event.SessionGeneration != 0 {
query += ` AND commands.target_session_generation = ?`
arguments = append(arguments, event.SessionGeneration)
}
err = database.QueryRowContext(ctx, query, arguments...).Scan( err = database.QueryRowContext(ctx, query, arguments...).Scan(
&lastSequence, &commandOutputCharged, &commandCharged, &closeoutRemaining, &clientID, &clientCharged, &serverCharged) &lastSequence, &commandOutputCharged, &commandCharged, &closeoutRemaining, &clientID, &clientCharged, &serverCharged)
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {