Skip to content

Commit a8a810d

Browse files
committed
Cache score by decayInterval
1 parent 1125869 commit a8a810d

File tree

2 files changed

+115
-2
lines changed

2 files changed

+115
-2
lines changed

test/peer-score.spec.js

+69-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
const sinon = require('sinon')
12
const { expect } = require('chai')
23
const PeerId = require('peer-id')
34
const delay = require('delay')
45

56
const { PeerScore, createPeerScoreParams, createTopicScoreParams } = require('../src/score')
7+
const computeScoreModule = require('../src/score/compute-score')
68
const { ERR_TOPIC_VALIDATOR_IGNORE, ERR_TOPIC_VALIDATOR_REJECT } = require('../src/constants')
79
const { makeTestMessage, getMsgId } = require('./utils')
810

@@ -642,7 +644,6 @@ describe('PeerScore', () => {
642644
const ps = new PeerScore(params, connectionManager, getMsgId)
643645
ps.addPeer(peerA)
644646
ps.graft(peerA, mytopic)
645-
646647
// score should equal -1000 (app-specific score)
647648
const expected = -1000
648649
ps._refreshScores()
@@ -665,3 +666,70 @@ describe('PeerScore', () => {
665666
expect(aScore).to.equal(0)
666667
})
667668
})
669+
670+
describe('PeerScore score cache', function () {
671+
let ps2
672+
let peerA
673+
const sandbox = sinon.createSandbox()
674+
let computeStoreStub
675+
const params = createPeerScoreParams({
676+
appSpecificScore: () => -1000,
677+
appSpecificWeight: 1,
678+
retainScore: 800,
679+
decayInterval: 1000,
680+
topics: {a: {topicWeight: 10}}
681+
})
682+
683+
beforeEach(async () => {
684+
sandbox.useFakeTimers()
685+
peerA = (await PeerId.create({keyType: 'secp256k1'})).toB58String()
686+
computeStoreStub = sandbox.stub(computeScoreModule, 'computeScore')
687+
ps2 = new PeerScore(params, connectionManager, getMsgId)
688+
})
689+
690+
afterEach(() => {
691+
sandbox.restore()
692+
})
693+
694+
it('should compute first time', function () {
695+
computeStoreStub.returns(10)
696+
ps2.addPeer(peerA)
697+
expect(computeStoreStub.calledOnce).to.be.false
698+
ps2.score(peerA)
699+
expect(computeStoreStub.calledOnce).to.be.true
700+
// this time peerA score is cached
701+
ps2.score(peerA)
702+
expect(computeStoreStub.calledOnce).to.be.true
703+
})
704+
705+
const testCases = [
706+
{name: 'decayInterval timeout', fun: () => sandbox.clock.tick(params.decayInterval)},
707+
{name: '_refreshScores', fun: () => ps2._refreshScores()},
708+
{name: 'addPenalty', fun: () => ps2.addPenalty(peerA, 10)},
709+
{name: 'graft', fun: () => ps2.graft(peerA, 'a')},
710+
{name: 'prune', fun: () => ps2.prune(peerA, 'a')},
711+
{name: '_markInvalidMessageDelivery', fun: () => ps2._markInvalidMessageDelivery(peerA, {topicIDs: ['a']})},
712+
{name: '_markFirstMessageDelivery', fun: () => ps2._markFirstMessageDelivery(peerA, {topicIDs: ['a']})},
713+
{name: '_markDuplicateMessageDelivery', fun: () => ps2._markDuplicateMessageDelivery(peerA, {topicIDs: ['a']})},
714+
{name: '_setIPs', fun: () => ps2._setIPs(peerA, [], ['127.0.0.1'])},
715+
{name: '_removeIPs', fun: () => ps2._removeIPs(peerA, ['127.0.0.1'])},
716+
{name: '_updateIPs', fun: () => ps2._updateIPs()},
717+
]
718+
719+
for (const {name, fun} of testCases) {
720+
it(`should invalidate the cache after ${name}`, function () {
721+
computeStoreStub.returns(10)
722+
ps2.addPeer(peerA)
723+
ps2.score(peerA)
724+
expect(computeStoreStub.calledOnce).to.be.true
725+
// the score is cached
726+
ps2.score(peerA)
727+
expect(computeStoreStub.calledOnce).to.be.true
728+
// invalidate the cache
729+
fun()
730+
// should not use the cache
731+
ps2.score(peerA)
732+
expect(computeStoreStub.calledTwice).to.be.true
733+
})
734+
}
735+
})

ts/score/peer-score.ts

