liamkillingback
Every SaaS I have built on Phoenix ended up with the same three hand-rolled pieces: count what each customer uses, stop them at their plan’s limit, and bill for the rest. There is no Elixir equivalent of Laravel Cashier for the billing half, and OpenMeter ships SDKs for Node, Python and Go but not for us. So I extracted the layer I kept rewriting into a library.
Aurora Meter does the counting and gating in a free MIT core:
# count: an ETS increment, nothing touches the database
AuroraMeter.track(org, :ai_generations)
# gate, run and meter atomically
AuroraMeter.with_quota(org, :ai_generations, fn ->
generate_report()
end)
# => {:ok, result} | {:error, :limit_exceeded} | {:error, :not_entitled}
Plans are a small compile-time DSL with hard caps, metered overage and feature flags:
defmodule MyApp.Plans do
use AuroraMeter.Plans
plan :free do
limit :ai_generations, 50, :hard
end
plan :scale do
metered :ai_generations, included: 1_000, unit_price: 2
feature :api_access, true
end
end
How it is built. Increments hit a shared ETS table with :ets.update_counter/4, so there is no GenServer per tenant and nothing on the database write path. A single flusher persists snapshots to Postgres on an interval and on shutdown, and a broadcaster fans live values over PubSub. Two LiveView components read those counters, so a usage bar in your admin updates within a second of the increment on the server. Here it is with a burst of traffic driven from the BEAM:

with_quota/4 reserves first (increment, compare, roll back on breach), runs the function, and releases the reservation if it raises. The reservation is the usage, so two concurrent calls cannot both squeeze through the last unit of a hard limit.
The bundled benchmark (mix aurora_meter.bench 8 500000, dev laptop, Elixir 1.20 / OTP 29) does about 7.9M increments/s across distinct counters, and about 53k/s when all eight processes hammer one counter. The run is in the repo under docs/evidence.
What it does not do yet, honestly:
- Buffered by default: a hard crash can lose up to one flush interval of increments. Durable features exist for anything you invoice.
- Periods are UTC calendar months in the core. Postgres only.
Links: Hex · HexDocs · GitHub:
The core is MIT forever, no email gate. There is a paid Pro package for when Stripe should start charging (checkout, webhook sync, usage reported to Billing Meters, dashboards), but everything above is in the free library.
I would genuinely like to hear what metering shapes people have hand-rolled in their own apps, and what you would want gated that this does not cover.
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex









