BaryoDev / BaryoDev/barakoCMS

A reservation primitive: decrement under a floor, atomically, with an expiring hold

Open
#580 0 comments 0 reactions 0 assignees View on GitHub
core enhancement
Dominant language
C#
Stars
6
Forks
7
Avg merge
4h 42m
Merged PRs (30d)
307

Description

Four of the systems barakoCMS is meant to back need the same primitive: decrement a shared counter, never below a floor, under concurrency, with a hold that expires if it is not confirmed.

- Inventory: stock on hand.
- Booking: seats on a slot.
- Enrollment: places on a class.
- Delivery: capacity on a route.

It does not exist, and the two nearest things both point the wrong way.

`NumberSequence` (`BarakoCMS.Accounting/Domain/NumberSequence.cs`) is one row per named sequence under Marten optimistic concurrency, and #551 already records that its abort rate rises with concurrency, which is the exact workload a reservation has.

`Content` has no optimistic concurrency at all. `WorkflowRun`, `JobRecord`, `RefreshToken`, `OtpCode`, `PendingRegistration` and `MfaSecret` are configured with it in `Extensions/ServiceCollectionExtensions.cs`; `Content` is not. So the most-needed primitive is missing and its closest relative is known to be the wrong shape.

## What to add

A counter that can be reserved against, with:

**An atomic decrement with a floor.** Two callers racing for the last unit: one succeeds, one is refused. Refused, not queued and not silently allowed negative. In Postgres this is one statement, not a read followed by a write, which is why it cannot be built on the current content write path.

**A hold with an expiry.** A booking held while payment is attempted, released automatically if payment never lands. Without expiry every abandoned checkout permanently consumes a seat.

**Confirmation and release as explicit steps**, so a workflow can hold, call the payment provider, and then confirm or release based on the answer.

**A record of every movement**, because "why is stock 3 when we received 10 and sold 5" is the question this always has to answer.

## What to decide

**Whether gapless matters.** #551 makes the point for numbering: gapless is a business requirement that costs serialization. For stock it usually does not matter, and saying so explicitly avoids inheriting the sequence design by accident.

**Whether it is core or a module.** It needs a write path that core owns, which argues core, and it is not something a CMS needs, which argues module. This is the decision to make in the thread before code.

## Done when

- Concurrent reservations for the last unit: exactly one succeeds.
- A hold that is not confirmed expires and returns the unit.
- Every movement is recorded and the balance can be derived from the movements.
- A test drives real concurrency rather than asserting a sequential path.

Contributor guide

Open the contributing guide

Research direction

Start by reading BarakoCMS.Accounting/Domain/NumberSequence.cs, #551, and the optimistic-concurrency configuration in Extensions/ServiceCollectionExtensions.cs. Resolve in the issue thread whether this belongs in core or a module before defining the design. Done means real concurrent reservation testing, expiry and return of unconfirmed holds, explicit confirmation and release, and a complete movement record.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, postgresql
Domain
backend, backend-api-design, databases, distributed-systems
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.