trac-msb 0.2.26 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/package.json +11 -4
- package/rpc/handlers.js +9 -0
- package/rpc/routes/v1.js +2 -0
- package/scripts/check-hyperdht-bogon.cjs +50 -0
- package/scripts/hypercore-stallfix/apply.mjs +199 -0
- package/scripts/hypercore-stallfix/hypercore-11.18.3.mjs +434 -0
- package/scripts/hypercore-stallfix/hypercore-11.8.3.mjs +348 -0
- package/src/config/config.js +5 -0
- package/src/config/env.js +3 -0
- package/src/core/network/services/ReplicationStallGuard.js +177 -0
- package/src/index.js +28 -0
- package/src/utils/hypercoreStallfix.js +53 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// Exact-text patch set for hypercore 11.18.3 lib/replicator.js (OPENMAYHEM-STALLFIX v2).
|
|
2
|
+
// Each edit is [original, replacement]; the original must occur exactly once in the unpatched file.
|
|
3
|
+
// Keep this file free of backticks and dollar-brace sequences inside the edit texts (String.raw templates).
|
|
4
|
+
|
|
5
|
+
// Upstream c24f37a6 (#911): a close event that already fired cannot detach a later attachment.
|
|
6
|
+
const closedStreamGuard = [
|
|
7
|
+
String.raw` attachTo(protomux) {
|
|
8
|
+
if (this.core.closed) return
|
|
9
|
+
`,
|
|
10
|
+
String.raw` attachTo(protomux) {
|
|
11
|
+
if (this.core.closed) return
|
|
12
|
+
if (protomux.stream.destroying) return
|
|
13
|
+
`
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
version: '11.18.3',
|
|
18
|
+
file: 'lib/replicator.js',
|
|
19
|
+
originalSha256: '2e7a2631a0d8e805861339bbc75e6f6de75565813cf201256105408c53c8ac93',
|
|
20
|
+
patchedSha256: '1ba2f169d20840d41333cc55e8fbb7da9bc5b4698d808739ad97acce0cbe0f1a',
|
|
21
|
+
// Only this exact released v1 file is eligible for an in-place upgrade.
|
|
22
|
+
previousPatchedSha256: '79944caa33cc875d563f7eba8b820ffbd9323984d30e2fd6c58030804ded2f6c',
|
|
23
|
+
upgradeEdits: [
|
|
24
|
+
['// OPENMAYHEM-STALLFIX v1 (hypercore 11.18.3).', '// OPENMAYHEM-STALLFIX v2 (hypercore 11.18.3).'],
|
|
25
|
+
["const STALLFIX = 'openmayhem-stallfix-v1'", "const STALLFIX = 'openmayhem-stallfix-v2'"],
|
|
26
|
+
closedStreamGuard
|
|
27
|
+
],
|
|
28
|
+
edits: [
|
|
29
|
+
// 1. Constants + marker.
|
|
30
|
+
[
|
|
31
|
+
String.raw`const MAX_BACKOFFS = MAX_INVALID_REQUESTS / 2
|
|
32
|
+
`,
|
|
33
|
+
String.raw`const MAX_BACKOFFS = MAX_INVALID_REQUESTS / 2
|
|
34
|
+
|
|
35
|
+
// OPENMAYHEM-STALLFIX v2 (hypercore 11.18.3). Install-time patch shipped by trac-msb and trac-peer
|
|
36
|
+
// (scripts/hypercore-stallfix/). Fixes the waiting-side replication stall: an in-flight request that a peer
|
|
37
|
+
// never answers is cancelled after STALLFIX_REQUEST_MAX_AGE and asked elsewhere; that peer gets a cool-down;
|
|
38
|
+
// a peer we paused gets a capped NOT_AVAILABLE instead of silence; an INVALID_OPERATION verification error pauses
|
|
39
|
+
// a peer for an escalating time instead of until disconnect. Wire format unchanged.
|
|
40
|
+
const STALLFIX = 'openmayhem-stallfix-v2'
|
|
41
|
+
const STALLFIX_REQUEST_MAX_AGE = 30000 // ms an in-flight request may wait for its answer
|
|
42
|
+
const STALLFIX_SWEEP_INTERVAL = 5000 // one unref'd timer per replicator, only while requests are in flight
|
|
43
|
+
const STALLFIX_MAX_SCAN = 4096 // request slots looked at per sweep
|
|
44
|
+
const STALLFIX_MAX_EXPIRE = 64 // requests expired per sweep
|
|
45
|
+
const STALLFIX_COOLDOWN_MIN = 30000 // first cool-down of a peer that let a request expire (doubles, capped)
|
|
46
|
+
const STALLFIX_COOLDOWN_MAX = 240000
|
|
47
|
+
const STALLFIX_NOT_AVAILABLE_WINDOW = 60000
|
|
48
|
+
const STALLFIX_NOT_AVAILABLE_MAX = 16 // NOT_AVAILABLE replies per window to a peer we paused; silent beyond
|
|
49
|
+
const STALLFIX_OP_PAUSE_MIN = 60000 // first timed pause after an INVALID_OPERATION verification error (doubles)
|
|
50
|
+
const STALLFIX_OP_PAUSE_EPISODES = 5 // timed pauses without valid data in between; the next one is permanent
|
|
51
|
+
`
|
|
52
|
+
],
|
|
53
|
+
// 2. Per-peer state.
|
|
54
|
+
[
|
|
55
|
+
String.raw` this.lastExtensionRecv = ''
|
|
56
|
+
|
|
57
|
+
replicator._ifAvailable++
|
|
58
|
+
replicator._active++
|
|
59
|
+
}
|
|
60
|
+
`,
|
|
61
|
+
String.raw` this.lastExtensionRecv = ''
|
|
62
|
+
|
|
63
|
+
this._sfTimeouts = 0 // OPENMAYHEM-STALLFIX: cool-down level of this peer
|
|
64
|
+
this._sfCoolUntil = 0
|
|
65
|
+
this._sfNaWindow = 0
|
|
66
|
+
this._sfNaCount = 0
|
|
67
|
+
this._sfOpUntil = 0 // timed pause after INVALID_OPERATION (0 = none)
|
|
68
|
+
this._sfOpLevel = 0
|
|
69
|
+
|
|
70
|
+
replicator._ifAvailable++
|
|
71
|
+
replicator._active++
|
|
72
|
+
}
|
|
73
|
+
`
|
|
74
|
+
],
|
|
75
|
+
// 3. Responder: reply NOT_AVAILABLE (capped) to a peer we paused instead of dropping the request silently.
|
|
76
|
+
// Also covers proof === null for an inactive peer (upstream 03f7156: that case threw a TypeError here).
|
|
77
|
+
[
|
|
78
|
+
String.raw` if (!this.isActive() && proof.block !== null) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
`,
|
|
82
|
+
String.raw` if (!this.isActive() && (proof === null || proof.block !== null)) {
|
|
83
|
+
// OPENMAYHEM-STALLFIX: upstream returns silently here and the remote waits forever. Never INVALID_REQUEST.
|
|
84
|
+
this._sfNotAvailable(req.msg.id)
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
],
|
|
89
|
+
// 3b. INVALID_OPERATION during verification: timed pause instead of paused until disconnect. On 11.18.3 this
|
|
90
|
+
// error is also raised for VALID replies whose upgrade starts at a length we no longer have (local commit
|
|
91
|
+
// or another upgrade moved our length while the request was in flight; upstream 11.40.0 #895 refits such
|
|
92
|
+
// proofs). Other verification errors (possible forks, bad data) keep the conflict check and the permanent pause.
|
|
93
|
+
[
|
|
94
|
+
String.raw` if (err.code !== 'INVALID_OPERATION') {
|
|
95
|
+
// might be a fork, verify
|
|
96
|
+
this._checkIfConflict()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.paused = true
|
|
100
|
+
this.replicator._onnodata(this, req, 0)
|
|
101
|
+
`,
|
|
102
|
+
String.raw` if (err.code !== 'INVALID_OPERATION') {
|
|
103
|
+
// might be a fork, verify
|
|
104
|
+
this._checkIfConflict()
|
|
105
|
+
this.paused = true
|
|
106
|
+
} else {
|
|
107
|
+
// OPENMAYHEM-STALLFIX: never a fork (no conflict check); pause for a bounded, escalating time
|
|
108
|
+
this._sfInvalidOperationPause(Date.now())
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.replicator._onnodata(this, req, 0)
|
|
112
|
+
`
|
|
113
|
+
],
|
|
114
|
+
// 3c. A timed pause makes the peer inactive like a pause, until it expires.
|
|
115
|
+
[
|
|
116
|
+
String.raw` isActive() {
|
|
117
|
+
if (this.paused || this.removed || this.core.header.frozen) return false
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
120
|
+
`,
|
|
121
|
+
String.raw` isActive() {
|
|
122
|
+
if (this.paused || this.removed || this.core.header.frozen) return false
|
|
123
|
+
if (this._sfOpUntil !== 0 && this._sfOpUntil > Date.now()) return false // OPENMAYHEM-STALLFIX timed pause
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
`
|
|
127
|
+
],
|
|
128
|
+
// 4. Peer helpers: capped NOT_AVAILABLE and request expiry.
|
|
129
|
+
[
|
|
130
|
+
String.raw` _cancelRequest(req) {
|
|
131
|
+
if (req.priority === PRIORITY.CANCELLED) return
|
|
132
|
+
`,
|
|
133
|
+
String.raw` // OPENMAYHEM-STALLFIX: at most STALLFIX_NOT_AVAILABLE_MAX replies per window, silent beyond the cap.
|
|
134
|
+
_sfNotAvailable(id) {
|
|
135
|
+
if (this.removed === true) return
|
|
136
|
+
const now = Date.now()
|
|
137
|
+
if (now - this._sfNaWindow >= STALLFIX_NOT_AVAILABLE_WINDOW) {
|
|
138
|
+
this._sfNaWindow = now
|
|
139
|
+
this._sfNaCount = 0
|
|
140
|
+
}
|
|
141
|
+
if (this._sfNaCount >= STALLFIX_NOT_AVAILABLE_MAX) {
|
|
142
|
+
this.replicator._sfStats.notAvailableSuppressed++
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
this._sfNaCount++
|
|
146
|
+
this.replicator._sfStats.notAvailableSent++
|
|
147
|
+
this.wireNoData.send({ request: id, reason: NOT_AVAILABLE })
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// OPENMAYHEM-STALLFIX: timed pause after INVALID_OPERATION. Replies already in flight do not extend it.
|
|
151
|
+
// Episodes double (60 s .. 16 min); valid block data from the peer resets the level (_ondata); after
|
|
152
|
+
// STALLFIX_OP_PAUSE_EPISODES episodes without valid data the peer is paused until disconnect, as upstream.
|
|
153
|
+
_sfInvalidOperationPause(now) {
|
|
154
|
+
if (this.paused === true || this._sfOpUntil > now) return
|
|
155
|
+
if (this._sfOpLevel >= STALLFIX_OP_PAUSE_EPISODES) {
|
|
156
|
+
this._sfOpUntil = 0
|
|
157
|
+
this.paused = true
|
|
158
|
+
this.replicator._sfStats.invalidOperationPermanent++
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
this._sfOpLevel++
|
|
162
|
+
this._sfOpUntil = now + this.replicator._sfOpPauseMin * 2 ** (this._sfOpLevel - 1)
|
|
163
|
+
this.replicator._sfStats.invalidOperationPauses++
|
|
164
|
+
this.replicator._sfArm()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// OPENMAYHEM-STALLFIX: give up on one request this peer did not answer in time. Bookkeeping as for a
|
|
168
|
+
// NOT_AVAILABLE reply (_clearRequest re-queues a wanted block) plus a wire cancel. The id is only reused
|
|
169
|
+
// after the next roundtrip (as in _cancelRequest), so a late reply finds no request and is dropped
|
|
170
|
+
// in ondata/onnodata: it is not invalid data and does not pause the peer.
|
|
171
|
+
_sfExpireRequest(req, now) {
|
|
172
|
+
if (req.priority === PRIORITY.CANCELLED) return false
|
|
173
|
+
req.priority = PRIORITY.CANCELLED
|
|
174
|
+
|
|
175
|
+
this.inflight--
|
|
176
|
+
this.replicator._requestDone(req.id, false)
|
|
177
|
+
|
|
178
|
+
if (this.roundtripQueue === null) this.roundtripQueue = new RoundtripQueue()
|
|
179
|
+
this.roundtripQueue.add(req.id)
|
|
180
|
+
if (this.removed === false) {
|
|
181
|
+
this.wireCancel.send({ request: req.id })
|
|
182
|
+
incrementTx(this.stats.wireCancel, this.replicator.stats.wireCancel)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (this._sfCoolUntil <= now) {
|
|
186
|
+
if (this._sfTimeouts < 8) this._sfTimeouts++
|
|
187
|
+
const cooldown = this.replicator._sfCooldownMin * 2 ** (this._sfTimeouts - 1)
|
|
188
|
+
this._sfCoolUntil = now + Math.min(STALLFIX_COOLDOWN_MAX, cooldown)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this.replicator._manifestPeer === this && this.core.header.manifest === null) {
|
|
192
|
+
this.replicator._manifestPeer = null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.replicator._clearRequest(this, req)
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_cancelRequest(req) {
|
|
200
|
+
if (req.priority === PRIORITY.CANCELLED) return
|
|
201
|
+
`
|
|
202
|
+
],
|
|
203
|
+
// 5. Arm the sweep when a request goes in flight.
|
|
204
|
+
[
|
|
205
|
+
String.raw` this.inflight++
|
|
206
|
+
this.replicator._inflight.add(req)
|
|
207
|
+
`,
|
|
208
|
+
String.raw` this.inflight++
|
|
209
|
+
this.replicator._inflight.add(req)
|
|
210
|
+
if (this.replicator._sfTimer === null) this.replicator._sfArm() // OPENMAYHEM-STALLFIX
|
|
211
|
+
`
|
|
212
|
+
],
|
|
213
|
+
// 6. Marker on the Replicator class (runtime check: core.replicator.constructor.STALLFIX).
|
|
214
|
+
[
|
|
215
|
+
String.raw` static Peer = Peer // hack to be able to access Peer from outside this module
|
|
216
|
+
`,
|
|
217
|
+
String.raw` static Peer = Peer // hack to be able to access Peer from outside this module
|
|
218
|
+
static STALLFIX = STALLFIX // OPENMAYHEM-STALLFIX marker
|
|
219
|
+
`
|
|
220
|
+
],
|
|
221
|
+
// 7. Replicator state.
|
|
222
|
+
[
|
|
223
|
+
String.raw` this._notDownloadingLinger = notDownloadingLinger
|
|
224
|
+
this._downloadingTimer = null
|
|
225
|
+
`,
|
|
226
|
+
String.raw` this._notDownloadingLinger = notDownloadingLinger
|
|
227
|
+
this._downloadingTimer = null
|
|
228
|
+
|
|
229
|
+
// OPENMAYHEM-STALLFIX state (the _sf* numbers are only changed by tests)
|
|
230
|
+
this._sfTimer = null
|
|
231
|
+
this._sfCursor = 0
|
|
232
|
+
this._sfAllCooling = false
|
|
233
|
+
this._sfMaxAge = STALLFIX_REQUEST_MAX_AGE
|
|
234
|
+
this._sfInterval = STALLFIX_SWEEP_INTERVAL
|
|
235
|
+
this._sfCooldownMin = STALLFIX_COOLDOWN_MIN
|
|
236
|
+
this._sfOpPauseMin = STALLFIX_OP_PAUSE_MIN
|
|
237
|
+
this._sfStats = {
|
|
238
|
+
expired: 0,
|
|
239
|
+
notAvailableSent: 0,
|
|
240
|
+
notAvailableSuppressed: 0,
|
|
241
|
+
invalidOperationPauses: 0,
|
|
242
|
+
invalidOperationPermanent: 0
|
|
243
|
+
}
|
|
244
|
+
`
|
|
245
|
+
],
|
|
246
|
+
// 8. Valid block data from a peer ends its cool-down.
|
|
247
|
+
[
|
|
248
|
+
String.raw` _ondata(peer, req, data) {
|
|
249
|
+
req.elapsed = Date.now() - req.timestamp
|
|
250
|
+
if (data.block !== null) {
|
|
251
|
+
`,
|
|
252
|
+
String.raw` _ondata(peer, req, data) {
|
|
253
|
+
req.elapsed = Date.now() - req.timestamp
|
|
254
|
+
if (data.block !== null) {
|
|
255
|
+
peer._sfTimeouts = 0 // OPENMAYHEM-STALLFIX
|
|
256
|
+
peer._sfCoolUntil = 0
|
|
257
|
+
peer._sfOpLevel = 0
|
|
258
|
+
`
|
|
259
|
+
],
|
|
260
|
+
// 9. The sweep (bounded work, one timer per replicator).
|
|
261
|
+
[
|
|
262
|
+
String.raw` _maybeUpdate() {
|
|
263
|
+
return this._upgrade !== null && this._upgrade.inflight.length === 0
|
|
264
|
+
}
|
|
265
|
+
`,
|
|
266
|
+
String.raw` _maybeUpdate() {
|
|
267
|
+
return this._upgrade !== null && this._upgrade.inflight.length === 0
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// OPENMAYHEM-STALLFIX: one unref'd timer per replicator, armed by _send, re-armed only while requests are
|
|
271
|
+
// in flight or a peer is cooling down. Looks at <= STALLFIX_MAX_SCAN slots, expires <= STALLFIX_MAX_EXPIRE.
|
|
272
|
+
_sfArm() {
|
|
273
|
+
if (this.destroyed || this._sfTimer !== null) return
|
|
274
|
+
this._sfTimer = setTimeout(sfSweepLater, this._sfInterval, this)
|
|
275
|
+
if (this._sfTimer.unref) this._sfTimer.unref()
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_sfSweep() {
|
|
279
|
+
this._sfTimer = null
|
|
280
|
+
if (this.destroyed) return
|
|
281
|
+
|
|
282
|
+
const now = Date.now()
|
|
283
|
+
const reqs = this._inflight._requests
|
|
284
|
+
const scan = Math.min(reqs.length, STALLFIX_MAX_SCAN)
|
|
285
|
+
let live = 0
|
|
286
|
+
let expired = 0
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < scan && expired < STALLFIX_MAX_EXPIRE; i++) {
|
|
289
|
+
if (this._sfCursor >= reqs.length) this._sfCursor = 0
|
|
290
|
+
const req = reqs[this._sfCursor++]
|
|
291
|
+
if (!req) continue
|
|
292
|
+
live++
|
|
293
|
+
if (now - req.timestamp < this._sfMaxAge) continue
|
|
294
|
+
if (req.peer._sfExpireRequest(req, now) === true) expired++
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let cooling = 0
|
|
298
|
+
let usable = 0
|
|
299
|
+
let opPaused = 0
|
|
300
|
+
let cooled = false
|
|
301
|
+
for (const peer of this.peers) {
|
|
302
|
+
if (peer._sfCoolUntil !== 0 && peer._sfCoolUntil <= now) {
|
|
303
|
+
peer._sfCoolUntil = 0
|
|
304
|
+
cooled = true
|
|
305
|
+
}
|
|
306
|
+
if (peer._sfOpUntil !== 0) {
|
|
307
|
+
if (peer._sfOpUntil <= now) {
|
|
308
|
+
peer._sfOpUntil = 0
|
|
309
|
+
cooled = true
|
|
310
|
+
} else {
|
|
311
|
+
opPaused++
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (peer._sfCoolUntil !== 0) cooling++
|
|
315
|
+
else if (peer.isActive()) usable++
|
|
316
|
+
}
|
|
317
|
+
// if no other peer is usable, a cooling peer may still be asked (no deadlock with a single peer)
|
|
318
|
+
this._sfAllCooling = cooling > 0 && usable === 0
|
|
319
|
+
|
|
320
|
+
if (expired > 0) this._sfStats.expired += expired
|
|
321
|
+
if (expired > 0 || cooled) this.updateAll()
|
|
322
|
+
|
|
323
|
+
if (live > 0 || cooling > 0 || opPaused > 0 || scan < reqs.length) this._sfArm()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
_sfCoolingDown(peer) {
|
|
327
|
+
return peer._sfCoolUntil !== 0 && this._sfAllCooling === false && peer._sfCoolUntil > Date.now()
|
|
328
|
+
}
|
|
329
|
+
`
|
|
330
|
+
],
|
|
331
|
+
// 10. Hotswap does not pick a cooling peer.
|
|
332
|
+
[
|
|
333
|
+
String.raw` if (!peer.isActive() || peer.inflight >= maxHotswaps) return
|
|
334
|
+
`,
|
|
335
|
+
String.raw` if (!peer.isActive() || peer.inflight >= maxHotswaps || this._sfCoolingDown(peer)) return
|
|
336
|
+
`
|
|
337
|
+
],
|
|
338
|
+
// 11. _updatePeer: skip a cooling peer + backport upstream 8cf8ad9 fix 2 (stale queue entry must not
|
|
339
|
+
// consume the peer's update).
|
|
340
|
+
[
|
|
341
|
+
String.raw` _updatePeer(peer) {
|
|
342
|
+
if (!peer.isActive() || peer.inflight >= peer.getMaxInflight() || !peer.remoteUploading) {
|
|
343
|
+
return false
|
|
344
|
+
}
|
|
345
|
+
`,
|
|
346
|
+
String.raw` _updatePeer(peer) {
|
|
347
|
+
if (
|
|
348
|
+
!peer.isActive() ||
|
|
349
|
+
peer.inflight >= peer.getMaxInflight() ||
|
|
350
|
+
!peer.remoteUploading ||
|
|
351
|
+
this._sfCoolingDown(peer)
|
|
352
|
+
) {
|
|
353
|
+
return false
|
|
354
|
+
}
|
|
355
|
+
`
|
|
356
|
+
],
|
|
357
|
+
[
|
|
358
|
+
String.raw` for (const b of blks) {
|
|
359
|
+
if (b.queued === false || peer._requestBlock(b) === true) {
|
|
360
|
+
b.queued = false
|
|
361
|
+
blks.dequeue()
|
|
362
|
+
return true
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
`,
|
|
366
|
+
String.raw` for (const b of blks) {
|
|
367
|
+
if (b.queued === false) {
|
|
368
|
+
// upstream 8cf8ad9 (#896) fix 2, backported by OPENMAYHEM-STALLFIX
|
|
369
|
+
blks.dequeue()
|
|
370
|
+
continue
|
|
371
|
+
}
|
|
372
|
+
if (peer._requestBlock(b) === true) {
|
|
373
|
+
b.queued = false
|
|
374
|
+
blks.dequeue()
|
|
375
|
+
return true
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
`
|
|
379
|
+
],
|
|
380
|
+
// 12. _updatePeerNonPrimary: skip a cooling peer.
|
|
381
|
+
[
|
|
382
|
+
String.raw` _updatePeerNonPrimary(peer) {
|
|
383
|
+
if (!peer.isActive() || peer.inflight >= peer.getMaxInflight() || !peer.remoteUploading) {
|
|
384
|
+
return false
|
|
385
|
+
}
|
|
386
|
+
`,
|
|
387
|
+
String.raw` _updatePeerNonPrimary(peer) {
|
|
388
|
+
if (
|
|
389
|
+
!peer.isActive() ||
|
|
390
|
+
peer.inflight >= peer.getMaxInflight() ||
|
|
391
|
+
!peer.remoteUploading ||
|
|
392
|
+
this._sfCoolingDown(peer)
|
|
393
|
+
) {
|
|
394
|
+
return false
|
|
395
|
+
}
|
|
396
|
+
`
|
|
397
|
+
],
|
|
398
|
+
// 13. destroy() clears the sweep timer.
|
|
399
|
+
[
|
|
400
|
+
String.raw` if (this._downloadingTimer) {
|
|
401
|
+
clearTimeout(this._downloadingTimer)
|
|
402
|
+
this._downloadingTimer = null
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
while (this.peers.length) {
|
|
406
|
+
`,
|
|
407
|
+
String.raw` if (this._downloadingTimer) {
|
|
408
|
+
clearTimeout(this._downloadingTimer)
|
|
409
|
+
this._downloadingTimer = null
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (this._sfTimer !== null) {
|
|
413
|
+
clearTimeout(this._sfTimer) // OPENMAYHEM-STALLFIX
|
|
414
|
+
this._sfTimer = null
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
while (this.peers.length) {
|
|
418
|
+
`
|
|
419
|
+
],
|
|
420
|
+
// 14. Timer trampoline.
|
|
421
|
+
[
|
|
422
|
+
String.raw`function setDownloadingLater(repl, downloading, session) {
|
|
423
|
+
`,
|
|
424
|
+
String.raw`function sfSweepLater(repl) {
|
|
425
|
+
repl._sfSweep() // OPENMAYHEM-STALLFIX
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function setDownloadingLater(repl, downloading, session) {
|
|
429
|
+
`
|
|
430
|
+
],
|
|
431
|
+
// Upstream #911, in addition to the existing stall fixes.
|
|
432
|
+
closedStreamGuard
|
|
433
|
+
]
|
|
434
|
+
}
|