rewrite of backoff mechanism
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Nov 5, 2015 at 13:36 UTC
097d40ba74e5794c75f02fb3b2ca52fc5408cb35
3 files changed
+46
-16
p2p/net/swarm/dial_test.go
+6
-6
@@ -420,18 +420,18 @@ func TestDialBackoffClears(t *testing.T) {
420
}
421
s1.peers.AddAddrs(s2.local, ifaceAddrs1, peer.PermanentAddrTTL)
422
423
- before = time.Now()
423
+ if _, err := s1.Dial(ctx, s2.local); err == nil {
424
+ t.Fatal("should have failed to dial backed off peer")
425
+ }
426
+
427
+ time.Sleep(baseBackoffTime)
428
+
429
if c, err := s1.Dial(ctx, s2.local); err != nil {
430
t.Fatal(err)
431
} else {
432
c.Close()
433
t.Log("correctly connected")
434
}
430
- duration = time.Now().Sub(before)
431
-
432
- if duration >= dt {
433
- // t.Error("took too long", duration, dt)
434
- }
435
436
if s1.backf.Backoff(s2.local) {
437
t.Error("s2 should no longer be on backoff")
p2p/net/swarm/swarm_dial.go
+37
-10
@@ -147,44 +147,71 @@ func (ds *dialsync) Unlock(dst peer.ID) {
147
// dialbackoff.Clear(p)
148
// }
149
//
150
+
151
type dialbackoff struct {
151
- entries map[peer.ID]struct{}
152
+ entries map[peer.ID]*backoffPeer
153
lock sync.RWMutex
154
}
155
156
+type backoffPeer struct {
157
+ tries int
158
+ until time.Time
159
+}
160
+
161
func (db *dialbackoff) init() {
162
if db.entries == nil {
157
- db.entries = make(map[peer.ID]struct{})
163
+ db.entries = make(map[peer.ID]*backoffPeer)
164
}
165
}
166
167
// Backoff returns whether the client should backoff from dialing
162
-// peeer p
163
-func (db *dialbackoff) Backoff(p peer.ID) bool {
168
+// peer p
169
+func (db *dialbackoff) Backoff(p peer.ID) (backoff bool) {
170
db.lock.Lock()
171
+ defer db.lock.Unlock()
172
db.init()
166
- _, found := db.entries[p]
167
- db.lock.Unlock()
168
- return found
173
+ bp, found := db.entries[p]
174
+ if found && time.Now().Before(bp.until) {
175
+ return true
176
+ }
177
+
178
+ return false
179
}
180
181
+const baseBackoffTime = time.Second * 5
182
+const maxBackoffTime = time.Minute * 5
183
+
184
// AddBackoff lets other nodes know that we've entered backoff with
185
// peer p, so dialers should not wait unnecessarily. We still will
186
// attempt to dial with one goroutine, in case we get through.
187
func (db *dialbackoff) AddBackoff(p peer.ID) {
188
db.lock.Lock()
189
+ defer db.lock.Unlock()
190
db.init()
177
- db.entries[p] = struct{}{}
178
- db.lock.Unlock()
191
+ bp, ok := db.entries[p]
192
+ if !ok {
193
+ db.entries[p] = &backoffPeer{
194
+ tries: 1,
195
+ until: time.Now().Add(baseBackoffTime),
196
+ }
197
+ return
198
+ }
199
+
200
+ expTimeAdd := time.Second * time.Duration(bp.tries*bp.tries)
201
+ if expTimeAdd > maxBackoffTime {
202
+ expTimeAdd = maxBackoffTime
203
+ }
204
+ bp.until = time.Now().Add(baseBackoffTime + expTimeAdd)
205
+ bp.tries++
206
}
207
208
// Clear removes a backoff record. Clients should call this after a
209
// successful Dial.
210
func (db *dialbackoff) Clear(p peer.ID) {
211
db.lock.Lock()
212
+ defer db.lock.Unlock()
213
db.init()
214
delete(db.entries, p)
187
- db.lock.Unlock()
215
}
216
217
// Dial connects to a peer.
p2p/net/swarm/swarm_listen.go
+3
@@ -138,5 +138,8 @@ func (s *Swarm) connHandler(c *ps.Conn) *Conn {
138
return nil
139
}
140
141
+ // if a peer dials us, remove from dial backoff.
142
+ s.backf.Clear(sc.RemotePeer())
143
+
144
return sc
145
}