lightninglabs / lightninglabs/aperture

meterd: SQL-backed Store over aperturedb, replacing the JSON state file

Open
#254 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

database enhancement metering
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 bundles table (token ID, model, remaining tokens, price, created/authorized timestamps) with a migration under aperturedb/sqlc/migrations, and queries under aperturedb/sqlc/queries.
  • A SQLStore implementing meterd.Store over the generated querier, wired through the existing WithStore option.
  • Debit and Authorize in 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 JSONStore for 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.
  • lightmeter embeds meterd with 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.