lightninglabs / lightninglabs/aperture
meterd: SQL-backed Store over aperturedb, replacing the JSON state file
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 268
- Forks
- 78
- Avg merge
- 22h 25m
- Merged PRs (30d)
- 1
Description
meterd persists bundle balances to a JSON file today (JSONStore, coalesced writes to statepath). That was the right thing to get the metering loop working end to end, but it is the wrong shape for anything running for real: the whole state is rewritten on every flush, there are no transactions across a debit, and it does not compose with the storage the rest of aperture already has.
Aperture has a full sqlc-generated SQL layer in aperturedb/ (sqlite and postgres, with migrations and typed queries) already serving l402_transactions, mpp_sessions, lnc_sessions, secrets, and onion. Bundle balances belong there too, next to the L402 transactions they are keyed against.
Why this is small
The seam already exists. #247 extracted a Store interface (meterd/interfaces.go) precisely so embedders could supply their own persistence, and JSONStore is just one implementation of it:
type Store interface {
Book(tokenID, model string, tokens, priceSats int64) (bool, error)
Authorize(tokenID string, estimate int64) (int64, bool, error)
Get(tokenID string) (Bundle, bool)
Debit(tokenID string, tokens, releaseEstimate int64) (Bundle, bool, error)
ExpireStale(...) (int, error)
...
}
So this is a new implementation behind an existing interface plus a migration, not a refactor of meterd.
What to build
- A
bundlestable (token ID, model, remaining tokens, price, created/authorized timestamps) with a migration underaperturedb/sqlc/migrations, and queries underaperturedb/sqlc/queries. - A
SQLStoreimplementingmeterd.Storeover the generated querier, wired through the existingWithStoreoption. DebitandAuthorizein a single transaction each. This is the real win over the JSON store: the read-modify-write around a balance becomes atomic instead of relying on a process-wide mutex, which is also what makes more than one meterd instance possible.- Keep
JSONStorefor the regtest/dev path and the tests; select by config.
Notes
- Reservations are intentionally in-memory and non-persisted (a restart clearing them is currently the only thing bounding a stranded reservation, see the TTL issue). Decide deliberately whether the SQL store keeps that property or persists them; if it persists them, the TTL work becomes a prerequisite rather than a follow-up.
lightmeterembedsmeterdwith its own sqlite store, so there is a working reference for what the interface demands in practice.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with meterd/interfaces.go and JSONStore, then inspect aperturedb/sqlc/migrations, aperturedb/sqlc/queries, and the existing WithStore wiring. Use lightmeter's sqlite store as the reference for the Store interface and decide how reservations retain their current behavior. Done means generated SQL access, a transactional SQLStore, config-based selection, and coverage for the new persistence path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, postgresql, sql, sqlite
- Domain
- backend, database
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100