---
id: w8/m5/t002
title: Rollup meters Database/KeyValue instance-seconds
worker: worker8
status: todo
estimate: 45m
depends_on: [w8/m5/t001]
---

## Objective

Extend `usage.Service.catchUp`/`rollup` to also process `Database` and `KeyValue` instances, per t001's applicability matrix, so their compute is measured and upserted just like `App` services.

## Context

- `lego/backend/internal/usage/service.go:180` (`catchUp`) and `:207` (`rollup`) both call `s.Store.ListApps(ctx)` exclusively.
- `Database`/`KeyValue` are not control-plane store rows — list them via the k8s client, filtered by `core.LabelTenant`/`core.LabelWorkspace` (see `internal/postgres/service.go`).
- `queryInstanceSeconds` already takes an app/service name + namespace; confirm the cAdvisor/Prometheus pod-presence query generalizes to CNPG/Valkey pod naming, or add a kind-aware variant.
- `HourlyRow` (`store.HourlyRow`) needs `WorkspaceID`/`ServiceID`/`Tier` populated from the CR's tenant label and `Spec.Plan`.

## Steps

1. Add a k8s-client list helper for `Database`+`KeyValue` CRs (tenant-scoped, mirroring `internal/postgres/service.go`'s `List` pattern).
2. Wire both into `catchUp` and `rollup` alongside the existing App loop.
3. Query `instance_seconds` for each and upsert `HourlyRow{ServiceID: <cr id>, Tier: <cr plan>, Kind: instance_seconds}`.
4. Skip `egress_bytes`/`build_seconds` for these kinds per t001, unless t001 decided otherwise for public egress.

## Files

- `lego/backend/internal/usage/service.go`

## Acceptance criteria

- [ ] A `Database`/`KeyValue` instance with pod activity in a window produces an `instance_seconds` `usage_hourly` row keyed by its id and plan.
- [ ] Catch-up on restart covers Database/KeyValue windows the same way it does for Apps.

## Out of scope

- Store/`MonthToDate` query changes (t003).
