Skip to content

feat(node): add --promRemoteInterval and report remote write rejections - #5036

Open
jmgilman wants to merge 7 commits into
wormhole-foundation:mainfrom
jmgilman:jmgilman/prom-remote-write
Open

jmgilman wants to merge 7 commits into
wormhole-foundation:mainfrom
jmgilman:jmgilman/prom-remote-write

Conversation

@jmgilman

@jmgilman jmgilman commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Two changes to the Prometheus remote write push (--promRemoteURL).

Rejected pushes were reported as success. On a non-2xx response, ScrapeAndSendLocalMetrics logged the status and returned the outer err, which is nil at that point. An operator with a wrong or expired token saw a node that looked healthy while every push was dropped. Any non-2xx now returns remote write returned status N: <body>, with the receiver's reason (up to 512 bytes), e.g. Grafana's authentication error: invalid token.

The push interval was fixed at 15 seconds. Hosted receivers such as Grafana Cloud bill by samples per minute as well as by active series, so 15 s costs about four times what 1 min does for the same metrics. The new --promRemoteInterval flag (also GUARDIAND_PROMREMOTEINTERVAL or the config file) sets the interval. The default stays 15 s, so nothing changes unless an operator sets it. A zero or negative value stops the node at startup.

Docs: operations.md now documents --promRemoteURL and the interval. It also corrects the claim that Loki telemetry is optional: on mainnet and testnet the node won't start without --telemetryLokiURL unless --disableTelemetry is set.

A non-2xx response from the remote write endpoint was logged and then
reported as success, so an invalid token or rejected batch looked healthy
to the caller. Return an error carrying the status and the start of the
response body for any non-2xx response.
The push interval was fixed at 15 seconds. Hosted receivers bill by
samples per minute as well as active series, so a fixed 15 s interval
costs four times what a 1 minute interval does for the same series.
Default stays 15 s; non-positive values are rejected at startup.

Also document --promRemoteURL in operations.md, and correct the
statement that Loki telemetry is optional on mainnet.

@djb15 djb15 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good otherwise

Comment thread node/pkg/telemetry/prom_remote_write/scrape.go Outdated
SafeRead caps reads at MaxSafeInputSize (128MB). The remote write error
path wrapped it in an io.LimitReader to keep the logged body small.
SafeReadN takes the limit as an argument (1..MaxSafeInputSize) and
SafeRead now delegates to it, so existing callers are unchanged.

A rejected remote write with a body over 512 bytes now reports the status
without the body instead of the first 512 bytes.
djb15
djb15 previously approved these changes Oct 8, 2026
Comment thread node/cmd/guardiand/node.go Outdated
// SafeReadN is SafeRead with a caller-chosen limit of n bytes, for callers that need a tighter
// bound than MaxSafeInputSize. It returns ErrInputTooLarge if the input exceeds n bytes, and
// ErrInvalidReadLimit if n is not between 1 and MaxSafeInputSize.
func SafeReadN(r io.Reader, n int) ([]byte, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd recommend changing SafeReadN() to accept uint instead of int to avoid any casting requirements, since I don't think there's a reason for a caller to ever specify a negative value? The first if-statement could then be updated to if n == 0 || n > MaxSafeInputSize.

@jmgilman jmgilman Oct 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tried uint: it actually adds a cast (uint(len(b)) > n, since len returns int) rather than removing one, and int64(n) for io.LimitReader is needed either way. I kept int to match Go's convention for sizes (len, make, bufio.NewReaderSize). Negative values are also rejected with ErrInvalidReadLimit and covered by a test.

Each push gathers and sends the whole metrics registry, so a very short
interval loads both the node and the receiver. The flag exists to push
less often; make the 15s default also the minimum.

@djb15 djb15 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approving

This branch has not been deployed

No deployments
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.

3 participants