AuroraMeter. Events. Outbox behaviour
(Aurora Meter v1.0.0)
View Source
The one seam between a recorded event and anything that delivers it.
AuroraMeter.record/4 calls enqueue/2 inside the transaction that
writes the event row and its projection delta. That is the whole point: an
export intent that is staged in the same commit as the fact cannot be lost,
and cannot exist for a fact that was rolled back. Configure it with
config :aurora_meter, events_outbox: MyApp.OutboxCore ships AuroraMeter.Events.Outbox.Noop, which is the default and does
nothing. Core has no delivery of its own and knows nothing about Stripe,
customers, meters or modes; Aurora Meter Pro implements this behaviour and
owns every provider-shaped decision.
The contract an implementation must keep
- Use the repo you are given.
context.repois the repo running the open transaction. Checking out another connection puts your write outside the commit that carries the fact, which is the one thing this seam exists to prevent. - Be fast and do no I/O off the database. You are inside a financial
transaction that also holds a shared lock on the projection checkpoint
row. A network call here stalls every concurrent
record/4. - Reserve errors for storage failures. Returning
{:error, reason}or raising rolls the caller'srecord/4back and the caller sees{:error, {:unavailable, {:outbox, reason}}}. Anything you can express as a quarantined item should be enqueued as one and reported later, not turned into a refusal to record the usage.
Eligibility
Each item carries what core knows about whether the event is a candidate for delivery, and nothing else:
:eligible{:ineligible, :feature_buffered}when the feature reports from the buffered counter rather than from events (build unit 03c){:ineligible, :attribution_unresolved}when the period source could not placeoccurred_at, so the period on the row is an approximation{:ineligible, :plan_unresolved}when the period is a fact but no plan assignment coversoccurred_at, so the row names no commercial contract (build unit 07c). The item is never delivered on a guess: substituting today's plan for a historical fact is the repricing decision D05 forbids.{:ineligible, :original_ineligible}on a correction whose original carried an unresolved attribution, of either kind. The correction of an unattributed fact cannot be attributed either, and the reason names which of the two rows is the problem.
Mapping, customer, mode, meter, cutover watermark and timestamp window are the implementation's decisions, taken at enqueue time from its own configuration.
A correction is never dropped. Core stages every one of them, with a reason attached when core can already tell delivery is not possible, because a correction that quietly disappears is a customer credited nothing and told otherwise (I09).
Summary
Types
What the record transaction hands the implementation.
Why core believes an event is not a candidate for delivery.
One export intent.
Callbacks
Stages the export intent for every accepted event, inside the caller's transaction.
Types
What the record transaction hands the implementation.
@type ineligibility() ::
:feature_buffered
| :attribution_unresolved
| :plan_unresolved
| :original_ineligible
Why core believes an event is not a candidate for delivery.
@type item() :: %{ event: AuroraMeter.Event.t(), eligibility: :eligible | {:ineligible, ineligibility()} }
One export intent.