Summary
Marketplace listing create and cancel can leave a duplicate listing or a row stuck in pending, and operators cannot recover a stuck row through the API.
Location (origin/main 496d9d7, internal/api/handler_marketplace.go)
reserveAndCreateListing (:221): ClientToken is uuid.New() per call (:251), so it is not persisted and AWS cannot dedup a retry. An ambiguous AWS error (for example a timeout after AWS accepted the request) hits releaseMarketplaceClaim (:257), so the slot is free and a second listing is possible.
- A process crash between
CreateMarketplaceListing and UpdatePurchaseHistoryListing (:266) leaves the row pending with no listing id.
marketplaceCancel (:336) accepts only active (:360), so a pending row cannot be recovered through the API.
marketplaceCancel (:371-381): AWS cancel succeeds, the DB write fails, the row stays active, and a retry calls AWS on an already-cancelled listing.
- The success path records an empty AWS
Status as '', which the claim treats as free while a listing is live.
releaseMarketplaceClaim (:329) after a successful cancel still uses the request context.
Suggested direction
Persist the ClientToken before the AWS call and reconcile through DescribeReservedInstancesListings.
Context
Found in the #506 review (merged). Related: #335 (closed), #267, #261, #449.
Acceptance
A simulated timeout after AWS accepts the listing, and a simulated crash before persist, both recover to a single correct listing state without a duplicate, covered by tests.
Summary
Marketplace listing create and cancel can leave a duplicate listing or a row stuck in
pending, and operators cannot recover a stuck row through the API.Location (origin/main 496d9d7,
internal/api/handler_marketplace.go)reserveAndCreateListing(:221):ClientTokenisuuid.New()per call (:251), so it is not persisted and AWS cannot dedup a retry. An ambiguous AWS error (for example a timeout after AWS accepted the request) hitsreleaseMarketplaceClaim(:257), so the slot is free and a second listing is possible.CreateMarketplaceListingandUpdatePurchaseHistoryListing(:266) leaves the rowpendingwith no listing id.marketplaceCancel(:336) accepts onlyactive(:360), so apendingrow cannot be recovered through the API.marketplaceCancel(:371-381): AWS cancel succeeds, the DB write fails, the row staysactive, and a retry calls AWS on an already-cancelled listing.Statusas'', which the claim treats as free while a listing is live.releaseMarketplaceClaim(:329) after a successful cancel still uses the request context.Suggested direction
Persist the
ClientTokenbefore the AWS call and reconcile throughDescribeReservedInstancesListings.Context
Found in the #506 review (merged). Related: #335 (closed), #267, #261, #449.
Acceptance
A simulated timeout after AWS accepts the listing, and a simulated crash before persist, both recover to a single correct listing state without a duplicate, covered by tests.