Repository navigation
docs(events): qualify idempotencyKey replay as the createEvent contract #1153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -172,12 +172,17 @@ event := events. | |||||
| `Event.IdempotencyKey` carries the schemas v1beta3 | ||||||
| `EventPayload.idempotencyKey` (meshery/schemas v1.3.116 and later): an | ||||||
| optional, client-chosen key of 1 to 255 characters that makes recording one | ||||||
| event safe to retry. Under `createEvent`, a create carrying a key its | ||||||
| authenticated caller already used records nothing and is answered 200 with | ||||||
| the event the first create recorded (`Event-Disposition: replayed`), whatever | ||||||
| the rest of the payload says. Because MeshKit's `Event` decodes the field, | ||||||
| Meshery Server keeps the key when it decodes a client event instead of | ||||||
| dropping it. | ||||||
| event safe to retry. MeshKit carries the field: `Event` encodes, decodes, | ||||||
| validates and persists it, and nothing more. The replay semantics are the | ||||||
| schemas `createEvent` contract: a create carrying a key its authenticated | ||||||
| caller already used records nothing and is answered 200 with the event the | ||||||
| first create recorded (`Event-Disposition: replayed`), whatever the rest of | ||||||
| the payload says. Meshery Server honours that contract only once its | ||||||
| follow-up lands: today its client event handler rebuilds the event without | ||||||
| copying the key (`ApplyClientEventFields`) and persists it without a replay | ||||||
| lookup (`PersistClientEvent`), so a keyed retry to Meshery Server still | ||||||
| records a second event. Do not rely on replay against a server that predates | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Records a second event" is exact for an event that names no condition, but a keyed retry of a condition event takes a different path: PersistClientEvent still calls persist, which routes a correlated event to persistCorrelatedEvent and counts it as another occurrence on the open condition rather than storing a second event row. Worth qualifying so a reader does not expect a duplicate row in that case.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks - Meshery Server now replays keyed retries (meshery/meshery#22958, merged). #1154 replaces the interim caveat in docs/event-streaming.md with the current behaviour: a first keyed create answers 201, a retry with a key the same owner already used answers 200 with |
||||||
| that follow-up. | ||||||
|
|
||||||
| ```go | ||||||
| event := events.NewEvent(). | ||||||
|
|
@@ -192,15 +197,16 @@ event := events.NewEvent(). | |||||
| condition and spans many creates; an `idempotencyKey` makes one create safe | ||||||
| to retry. | ||||||
| - `Build` copies the key into every event it builds. Set a new key before | ||||||
| building another occurrence from the same builder, or the server answers | ||||||
| that occurrence as a replay of the first. | ||||||
| building another occurrence from the same builder: under the | ||||||
| `createEvent` contract, a server that honours it answers that occurrence | ||||||
| as a replay of the first. | ||||||
| - GORM `AutoMigrate` adds a nullable `idempotency_key` column and a | ||||||
| non-unique composite index `idx_events_owner_idempotency_key` on | ||||||
| `(owner, idempotency_key)`, so a server finds the event a key is bound to | ||||||
| by owner and key. The contract scopes keys to the authenticated caller | ||||||
| (organization and user), not to the client-settable `owner` field, so the | ||||||
| server sets `owner` from the caller and applies the replay semantics; | ||||||
| MeshKit does neither. | ||||||
| (organization and user), not to the client-settable `owner` field, so a | ||||||
| server that honours the contract sets `owner` from the caller and applies | ||||||
|
Comment on lines
+207
to
+208
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope replay by both organization and user. Setting
|
||||||
| the replay semantics; MeshKit does neither. | ||||||
| - `Event.Validate()` rejects a key that is empty or longer than 255 | ||||||
| characters with `ErrInvalidEvent`. | ||||||
|
|
||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Describe the event bound to the key.
A first keyed create can increment or resolve an existing correlated event rather than record a new event. A lookup against the event’s single
idempotency_keycolumn cannot find every key bound to that event. Describe the binding separately, and update the same wording inmodels/events/events.goandmodels/events/correlation.go. (raw.githubusercontent.com)