Skip to content

Commit 0216749

Browse files
committed
Unmarshal directly during iteration to reduce intermediate slices
Currently, listing many rules or set elements generates a lot of intermediate slices, which increases memory usage unnecessarily. This change unmarshals elements during iteration, avoiding these intermediate allocations. Benchmarks show improved performance, particularly when reading rules. Depends on: mdlayher/netlink#258
1 parent 1db35da commit 0216749

4 files changed

Lines changed: 47 additions & 39 deletions

File tree

conn.go

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -185,37 +185,34 @@ func (cc *Conn) receiveSeq(conn *netlink.Conn) iter.Seq2[netlink.Message, error]
185185
break
186186
}
187187

188-
replies, err := conn.Receive()
189-
if err != nil {
190-
// Yield the error but continue iterating
191-
if !yield(netlink.Message{}, err) {
192-
return
188+
for res, err := range conn.ReceiveIter() {
189+
if err != nil {
190+
// Yield the error but continue iterating
191+
if !yield(netlink.Message{}, err) {
192+
return
193+
}
193194
}
194-
continue
195-
}
196195

197-
if len(replies) == 0 && cc.TestDial != nil {
198-
// When using a test dial function, we don't always get a reply for each
199-
// sent message. Additionally, there is no buffer to poll for more data,
200-
// so we stop here.
201-
return
202-
}
203-
204-
for _, msg := range replies {
205196
// Filter out non-nftables messages.
206197
// In practice, this would only be netlink.Error messages.
207198
// Those are handled by the netlink library itself and should be
208-
// reported as errors by conn.Receive().
209-
subsystem := msg.Header.Type >> 8
199+
// reported as errors by conn.ReceiveIter().
200+
subsystem := res.Header.Type >> 8
210201
if subsystem != unix.NFNL_SUBSYS_NFTABLES {
211202
continue
212203
}
213204

214-
// Stop iteration if yield returns false
215-
if !yield(msg, nil) {
205+
if !yield(res, nil) {
216206
return
217207
}
218208
}
209+
210+
if cc.TestDial != nil {
211+
// When using a test dial function, we don't always get a reply for each
212+
// sent message. Additionally, there is no buffer to poll for more data,
213+
// so we stop here.
214+
return
215+
}
219216
}
220217
}
221218
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ require (
1515
golang.org/x/net v0.43.0 // indirect
1616
golang.org/x/sync v0.6.0 // indirect
1717
)
18+
19+
replace github.com/mdlayher/netlink => ../netlink

rule.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,20 +170,25 @@ func (cc *Conn) getRules(t *Table, c *Chain, msgType nftMsgType, handle uint64)
170170
return nil, fmt.Errorf("SendMessages: %v", err)
171171
}
172172

173-
reply, err := cc.receive(conn)
174-
if err != nil {
175-
return nil, fmt.Errorf("receive: %w", err)
176-
}
177173
var rules []*Rule
178-
for _, msg := range reply {
179-
r, err := ruleFromMsg(t.Family, msg)
180-
if err != nil {
181-
return nil, err
174+
var firstErr error
175+
176+
for msg, err := range cc.receiveSeq(conn) {
177+
if err != nil && firstErr == nil {
178+
firstErr = err
179+
continue
182180
}
183-
rules = append(rules, r)
181+
182+
rule, err := ruleFromMsg(t.Family, msg)
183+
if err != nil && firstErr == nil {
184+
firstErr = err
185+
continue
186+
}
187+
188+
rules = append(rules, rule)
184189
}
185190

186-
return rules, nil
191+
return rules, firstErr
187192
}
188193

189194
func (cc *Conn) newRule(r *Rule, op ruleOperation) *Rule {

set.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,20 +1106,24 @@ func (cc *Conn) getSetElements(s *Set, e []SetElement, reset bool) ([]SetElement
11061106
return nil, fmt.Errorf("SendMessages: %v", err)
11071107
}
11081108

1109-
reply, err := cc.receive(conn)
1110-
if err != nil {
1111-
return nil, fmt.Errorf("receive: %w", err)
1112-
}
11131109
var elems []SetElement
1114-
for _, msg := range reply {
1115-
s, err := elementsFromMsg(uint8(s.Table.Family), msg)
1116-
if err != nil {
1117-
return nil, err
1110+
var firstErr error
1111+
for msg, err := range cc.receiveSeq(conn) {
1112+
if err != nil && firstErr == nil {
1113+
firstErr = err
1114+
continue
11181115
}
1119-
elems = append(elems, s...)
1116+
1117+
e, err := elementsFromMsg(uint8(s.Table.Family), msg)
1118+
if err != nil && firstErr == nil {
1119+
firstErr = err
1120+
continue
1121+
}
1122+
1123+
elems = append(elems, e...)
11201124
}
11211125

1122-
return elems, nil
1126+
return elems, firstErr
11231127
}
11241128

11251129
// GetSetElements returns the elements in the specified set.

0 commit comments

Comments
 (0)