+46-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ export class PeerScore {
3030
* IP colocation tracking; maps IP => set of peers.
3131
*/
3232
peerIPs: Map<string, Set<string>>
33+
scoreCache: Map<string, number>
34+
/**
35+
* Flag to mark a peer score cache valid or not.
36+
*/
37+
scoreCacheValid: Map<string, boolean>
38+
/**
39+
* The last time the score for a peer was cached.
40+
*/
41+
scoreCacheTime: Map<string, number>
3342
/**
3443
* Recent message delivery timing/participants
3544
*/
@@ -47,6 +56,9 @@ export class PeerScore {
4756
this._connectionManager = connectionManager
4857
this.peerStats = new Map()
4958
this.peerIPs = new Map()
59+
this.scoreCache = new Map()
60+
this.scoreCacheValid = new Map()
61+
this.scoreCacheTime = new Map()
5062
this.deliveryRecords = new MessageDeliveries()
5163
this.msgId = msgId
5264
}
@@ -153,6 +165,8 @@ export class PeerScore {
153165
if (pstats.behaviourPenalty < decayToZero) {
154166
pstats.behaviourPenalty = 0
155167
}
168+
169+
this.scoreCacheValid.set(id, false)
156170
})
157171
}
158172

@@ -166,7 +180,18 @@ export class PeerScore {
166180
if (!pstats) {
167181
return 0
168182
}
169-
return computeScore(id, pstats, this.params, this.peerIPs)
183+
184+
const now = Date.now()
185+
if (this.scoreCacheValid.get(id) && now - (this.scoreCacheTime.get(id) ?? 0) < this.params.decayInterval) {
186+
const score = this.scoreCache.get(id)
187+
if (score !== undefined) return score
188+
}
189+
190+
const score = computeScore(id, pstats, this.params, this.peerIPs)
191+
this.scoreCacheValid.set(id, true)
192+
this.scoreCacheTime.set(id, now)
193+
this.scoreCache.set(id, score)
194+
return score
170195
}
171196

172197
/**
@@ -181,6 +206,7 @@ export class PeerScore {
181206
return
182207
}
183208
pstats.behaviourPenalty += penalty
209+
this.scoreCacheValid.set(id, false)
184210
}
185211

186212
/**
@@ -199,6 +225,10 @@ export class PeerScore {
199225
const ips = this._getIPs(id)
200226
this._setIPs(id, ips, pstats.ips)
201227
pstats.ips = ips
228+
229+
// initialize score cache
230+
this.scoreCacheTime.set(id, 0)
231+
this.scoreCacheValid.set(id, false)
202232
}
203233

204234
/**
@@ -219,6 +249,11 @@ export class PeerScore {
219249
return
220250
}
221251

252+
// delete score cache
253+
this.scoreCache.delete(id)
254+
this.scoreCacheTime.delete(id)
255+
this.scoreCacheValid.delete(id)
256+
222257
// furthermore, when we decide to retain the score, the firstMessageDelivery counters are
223258
// reset to 0 and mesh delivery penalties applied.
224259
Object.entries(pstats.topics).forEach(([topic, tstats]) => {
@@ -257,6 +292,7 @@ export class PeerScore {
257292
tstats.graftTime = Date.now()
258293
tstats.meshTime = 0
259294
tstats.meshMessageDeliveriesActive = false
295+
this.scoreCacheValid.set(id, false)
260296
}
261297

262298
/**
@@ -282,6 +318,7 @@ export class PeerScore {
282318
tstats.meshFailurePenalty += deficit * deficit
283319
}
284320
tstats.inMesh = false
321+
this.scoreCacheValid.set(id, false)
285322
}
286323

287324
/**
@@ -416,6 +453,7 @@ export class PeerScore {
416453

417454
tstats.invalidMessageDeliveries += 1
418455
})
456+
this.scoreCacheValid.set(id, false)
419457
}
420458

421459
/**
@@ -453,6 +491,7 @@ export class PeerScore {
453491
tstats.meshMessageDeliveries = cap
454492
}
455493
})
494+
this.scoreCacheValid.set(id, false)
456495
}
457496

458497
/**
@@ -496,6 +535,7 @@ export class PeerScore {
496535
tstats.meshMessageDeliveries = cap
497536
}
498537
})
538+
this.scoreCacheValid.set(id, false)
499539
}
500540

501541
/**
@@ -556,6 +596,8 @@ export class PeerScore {
556596
this.peerIPs.delete(ip)
557597
}
558598
}
599+
600+
this.scoreCacheValid.set(id, false)
559601
}
560602

561603
/**
@@ -576,6 +618,8 @@ export class PeerScore {
576618
this.peerIPs.delete(ip)
577619
}
578620
})
621+
622+
this.scoreCacheValid.set(id, false)
579623
}
580624

581625
/**
@@ -587,6 +631,7 @@ export class PeerScore {
587631
const newIPs = this._getIPs(id)
588632
this._setIPs(id, newIPs, pstats.ips)
589633
pstats.ips = newIPs
634+
this.scoreCacheValid.set(id, false)
590635
})
591636
}
592637
}

0 commit comments

Comments
 (0)