Create SetMatrix data structure
SetMatrix is a simple matrix of sets. Added tests This data structure will be used in following commit to handle transient states where the same key can momentarely be associated to more than a value Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
This commit is contained in:
parent
fad71827be
commit
2111d4cafb
3 changed files with 270 additions and 0 deletions
|
@ -121,6 +121,7 @@ run-tests:
|
|||
|
||||
check-local: check-format check-code run-tests
|
||||
|
||||
|
||||
integration-tests: ./bin/dnet
|
||||
@./test/integration/dnet/run-integration-tests.sh
|
||||
|
||||
|
|
123
libnetwork/common/setmatrix.go
Normal file
123
libnetwork/common/setmatrix.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
)
|
||||
|
||||
// SetMatrix is a map of Sets
|
||||
type SetMatrix interface {
|
||||
// Get returns the members of the set for a specific key as a slice.
|
||||
Get(key string) ([]interface{}, bool)
|
||||
// Contains is used to verify is an element is in a set for a specific key
|
||||
// returns true if the element is in the set
|
||||
// returns true if there is a set for the key
|
||||
Contains(key string, value interface{}) (bool, bool)
|
||||
// Insert inserts the mapping between the IP and the endpoint identifier
|
||||
// returns true if the mapping was not present, false otherwise
|
||||
// returns also the number of endpoints associated to the IP
|
||||
Insert(key string, value interface{}) (bool, int)
|
||||
// Remove removes the mapping between the IP and the endpoint identifier
|
||||
// returns true if the mapping was deleted, false otherwise
|
||||
// returns also the number of endpoints associated to the IP
|
||||
Remove(key string, value interface{}) (bool, int)
|
||||
// Cardinality returns the number of elements in the set of a specfic key
|
||||
// returns false if the key is not in the map
|
||||
Cardinality(key string) (int, bool)
|
||||
// String returns the string version of the set, empty otherwise
|
||||
// returns false if the key is not in the map
|
||||
String(key string) (string, bool)
|
||||
}
|
||||
|
||||
type setMatrix struct {
|
||||
matrix map[string]mapset.Set
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewSetMatrix creates a new set matrix object
|
||||
func NewSetMatrix() SetMatrix {
|
||||
s := &setMatrix{}
|
||||
s.init()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *setMatrix) init() {
|
||||
s.matrix = make(map[string]mapset.Set)
|
||||
}
|
||||
|
||||
func (s *setMatrix) Get(key string) ([]interface{}, bool) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
return nil, ok
|
||||
}
|
||||
return set.ToSlice(), ok
|
||||
}
|
||||
|
||||
func (s *setMatrix) Contains(key string, value interface{}) (bool, bool) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
return false, ok
|
||||
}
|
||||
return set.Contains(value), ok
|
||||
}
|
||||
|
||||
func (s *setMatrix) Insert(key string, value interface{}) (bool, int) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
s.matrix[key] = mapset.NewSet()
|
||||
s.matrix[key].Add(value)
|
||||
return true, 1
|
||||
}
|
||||
|
||||
return set.Add(value), set.Cardinality()
|
||||
}
|
||||
|
||||
func (s *setMatrix) Remove(key string, value interface{}) (bool, int) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
return false, 0
|
||||
}
|
||||
|
||||
var removed bool
|
||||
if set.Contains(value) {
|
||||
set.Remove(value)
|
||||
removed = true
|
||||
// If the set is empty remove it from the matrix
|
||||
if set.Cardinality() == 0 {
|
||||
delete(s.matrix, key)
|
||||
}
|
||||
}
|
||||
|
||||
return removed, set.Cardinality()
|
||||
}
|
||||
|
||||
func (s *setMatrix) Cardinality(key string) (int, bool) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
return 0, ok
|
||||
}
|
||||
|
||||
return set.Cardinality(), ok
|
||||
}
|
||||
|
||||
func (s *setMatrix) String(key string) (string, bool) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
set, ok := s.matrix[key]
|
||||
if !ok {
|
||||
return "", ok
|
||||
}
|
||||
return set.String(), ok
|
||||
}
|
146
libnetwork/common/setmatrix_test.go
Normal file
146
libnetwork/common/setmatrix_test.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "github.com/docker/libnetwork/testutils"
|
||||
)
|
||||
|
||||
func TestSetSerialInsertDelete(t *testing.T) {
|
||||
s := NewSetMatrix()
|
||||
|
||||
b, i := s.Insert("a", "1")
|
||||
if !b || i != 1 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "1")
|
||||
if b || i != 1 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "2")
|
||||
if !b || i != 2 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "1")
|
||||
if b || i != 2 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "3")
|
||||
if !b || i != 3 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "2")
|
||||
if b || i != 3 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "3")
|
||||
if b || i != 3 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
b, i = s.Insert("a", "4")
|
||||
if !b || i != 4 {
|
||||
t.Fatalf("error in insert %t %d", b, i)
|
||||
}
|
||||
|
||||
b, p := s.Contains("a", "1")
|
||||
if !b || !p {
|
||||
t.Fatalf("error in contains %t %t", b, p)
|
||||
}
|
||||
b, p = s.Contains("a", "2")
|
||||
if !b || !p {
|
||||
t.Fatalf("error in contains %t %t", b, p)
|
||||
}
|
||||
b, p = s.Contains("a", "3")
|
||||
if !b || !p {
|
||||
t.Fatalf("error in contains %t %t", b, p)
|
||||
}
|
||||
b, p = s.Contains("a", "4")
|
||||
if !b || !p {
|
||||
t.Fatalf("error in contains %t %t", b, p)
|
||||
}
|
||||
|
||||
i, b = s.Cardinality("a")
|
||||
if !b || i != 4 {
|
||||
t.Fatalf("error in cardinality count %t %d", b, i)
|
||||
}
|
||||
|
||||
b, i = s.Remove("a", "1")
|
||||
if !b || i != 3 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
b, i = s.Remove("a", "3")
|
||||
if !b || i != 2 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
b, i = s.Remove("a", "1")
|
||||
if b || i != 2 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
b, i = s.Remove("a", "4")
|
||||
if !b || i != 1 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
b, i = s.Remove("a", "2")
|
||||
if !b || i != 0 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
b, i = s.Remove("a", "2")
|
||||
if b || i != 0 {
|
||||
t.Fatalf("error in remove %t %d", b, i)
|
||||
}
|
||||
|
||||
i, b = s.Cardinality("a")
|
||||
if b || i != 0 {
|
||||
t.Fatalf("error in cardinality count %t %d", b, i)
|
||||
}
|
||||
}
|
||||
|
||||
func insertDeleteRotuine(ctx context.Context, endCh chan int, s SetMatrix, key, value string) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
endCh <- 0
|
||||
return
|
||||
default:
|
||||
b, _ := s.Insert(key, value)
|
||||
if !b {
|
||||
endCh <- 1
|
||||
return
|
||||
}
|
||||
|
||||
b, _ = s.Remove(key, value)
|
||||
if !b {
|
||||
endCh <- 2
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetParallelInsertDelete(t *testing.T) {
|
||||
s := NewSetMatrix()
|
||||
parallelRoutines := 6
|
||||
endCh := make(chan int)
|
||||
// Let the routines running and competing for 10s
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
for i := 0; i < parallelRoutines; i++ {
|
||||
go insertDeleteRotuine(ctx, endCh, s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i))
|
||||
}
|
||||
for parallelRoutines > 0 {
|
||||
v := <-endCh
|
||||
if v == 1 {
|
||||
t.Fatalf("error one goroutine failed on the insert")
|
||||
}
|
||||
if v == 2 {
|
||||
t.Fatalf("error one goroutine failed on the remove")
|
||||
}
|
||||
parallelRoutines--
|
||||
}
|
||||
if i, b := s.Cardinality("key"); b || i > 0 {
|
||||
t.Fatalf("error the set should be empty %t %d", b, i)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue