Skip to content

Commit e172f4b

Browse files
author
Derek Collison
committed
Fixed issue #29 with subscriptions leaking across routes on auto-unsubscribe
1 parent cd56514 commit e172f4b

File tree

6 files changed

+93
-13
lines changed

6 files changed

+93
-13
lines changed

server/client.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,14 +538,22 @@ func (c *client) deliverMsg(sub *subscription, mh, msg []byte) {
538538
sub.nm++
539539
// Check if we should auto-unsubscribe.
540540
if sub.max > 0 {
541+
// For routing..
542+
shouldForward := client.typ != ROUTER && client.srv != nil
541543
// If we are at the exact number, unsubscribe but
542544
// still process the message in hand, otherwise
543545
// unsubscribe and drop message on the floor.
544546
if sub.nm == sub.max {
545547
defer client.unsubscribe(sub)
548+
if shouldForward {
549+
defer client.srv.broadcastUnSubscribe(sub)
550+
}
546551
} else if sub.nm > sub.max {
547552
client.mu.Unlock()
548553
client.unsubscribe(sub)
554+
if shouldForward {
555+
client.srv.broadcastUnSubscribe(sub)
556+
}
549557
return
550558
}
551559
}
@@ -873,7 +881,7 @@ func (c *client) closeConnection() {
873881
// we are already connected to the other end.
874882
if c.isSolicitedRoute() {
875883
rid := c.route.remoteID
876-
if rid != "" && c.srv.remotes[rid] != nil {
884+
if rid != "" && srv.remotes[rid] != nil {
877885
Debug("Not attempting reconnect for solicited route, already connected.", rid)
878886
return
879887
} else {

server/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88

99
const (
1010
// VERSION is the current version for the server.
11-
VERSION = "0.5.1"
11+
VERSION = "0.5.2"
1212

1313
// DEFAULT_PORT is the deault port for client connections.
1414
DEFAULT_PORT = 4222

server/route.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,11 @@ func (s *Server) broadcastSubscribe(sub *subscription) {
208208
func (s *Server) broadcastUnSubscribe(sub *subscription) {
209209
rsid := routeSid(sub)
210210
maxStr := _EMPTY_
211-
if sub.max > 0 {
212-
maxStr = fmt.Sprintf("%d ", sub.max)
211+
// Set max if we have it set and have not tripped auto-unsubscribe
212+
if sub.max > 0 && sub.nm < sub.max {
213+
maxStr = fmt.Sprintf(" %d", sub.max)
213214
}
214-
proto := fmt.Sprintf(unsubProto, maxStr, rsid)
215+
proto := fmt.Sprintf(unsubProto, rsid, maxStr)
215216
s.broadcastToRoutes(proto)
216217
}
217218

test/cluster_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,36 @@ func TestClusterDropsRemoteSids(t *testing.T) {
321321
t.Fatalf("Expected no subscriptions for srvB, got %d\n", sc)
322322
}
323323
}
324+
325+
// This will test that we drop remote sids correctly.
326+
func TestAutoUnsubscribePropogation(t *testing.T) {
327+
srvA, srvB, optsA, _ := runServers(t)
328+
defer srvA.Shutdown()
329+
defer srvB.Shutdown()
330+
331+
clientA := createClientConn(t, optsA.Host, optsA.Port)
332+
defer clientA.Close()
333+
334+
sendA, expectA := setupConn(t, clientA)
335+
expectMsgs := expectMsgsCommand(t, expectA)
336+
337+
// We will create subscriptions that will auto-unsubscribe and make sure
338+
// we are not accumulating orphan subscriptions on the other side.
339+
for i := 1; i <= 100; i++ {
340+
sub := fmt.Sprintf("SUB foo %d\r\n", i)
341+
auto := fmt.Sprintf("UNSUB %d 1\r\n", i)
342+
sendA(sub)
343+
sendA(auto)
344+
// This will trip the auto-unsubscribe
345+
sendA("PUB foo 2\r\nok\r\n")
346+
expectMsgs(1)
347+
}
348+
349+
sendA("PING\r\n")
350+
expectA(pongRe)
351+
352+
// Make sure number of subscriptions on B is correct
353+
if subs := srvB.NumSubscriptions(); subs != 0 {
354+
t.Fatalf("Expected no subscriptions on remote server, got %d\n", subs)
355+
}
356+
}

test/routes_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,38 @@ func TestRouteResendsLocalSubsOnReconnect(t *testing.T) {
446446
routeSend(infoJson)
447447
routeExpect(subRe)
448448
}
449+
450+
func TestAutoUnsubPropogation(t *testing.T) {
451+
s, opts := runRouteServer(t)
452+
defer s.Shutdown()
453+
454+
client := createClientConn(t, opts.Host, opts.Port)
455+
clientSend, clientExpect := setupConn(t, client)
456+
457+
route := createRouteConn(t, opts.ClusterHost, opts.ClusterPort)
458+
expectAuthRequired(t, route)
459+
_, routeExpect := setupRoute(t, route, opts)
460+
461+
// Setup a local subscription
462+
clientSend("SUB foo 2\r\n")
463+
clientSend("PING\r\n")
464+
clientExpect(pongRe)
465+
466+
routeExpect(subRe)
467+
468+
clientSend("UNSUB 2 1\r\n")
469+
clientSend("PING\r\n")
470+
clientExpect(pongRe)
471+
472+
routeExpect(unsubmaxRe)
473+
474+
clientSend("PUB foo 2\r\nok\r\n")
475+
clientSend("PING\r\n")
476+
clientExpect(pongRe)
477+
478+
clientSend("UNSUB 2\r\n")
479+
clientSend("PING\r\n")
480+
clientExpect(pongRe)
481+
482+
routeExpect(unsubnomaxRe)
483+
}

test/test.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,17 @@ func sendProto(t tLogger, c net.Conn, op string) {
255255
}
256256

257257
var (
258-
infoRe = regexp.MustCompile(`INFO\s+([^\r\n]+)\r\n`)
259-
pingRe = regexp.MustCompile(`PING\r\n`)
260-
pongRe = regexp.MustCompile(`PONG\r\n`)
261-
msgRe = regexp.MustCompile(`(?:(?:MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\s*\r\n([^\\r\\n]*?)\r\n)+?)`)
262-
okRe = regexp.MustCompile(`\A\+OK\r\n`)
263-
errRe = regexp.MustCompile(`\A\-ERR\s+([^\r\n]+)\r\n`)
264-
subRe = regexp.MustCompile(`SUB\s+([^\s]+)((\s+)([^\s]+))?\s+([^\s]+)\r\n`)
265-
unsubRe = regexp.MustCompile(`UNSUB\s+([^\s]+)(\s+(\d+))?\r\n`)
258+
infoRe = regexp.MustCompile(`INFO\s+([^\r\n]+)\r\n`)
259+
pingRe = regexp.MustCompile(`PING\r\n`)
260+
pongRe = regexp.MustCompile(`PONG\r\n`)
261+
msgRe = regexp.MustCompile(`(?:(?:MSG\s+([^\s]+)\s+([^\s]+)\s+(([^\s]+)[^\S\r\n]+)?(\d+)\s*\r\n([^\\r\\n]*?)\r\n)+?)`)
262+
okRe = regexp.MustCompile(`\A\+OK\r\n`)
263+
errRe = regexp.MustCompile(`\A\-ERR\s+([^\r\n]+)\r\n`)
264+
subRe = regexp.MustCompile(`SUB\s+([^\s]+)((\s+)([^\s]+))?\s+([^\s]+)\r\n`)
265+
unsubRe = regexp.MustCompile(`UNSUB\s+([^\s]+)(\s+(\d+))?\r\n`)
266+
unsubmaxRe = regexp.MustCompile(`UNSUB\s+([^\s]+)(\s+(\d+))\r\n`)
267+
unsubnomaxRe = regexp.MustCompile(`UNSUB\s+([^\s]+)\r\n`)
268+
266269
connectRe = regexp.MustCompile(`CONNECT\s+([^\r\n]+)\r\n`)
267270
)
268271

0 commit comments

Comments
 (0)