Skip to content

Commit 68a8433

Browse files
authored
Ignore records older than TOMBSTONE_LIFESPAN (#71)
1 parent 1e203e5 commit 68a8433

File tree

5 files changed

+76
-5
lines changed

5 files changed

+76
-5
lines changed

catalog/services_state.go

+11
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,17 @@ func (state *ServicesState) AddServiceEntry(newSvc service.Service) {
296296
state.Lock()
297297
defer state.Unlock()
298298

299+
// Some weird edge cases can cause very old stuff to get broadcast. This
300+
// can end up in a broadcast/tombstone/broadcast loop. We'll attempt to
301+
// prevent that by dropping anything older than the tombstone window.
302+
if newSvc.IsStale(TOMBSTONE_LIFESPAN) {
303+
log.Warnf(
304+
"Dropping stale service received on gossip: %s:%s (%s)",
305+
newSvc.Hostname, newSvc.Name, newSvc.ID,
306+
)
307+
return
308+
}
309+
299310
if !state.HasServer(newSvc.Hostname) {
300311
state.Servers[newSvc.Hostname] = NewServer(newSvc.Hostname)
301312
}

catalog/services_state_test.go

+34-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7+
"os"
78
"regexp"
89
"sync"
910
"testing"
1011
"time"
1112

1213
"github.com/NinesStack/sidecar/service"
1314
"github.com/relistan/go-director"
15+
log "github.com/sirupsen/logrus"
1416
. "github.com/smartystreets/goconvey/convey"
1517
)
1618

@@ -130,7 +132,7 @@ func Test_ServicesStateWithData(t *testing.T) {
130132
So(state.Servers[anotherHostname].Services[svc.ID], ShouldNotBeNil)
131133
})
132134

133-
Convey("Doesn't merge a stale service", func() {
135+
Convey("Doesn't merge an update that is older than what we have", func() {
134136
state.AddServiceEntry(svc)
135137

136138
staleService := service.Service{
@@ -152,6 +154,26 @@ func Test_ServicesStateWithData(t *testing.T) {
152154
ShouldEqual, "101deadbeef")
153155
})
154156

157+
Convey("Doesn't merge an update that is past the tombstone lifespan", func() {
158+
staleService := service.Service{
159+
ID: "deadbeef123",
160+
Name: "stale_service",
161+
Image: "stale",
162+
Created: baseTime,
163+
Hostname: anotherHostname,
164+
Updated: baseTime.Add(0 - 1*time.Minute).Add(0 - TOMBSTONE_LIFESPAN),
165+
Status: service.ALIVE,
166+
}
167+
168+
capture := LogCapture(func() {
169+
state.AddServiceEntry(staleService)
170+
})
171+
172+
_, ok := state.Servers[anotherHostname]
173+
So(ok, ShouldBeFalse)
174+
So(capture, ShouldContainSubstring, "Dropping stale service received on gossip")
175+
})
176+
155177
Convey("Updates the LastUpdated time for the server", func() {
156178
newDate := svc.Updated.AddDate(0, 0, 5)
157179
svc.Updated = newDate
@@ -723,7 +745,7 @@ func Test_ClusterMembershipManagement(t *testing.T) {
723745

724746
func Test_DecodeStream(t *testing.T) {
725747
Convey("Test decoding stream", t, func() {
726-
serv := service.Service{ID: "007", Name: "api", Hostname: "some-aws-host", Status: 1}
748+
serv := service.Service{ID: "007", Name: "api", Hostname: "some-aws-host", Status: 1, Updated: time.Now().UTC()}
727749
state := NewServicesState()
728750
state.AddServiceEntry(serv)
729751

@@ -841,3 +863,13 @@ func ShouldMatch(actual interface{}, expected ...interface{}) string {
841863

842864
return ""
843865
}
866+
867+
// LogCapture logs for async testing where we can't get a nice handle on thigns
868+
func LogCapture(fn func()) string {
869+
capture := &bytes.Buffer{}
870+
log.SetOutput(capture)
871+
fn()
872+
log.SetOutput(os.Stdout)
873+
874+
return capture.String()
875+
}

catalog/url_listener_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"net/http"
55
"net/url"
66
"testing"
7+
"time"
78

89
"github.com/NinesStack/sidecar/service"
910
"github.com/relistan/go-director"
@@ -65,7 +66,7 @@ func Test_Listen(t *testing.T) {
6566
hostname := "grendel"
6667

6768
svcId1 := "deadbeef123"
68-
service1 := service.Service{ID: svcId1, Hostname: hostname}
69+
service1 := service.Service{ID: svcId1, Hostname: hostname, Updated: time.Now().UTC()}
6970

7071
state := NewServicesState()
7172
state.Hostname = hostname

service/service.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/NinesStack/sidecar/output"
13-
"github.com/fsouza/go-dockerclient"
13+
docker "github.com/fsouza/go-dockerclient"
1414
log "github.com/sirupsen/logrus"
1515
)
1616

@@ -65,6 +65,12 @@ func (svc *Service) Invalidates(otherSvc *Service) bool {
6565
return otherSvc != nil && svc.Updated.After(otherSvc.Updated)
6666
}
6767

68+
func (svc *Service) IsStale(lifespan time.Duration) bool {
69+
oldestAllowed := time.Now().UTC().Add(0 - lifespan)
70+
// We add a fudge factor for clock drift
71+
return svc.Updated.Before(oldestAllowed.Add(0 - 1*time.Minute))
72+
}
73+
6874
func (svc *Service) Format() string {
6975
var ports []string
7076
for _, port := range svc.Ports {

service/service_test.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package service
33
import (
44
"os"
55
"testing"
6+
"time"
67

7-
"github.com/fsouza/go-dockerclient"
8+
docker "github.com/fsouza/go-dockerclient"
89
. "github.com/smartystreets/goconvey/convey"
910
)
1011

@@ -137,3 +138,23 @@ func Test_ToService(t *testing.T) {
137138
})
138139
})
139140
}
141+
142+
func Test_IsStale(t *testing.T) {
143+
Convey("IsStale()", t, func() {
144+
Convey("identifies records that are too old to process", func() {
145+
lifespan := 1 * time.Hour
146+
lastUpdated := time.Now().UTC().Add(0-lifespan).Add(0-2 * time.Minute)
147+
148+
svc := &Service{
149+
Name: "hrunting",
150+
Updated: lastUpdated,
151+
Hostname: "beowulf",
152+
}
153+
154+
So(svc.IsStale(lifespan), ShouldBeTrue)
155+
156+
svc.Updated = time.Now().UTC().Add(0-lifespan)
157+
So(svc.IsStale(62*time.Minute), ShouldBeFalse)
158+
})
159+
})
160+
}

0 commit comments

Comments
 (0)