Skip to content

feat(metering): meter request and response bytes - #1073

Merged
samcamwilliams merged 2 commits into
permaweb:edgefrom
xylophonez:feat/meter-request-response-bytes
Aug 11, 2026
Merged

feat(metering): meter request and response bytes#1073
samcamwilliams merged 2 commits into
permaweb:edgefrom
xylophonez:feat/meter-request-response-bytes

Conversation

@xylophonez

Copy link
Copy Markdown

Extend metering@1.0 with request-bytes and response-bytes resources while retaining beam-reductions. Payloads are forced before sizing; rates remain operator-configured and default to zero.

P4 already hands the pricing device the inbound request in `estimate' and
the result in `price'; both were discarded. Sizing them there puts bandwidth
alongside `beam-reductions' in the same session, so a node can be priced on
what it carried as well as what it computed, and a device that relays on
another node's behalf needs no pricing logic of its own.

Bodies are forced through `hb_cache:ensure_all_loaded/2' before sizing. A
relayed payload arrives as unforced links and measures a few kilobytes
whatever it refers to, so an unforced size is not a size.

Sizes are of the ETF encoding rather than the wire form: stable and monotone
in payload size, but not reproducible by a payer from the bytes it sent, and
the ratio to wire size varies by request shape. Both resources are inert
until an operator sets a rate.

Tested via `rebar3 device test -m dev_metering': a 1 MB body prices above
100x a 1 KB one, and with no rates set both price to zero. Note that
`rebar3 eunit --module=dev_metering' cannot see preloaded devices and
reports the module missing.
Comment thread src/preloaded/payment/dev_metering.erl Outdated
body_size(Req, Opts) when is_map(Req) ->
case hb_maps:get(<<"body">>, Req, not_found, Opts) of
not_found -> 0;
Body -> term_size(hb_cache:ensure_all_loaded(Body, Opts))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this and concluded we definitely didn't want to do this, but only because it has a very bad pathological case. Imagine the node is about to return a giant linked message. It has (implicitly or not) accept-bundle: false. Subsequently, without this call it would happily just drop the value as +link in the result and it would be tiny. With this call we [tl;dr] get completely rekt.

The behavior we actually want is dependent, I think, on whether the body message has a commitment with bundle: true in it. There is still a situation where that goes wrong I think (codec devices that support the brainfucking 'unbundled encoding of a bundle: true commitment case) but we shouldn't encounter that with any frequency, I think. Famous last words...

To easily pick out whether the body message has a bundle: true commitment hb_message:commitment[s] is your friend.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right -- fixed by only loading linked body content when a bundle commitment includes it, with a test covering bundled / unbundled responses

Only load linked body content when a bundle commitment includes it. This preserves link-sized metering for unbundled messages while charging for committed bundled content.
@samcamwilliams
samcamwilliams merged commit 2a39d2e into permaweb:edge Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants