diff --git a/.travis.yml b/.travis.yml index db09fe7..1c595a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: go go: - - tip - - 1.5 - - 1.4 + - 1.14.x services: - redis-server @@ -15,5 +13,4 @@ before_script: - sh -c "sleep 5" # give Redis a(n additional) chance to start script: - - go test -v ./... - + - go test -v -race ./... diff --git a/cluster/cluster_test.go b/cluster/cluster_test.go index 220e812..ac32116 100644 --- a/cluster/cluster_test.go +++ b/cluster/cluster_test.go @@ -390,7 +390,7 @@ func TestSelectRange(t *testing.T) { t.Fatalf("key %q: expected \n\t%+v, got \n\t%+v", e.Key, expected, got) } if _, ok := <-ch; ok { - t.Fatalf("key %q: expected 1 element on the channel, got multiple") + t.Fatalf("key %q: expected 1 element on the channel, got multiple", e.Key) } // Top of the list. @@ -414,7 +414,7 @@ func TestSelectRange(t *testing.T) { t.Fatalf("key %q: expected \n\t%+v, got \n\t%+v", e.Key, expected, got) } if _, ok := <-ch; ok { - t.Fatalf("key %q: expected 1 element on the channel, got multiple") + t.Fatalf("key %q: expected 1 element on the channel, got multiple", e.Key) } // Restricted limit. @@ -432,7 +432,7 @@ func TestSelectRange(t *testing.T) { t.Fatalf("key %q: expected \n\t%+v, got \n\t%+v", e.Key, expected, got) } if _, ok := <-ch; ok { - t.Fatalf("key %q: expected 1 element on the channel, got multiple") + t.Fatalf("key %q: expected 1 element on the channel, got multiple", e.Key) } // Multiple keys, top of the list, all elements. @@ -535,7 +535,7 @@ func TestSelectRange(t *testing.T) { t.Fatalf("key %q: expected \n\t%+v, got \n\t%+v", e.Key, expected, got) } if _, ok := <-ch; ok { - t.Fatalf("key %q: expected 1 element on the channel, got multiple") + t.Fatalf("key %q: expected 1 element on the channel, got multiple", e.Key) } // Middle of the list, using the stopcursor. @@ -552,7 +552,7 @@ func TestSelectRange(t *testing.T) { t.Fatalf("key %q: expected \n\t%+v, got \n\t%+v", e.Key, expected, got) } if _, ok := <-ch; ok { - t.Fatalf("key %q: expected 1 element on the channel, got multiple") + t.Fatalf("key %q: expected 1 element on the channel, got multiple", e.Key) } } diff --git a/farm/mock_cluster_test.go b/farm/mock_cluster_test.go index 4ee3e0c..ce6ca3d 100644 --- a/farm/mock_cluster_test.go +++ b/farm/mock_cluster_test.go @@ -4,6 +4,7 @@ import ( "errors" "reflect" "sort" + "sync" "sync/atomic" "testing" @@ -78,14 +79,16 @@ type mockCluster struct { countScore int32 countKeys int32 countOpenChannels int32 + mutex *sync.Mutex } var mockClusterIDs int32 func newMockCluster() *mockCluster { return &mockCluster{ - id: atomic.AddInt32(&mockClusterIDs, 1), - m: map[string]map[string]float64{}, + id: atomic.AddInt32(&mockClusterIDs, 1), + m: map[string]map[string]float64{}, + mutex: &sync.Mutex{}, } } @@ -93,10 +96,14 @@ func newFailingMockCluster() *mockCluster { return &mockCluster{ m: map[string]map[string]float64{}, failing: true, + mutex: &sync.Mutex{}, } } func (c *mockCluster) Insert(keyScoreMembers []common.KeyScoreMember) error { + c.mutex.Lock() + defer c.mutex.Unlock() + atomic.AddInt32(&c.countInsert, 1) if c.failing { return errors.New("failtown, population you") @@ -129,6 +136,9 @@ func (c *mockCluster) SelectOffset(keys []string, offset, limit int) <-chan clus } atomic.AddInt32(&c.countOpenChannels, 1) go func() { + c.mutex.Lock() + defer c.mutex.Unlock() + defer func() { close(ch) atomic.AddInt32(&c.countOpenChannels, -1) @@ -193,6 +203,9 @@ func (a scoreMemberSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a scoreMemberSlice) Less(i, j int) bool { return a[i].score > a[j].score } func (c *mockCluster) Delete(keyScoreMembers []common.KeyScoreMember) error { + c.mutex.Lock() + defer c.mutex.Unlock() + atomic.AddInt32(&c.countDelete, 1) if c.failing { return errors.New("failtown, population you") @@ -223,12 +236,16 @@ func (c *mockCluster) Delete(keyScoreMembers []common.KeyScoreMember) error { // Score in this mock implementation will never return a score for // deleted entries. func (c *mockCluster) Score(keyMembers []common.KeyMember) (map[common.KeyMember]cluster.Presence, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + atomic.AddInt32(&c.countScore, 1) if c.failing { return map[common.KeyMember]cluster.Presence{}, errors.New("failtown, population you") } m := map[common.KeyMember]cluster.Presence{} + for _, keyMember := range keyMembers { members, ok := c.m[keyMember.Key] if !ok { @@ -250,11 +267,15 @@ func (c *mockCluster) Score(keyMembers []common.KeyMember) (map[common.KeyMember } func (c *mockCluster) Keys(batchSize int) <-chan []string { + c.mutex.Lock() + defer c.mutex.Unlock() + atomic.AddInt32(&c.countKeys, 1) // Copy keys from c.m, so that at least after this method has returned, // we don't run into issues with concurrent modifications. a := make([]string, 0, len(c.m)) + for key := range c.m { a = append(a, key) } @@ -278,6 +299,9 @@ func (c *mockCluster) Keys(batchSize int) <-chan []string { } func (c *mockCluster) clear() { + c.mutex.Lock() + defer c.mutex.Unlock() + c.m = map[string]map[string]float64{} } diff --git a/farm/repair_strategies_test.go b/farm/repair_strategies_test.go index 0afdad2..be9469a 100644 --- a/farm/repair_strategies_test.go +++ b/farm/repair_strategies_test.go @@ -19,6 +19,7 @@ func TestAllRepairs(t *testing.T) { // Make inserts, no repair. first := common.KeyScoreMember{Key: "foo", Score: 1., Member: "bar"} second := common.KeyScoreMember{Key: "foo", Score: 2.34, Member: "bar"} + farm.Insert([]common.KeyScoreMember{first}) // perfect insert clusters[0].Insert([]common.KeyScoreMember{second}) // imperfect insert diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bbc20a8 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module github.com/soundcloud/roshi + +go 1.14 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/garyburd/redigo v1.6.0 + github.com/golang/protobuf v1.4.2 // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/mux v1.7.4 // indirect + github.com/gorilla/pat v1.0.1 + github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea + github.com/prometheus/client_golang v0.9.4 + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/procfs v0.1.3 // indirect + github.com/tsenart/tb v0.0.0-20181025101425-0d2499c8b6e9 + golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect +) + +replace github.com/soundcloud/roshi => ./ diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3990adc --- /dev/null +++ b/go.sum @@ -0,0 +1,88 @@ +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= +github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/pat v1.0.1 h1:OeSoj6sffw4/majibAY2BAUsXjNP7fEE+w30KickaL4= +github.com/gorilla/pat v1.0.1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea h1:sKwxy1H95npauwu8vtF95vG/syrL0p8fSZo/XlDg5gk= +github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A= +github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tsenart/tb v0.0.0-20181025101425-0d2499c8b6e9 h1:kjbwitOGH46vD01f2s3leBfrMnePQa3NSAIlW35MvY8= +github.com/tsenart/tb v0.0.0-20181025101425-0d2499c8b6e9/go.mod h1:EcGP24b8DY+bWHnpfJDP7fM+o8Nmz4fYH0l2xTtNr3I= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=