Live Demo: Meter
A month of usage from an invented AI writing product runs through the Precomputing Meter, inside SQLite, right here in your browser. Six customers call three models through two gateways, and every request is reported to one meter. Retries arrive twice. A link goes down for six hours, and a queue sticks at the month’s end. The meter counts each request once, in the hour it happened, and September’s invoices come out exact.
Simulated usage, real meter. The customers, prices and requests are invented and follow a fixed script. The meter is a compiled Precomputing policy running in SQLite’s own WebAssembly build, with the billing tables in the same file.
How to Run It
- Press Play. September starts. The month passes in about a minute, slowing down when something happens.
- Make trouble. Start a retry storm, or cut the eu gateway’s link to the meter, and watch the refused reports and the hourly chart.
- Review the results. The month finishes and closes. Every invoice is checked against a recount of the gateways’ reports.
- Ask the meter. Run a ready-made SQL question or write your own, then download the file.
On a wide screen the demo can use the whole window: open it full screen.
The Controls
| Control | What it does |
|---|---|
| Play, Pause, Resume | Starts the month, pauses it and carries on. An uninterrupted month takes about a minute |
| Stop | Ends the run and goes back to 1 September |
| Review the results | Finishes the month at full speed, lets it close, then checks every invoice |
| Retry storm | For the next hour every request is reported three times, through either gateway |
| Cut the eu link | The eu gateway loses its link to the meter for 12 hours. It keeps serving and holds its reports |
| Raise Juniper’s quota | Once Juniper Bakery Blog has used its Free quota, raises its limit so the gateway serves it again |
| 3 days, Month | Shows the last three days of the hourly chart, or the whole month |
| Policy, Compiled SQL, Billing SQL, Try the compiler | Show the meter’s policy, its SQL, the billing tables beside it, and a box to compile a changed policy |
| Run | Runs your SQL against the meter while the month is paused or finished |
The Month
| Customer | Plan | Gateway | Requests an hour | Models |
|---|---|---|---|---|
| Sundial Research | Enterprise, 25% off list | us | About 210 | Small, medium and large |
| Quarry Road Media | Pro | us | About 130 | Mostly medium |
| Maple Grove Travel | Pro | us | About 85 | Mostly small |
| Harbor Legal Drafts | Pro | eu | About 55 | Medium and large |
| Juniper Bakery Blog | Free, 5 million tokens a month | eu | About 7 | Mostly small |
| Northgate Tutors | Free, 5 million tokens a month | eu | About 2 | Small |
Pro has a quota of 300 million tokens a month; Enterprise has none. Example list prices per million tokens, in and out: small $0.30 and $1.20, medium $2.50 and $10, large $12 and $48. Model cost is half of list. All invented.
About 2% of requests get no answer in time and are sent again, with the same request id, through either gateway. On top of that the script plants four events:
- 15 September, 08:00 to 14:00. The eu gateway loses its link to the meter for six hours, keeps serving, and delivers the 625 reports it held when the link returns.
- 27 September. Juniper Bakery Blog reaches its Free quota, and the eu gateway turns its requests away until October.
- Around midnight on 30 September. The eu gateway is slow, so some September requests reach the meter in October.
- 2 October, 05:00. A queue stuck in the us gateway since 30 September is emptied, five hours after September closed.
The Policy
stream usage exact {
id request_id refuse repeats 7d # a retried request is counted once
key customer text
key model text
key gateway text
value input_tokens integer
value output_tokens integer
derive tokens = input_tokens + output_tokens
late 48h # up to two days late still counts, in the month it happened,
period month close 24h # unless that month has closed: a month closes a day after it ends
raw until closed + 90d # every request kept until its month has been closed 90 days
rollup 1h keep 400d
rollup 1d keep forever
}
precompute requests_month = count(usage) by customer, model per month
precompute input_month = sum(usage.input_tokens) by customer, model per month
precompute output_month = sum(usage.output_tokens) by customer, model per month
precompute tokens_month = sum(usage.tokens) by customer per month
quota monthly_tokens = tokens_month
The invoices are a view over the precomputes and a price list kept in the same file, with money in billionths of a dollar as integers, so every sum is exact and rounding to the cent happens once, at the end.
What the Numbers Mean
- Requests metered: reports the meter counted, one per request. A retried report of a request already counted is refused, so no request counts twice.
- Retried reports refused: reports whose request id the meter had already counted in the last seven days.
- September revenue so far: the invoices as they stand, read from the precomputes with one SQL view.
- Gross margin after model costs: revenue minus what the product pays for the models, as a share of revenue.
- The quota bar: the
monthly_tokensview, which each gateway reads once a minute before it serves. A read is one lookup by primary key. - What the meter refused: every refused report, counted by reason in
usage_refused. A report refused as closed is money known to be missing, to the request.
Things to Try
- Let the month finish untouched. Press Play and wait, or Review the results at once. September’s invoices come to $5,804.56 against $3,337.84 of model cost, and every line matches the recount.
- Start a retry storm. For an hour every request is reported three times. The refused count jumps, the invoices do not.
- Cut the eu link. The eu gateway’s hourly chart shows a gap. When the link returns, the held reports arrive and the gap fills in, because each request counts in the hour it happened.
- Watch Juniper’s quota. Late in the month its bar fills and the gateway turns requests away. Raise the quota and it is served again; the requests turned away were never served, so they are never billed.
- Wait for the stuck queue. On 2 October, 642 reports arrive after September has closed. The meter refuses every one, the refusal count says exactly what was missed, and the invoices sent on the 2nd stay true.
- Ask the meter. Try “Reconcile with every request”, which sets each invoice line beside the same totals recomputed from the requests kept whole, or “Margin by customer”.
If It Does Not Start
The demo works in any current Chrome, Edge, Firefox or Safari, on a computer or a phone, with JavaScript switched on. It needs WebAssembly, which all of them support. SQLite comes with the page, 1.5 MB (577 KB compressed), and runs inside it. If the demo says it could not start, try another browser, or allow scripts on this page if an extension blocks them.
In testing no check has failed. If one ever does, the results say so in red, and the project would be glad to hear about it through the contact page.
What Is Real Here
Real:
- The meter: the compiled policy, from the real compiler, in SQLite’s own WebAssembly build.
- The billing tables and views, and every invoice amount, to the billionth of a dollar.
- The checks: separate code takes the gateways’ reports in the order they were sent, applies the policy’s rules itself and never reads the meter.
Simulated:
- The product, its customers, plans, prices and requests, and the two gateways.
- Time: a month passes in about a minute.
The native Engine takes the same month’s reports in 2.3 to 3.3 seconds and writes the same values in every table: see the Platform page. The AI usage billing case study follows the month.