@cryptotaxi247 / kubo / commits / 097d40ba7

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 }