Skip to content

Commit 32f2e96

Browse files
gammazerowenyue
authored andcommitted
fix(bitswap): memory leak on BlockPresenceManager (ipfs#636)
* Fix memory leak on BlockPresenceManager Limit BlockPresenceManager map growth be doing the following: - Use nil map when BlockPresenceManager CID map is empty. - Delete peer map when from CID map when peer map is empty. - Remove peers from BlockPresenceManager when peers are pruned from session. This allows GC to free memory when maps in BlockPresenceManager become empty. Additional improvement: Do not look for lists of keys or peers in empty maps; return early instead. Fix for issue ipfs#574
1 parent fa9cd9d commit 32f2e96

File tree

4 files changed

+55
-3
lines changed

4 files changed

+55
-3
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ The following emojis are used to highlight certain changes:
2525

2626
### Fixed
2727

28+
- `bitswap/client` fix memory leak in BlockPresenceManager due to unlimited map growth.
29+
2830
### Security
2931

3032
## [v0.21.0]

bitswap/client/internal/blockpresencemanager/blockpresencemanager.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ type BlockPresenceManager struct {
1515
}
1616

1717
func New() *BlockPresenceManager {
18-
return &BlockPresenceManager{
19-
presence: make(map[cid.Cid]map[peer.ID]bool),
20-
}
18+
return &BlockPresenceManager{}
2119
}
2220

2321
// ReceiveFrom is called when a peer sends us information about which blocks
@@ -26,6 +24,10 @@ func (bpm *BlockPresenceManager) ReceiveFrom(p peer.ID, haves []cid.Cid, dontHav
2624
bpm.Lock()
2725
defer bpm.Unlock()
2826

27+
if bpm.presence == nil {
28+
bpm.presence = make(map[cid.Cid]map[peer.ID]bool)
29+
}
30+
2931
for _, c := range haves {
3032
bpm.updateBlockPresence(p, c, true)
3133
}
@@ -75,6 +77,10 @@ func (bpm *BlockPresenceManager) AllPeersDoNotHaveBlock(peers []peer.ID, ks []ci
7577
bpm.RLock()
7678
defer bpm.RUnlock()
7779

80+
if len(bpm.presence) == 0 {
81+
return nil
82+
}
83+
7884
var res []cid.Cid
7985
for _, c := range ks {
8086
if bpm.allDontHave(peers, c) {
@@ -90,6 +96,9 @@ func (bpm *BlockPresenceManager) allDontHave(peers []peer.ID, c cid.Cid) bool {
9096
if !cok {
9197
return false
9298
}
99+
if len(ps) == 0 {
100+
return false
101+
}
93102

94103
// Check if we explicitly know that all the given peers do not have the cid
95104
for _, p := range peers {
@@ -108,6 +117,25 @@ func (bpm *BlockPresenceManager) RemoveKeys(ks []cid.Cid) {
108117
for _, c := range ks {
109118
delete(bpm.presence, c)
110119
}
120+
if len(bpm.presence) == 0 {
121+
bpm.presence = nil
122+
}
123+
}
124+
125+
// RemovePeers removes the given peer from every cid key in the presence map.
126+
func (bpm *BlockPresenceManager) RemovePeer(p peer.ID) {
127+
bpm.Lock()
128+
defer bpm.Unlock()
129+
130+
for c, pm := range bpm.presence {
131+
delete(pm, p)
132+
if len(pm) == 0 {
133+
delete(bpm.presence, c)
134+
}
135+
}
136+
if len(bpm.presence) == 0 {
137+
bpm.presence = nil
138+
}
111139
}
112140

113141
// HasKey indicates whether the BlockPresenceManager is tracking the given key

bitswap/client/internal/blockpresencemanager/blockpresencemanager_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@ func TestBlockPresenceManager(t *testing.T) {
9393
if bpm.PeerDoesNotHaveBlock(p, c1) {
9494
t.Fatal(expDoesNotHaveFalseMsg)
9595
}
96+
97+
bpm.ReceiveFrom(p, []cid.Cid{c0}, []cid.Cid{c1})
98+
if !bpm.PeerHasBlock(p, c0) {
99+
t.Fatal(expHasTrueMsg)
100+
}
101+
if !bpm.PeerDoesNotHaveBlock(p, c1) {
102+
t.Fatal(expDoesNotHaveTrueMsg)
103+
}
104+
bpm.RemovePeer(p)
105+
if bpm.PeerHasBlock(p, c0) {
106+
t.Fatal(expHasFalseMsg)
107+
}
108+
if bpm.PeerDoesNotHaveBlock(p, c0) {
109+
t.Fatal(expDoesNotHaveFalseMsg)
110+
}
111+
if bpm.PeerHasBlock(p, c1) {
112+
t.Fatal(expHasFalseMsg)
113+
}
114+
if bpm.PeerDoesNotHaveBlock(p, c1) {
115+
t.Fatal(expDoesNotHaveFalseMsg)
116+
}
96117
}
97118

98119
func TestAddRemoveMulti(t *testing.T) {

bitswap/client/internal/session/sessionwantsender.go

+1
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ func (sws *sessionWantSender) processUpdates(updates []update) []cid.Cid {
455455
go func() {
456456
for p := range prunePeers {
457457
// Peer doesn't have anything we want, so remove it
458+
sws.bpm.RemovePeer(p)
458459
log.Infof("peer %s sent too many dont haves, removing from session %d", p, sws.ID())
459460
sws.SignalAvailability(p, false)
460461
}

0 commit comments

Comments
 (0)