Skip to content

discovery: ChannelUpdateInfo freshness fields box on the heap for every channel #11006

Description

@Roasbeef

Background

graphdb.ChannelUpdateInfo stores its two freshness values as lnwire.Timestamp:

type ChannelUpdateInfo struct {
	ShortChannelID lnwire.ShortChannelID
	Version        lnwire.GossipVersion
	Node1Freshness lnwire.Timestamp
	Node2Freshness lnwire.Timestamp
}

lnwire.Timestamp is an interface, and the concrete v1 value is lnwire.UnixTimestamp, which is a uint64. A uint64 does not fit in an interface's data word, so every assignment boxes onto the heap.

Cost

Measured with runtime.MemStats over the exact construction processChanRangeReply performs:

bytes/channel heap allocs/channel
zero timestamps 48.1 0
both timestamps set 64.0 2

So the boxing costs 16 B/channel plus two allocations, a 33% increase over the struct itself.

Where it bites

GossipSyncer.bufferedChanRangeReplies holds one ChannelUpdateInfo per short channel ID for the duration of a query_channel_range sync. Two things multiply it:

  • Every connected peer buffers independently. All syncers start in syncingChans and issue their own range query; NumActiveSyncers (default 3) only governs whether a syncer sends a GossipTimestampRange, not whether it performs the range query.
  • DefaultNumRestrictedSlots is 100, so ~100 inbound peers can be buffering at once, and outbound connections are unrestricted.

With the 100k per-query SCID cap added in #10992, the worst case is roughly 610 MiB steady and 880 MiB peak across 100 peers, of which about 153 MiB is purely the interface boxing.

Proposal

Store the freshness values concretely in the hot struct. Either:

  1. Replace the interface fields with an explicit {version, value uint64} struct, keeping lnwire.Timestamp at the API boundary where polymorphism is actually needed, or
  2. Give ChannelUpdateInfo a v1-specific representation, since Version already discriminates.

Expected result: 64 → 48 B/channel (25% reduction) and 200k fewer heap allocations per saturated sync, with no change to the wire format or database encoding.

Notes

This is not a correctness issue and needs no backport. It surfaced while reviewing the memory bounds in #10992; the fix was left out of that PR to keep it focused on the bound itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions