Browse Source

Merge pull request #2105 from abhi/bitseq

Fix for Duplicate IP issues
Flavio Crisciani 7 years ago
parent
commit
0890a1a95d

+ 49 - 24
libnetwork/bitseq/sequence.go

@@ -108,6 +108,12 @@ func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
 		bitSel >>= 1
 		bitSel >>= 1
 		bits++
 		bits++
 	}
 	}
+	// Check if the loop exited because it could not
+	// find any available bit int block  starting from
+	// "from". Return invalid pos in that case.
+	if bitSel == 0 {
+		return invalidPos, invalidPos, ErrNoBitAvailable
+	}
 	return bits / 8, bits % 8, nil
 	return bits / 8, bits % 8, nil
 }
 }
 
 
@@ -313,14 +319,14 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
 		curr := uint64(0)
 		curr := uint64(0)
 		h.Lock()
 		h.Lock()
 		store = h.store
 		store = h.store
-		h.Unlock()
 		if store != nil {
 		if store != nil {
+			h.Unlock() // The lock is acquired in the GetObject
 			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
 			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
 				return ret, err
 				return ret, err
 			}
 			}
+			h.Lock() // Acquire the lock back
 		}
 		}
-
-		h.Lock()
+		logrus.Debugf("Received set for ordinal %v, start %v, end %v, any %t, release %t, serial:%v curr:%d \n", ordinal, start, end, any, release, serial, h.curr)
 		if serial {
 		if serial {
 			curr = h.curr
 			curr = h.curr
 		}
 		}
@@ -346,7 +352,6 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
 
 
 		// Create a private copy of h and work on it
 		// Create a private copy of h and work on it
 		nh := h.getCopy()
 		nh := h.getCopy()
-		h.Unlock()
 
 
 		nh.head = pushReservation(bytePos, bitPos, nh.head, release)
 		nh.head = pushReservation(bytePos, bitPos, nh.head, release)
 		if release {
 		if release {
@@ -355,22 +360,25 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
 			nh.unselected--
 			nh.unselected--
 		}
 		}
 
 
-		// Attempt to write private copy to store
-		if err := nh.writeToStore(); err != nil {
-			if _, ok := err.(types.RetryError); !ok {
-				return ret, fmt.Errorf("internal failure while setting the bit: %v", err)
+		if h.store != nil {
+			h.Unlock()
+			// Attempt to write private copy to store
+			if err := nh.writeToStore(); err != nil {
+				if _, ok := err.(types.RetryError); !ok {
+					return ret, fmt.Errorf("internal failure while setting the bit: %v", err)
+				}
+				// Retry
+				continue
 			}
 			}
-			// Retry
-			continue
+			h.Lock()
 		}
 		}
 
 
 		// Previous atomic push was succesfull. Save private copy to local copy
 		// Previous atomic push was succesfull. Save private copy to local copy
-		h.Lock()
-		defer h.Unlock()
 		h.unselected = nh.unselected
 		h.unselected = nh.unselected
 		h.head = nh.head
 		h.head = nh.head
 		h.dbExists = nh.dbExists
 		h.dbExists = nh.dbExists
 		h.dbIndex = nh.dbIndex
 		h.dbIndex = nh.dbIndex
+		h.Unlock()
 		return ret, nil
 		return ret, nil
 	}
 	}
 }
 }
@@ -498,24 +506,40 @@ func (h *Handle) UnmarshalJSON(data []byte) error {
 func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
 func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
 	// Find sequence which contains the start bit
 	// Find sequence which contains the start bit
 	byteStart, bitStart := ordinalToPos(start)
 	byteStart, bitStart := ordinalToPos(start)
-	current, _, _, inBlockBytePos := findSequence(head, byteStart)
-
+	current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart)
 	// Derive the this sequence offsets
 	// Derive the this sequence offsets
 	byteOffset := byteStart - inBlockBytePos
 	byteOffset := byteStart - inBlockBytePos
 	bitOffset := inBlockBytePos*8 + bitStart
 	bitOffset := inBlockBytePos*8 + bitStart
-	var firstOffset uint64
-	if current == head {
-		firstOffset = byteOffset
-	}
 	for current != nil {
 	for current != nil {
 		if current.block != blockMAX {
 		if current.block != blockMAX {
+			// If the current block is not full, check if there is any bit
+			// from the current bit in the current block. If not, before proceeding to the
+			// next block node, make sure we check for available bit in the next
+			// instance of the same block. Due to RLE same block signature will be
+			// compressed.
+		retry:
 			bytePos, bitPos, err := current.getAvailableBit(bitOffset)
 			bytePos, bitPos, err := current.getAvailableBit(bitOffset)
+			if err != nil && precBlocks == current.count-1 {
+				// This is the last instance in the same block node,
+				// so move to the next block.
+				goto next
+			}
+			if err != nil {
+				// There are some more instances of the same block, so add the offset
+				// and be optimistic that you will find the available bit in the next
+				// instance of the same block.
+				bitOffset = 0
+				byteOffset += blockBytes
+				precBlocks++
+				goto retry
+			}
 			return byteOffset + bytePos, bitPos, err
 			return byteOffset + bytePos, bitPos, err
 		}
 		}
 		// Moving to next block: Reset bit offset.
 		// Moving to next block: Reset bit offset.
+	next:
 		bitOffset = 0
 		bitOffset = 0
-		byteOffset += (current.count * blockBytes) - firstOffset
-		firstOffset = 0
+		byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes)
+		precBlocks = 0
 		current = current.next
 		current = current.next
 	}
 	}
 	return invalidPos, invalidPos, ErrNoBitAvailable
 	return invalidPos, invalidPos, ErrNoBitAvailable
@@ -526,19 +550,20 @@ func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
 // This can be further optimized to check from start till curr in case of a rollover
 // This can be further optimized to check from start till curr in case of a rollover
 func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) {
 func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) {
 	var bytePos, bitPos uint64
 	var bytePos, bitPos uint64
+	var err error
 	if curr != 0 && curr > start {
 	if curr != 0 && curr > start {
-		bytePos, bitPos, _ = getFirstAvailable(head, curr)
+		bytePos, bitPos, err = getFirstAvailable(head, curr)
 		ret := posToOrdinal(bytePos, bitPos)
 		ret := posToOrdinal(bytePos, bitPos)
-		if end < ret {
+		if end < ret || err != nil {
 			goto begin
 			goto begin
 		}
 		}
 		return bytePos, bitPos, nil
 		return bytePos, bitPos, nil
 	}
 	}
 
 
 begin:
 begin:
-	bytePos, bitPos, _ = getFirstAvailable(head, start)
+	bytePos, bitPos, err = getFirstAvailable(head, start)
 	ret := posToOrdinal(bytePos, bitPos)
 	ret := posToOrdinal(bytePos, bitPos)
-	if end < ret {
+	if end < ret || err != nil {
 		return invalidPos, invalidPos, ErrNoBitAvailable
 		return invalidPos, invalidPos, ErrNoBitAvailable
 	}
 	}
 	return bytePos, bitPos, nil
 	return bytePos, bitPos, nil

+ 55 - 6
libnetwork/bitseq/sequence_test.go

@@ -192,6 +192,11 @@ func TestGetFirstAvailable(t *testing.T) {
 		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 7, 7, 0},
 		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 7, 7, 0},
 
 
 		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0, 0},
 		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0, 0},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 4, 0, 16},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 1, 7, 15},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 1, 6, 10},
+		{&sequence{block: 0xfffcfffe, count: 1, next: &sequence{block: 0x0, count: 6}}, 3, 7, 31},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0xffffffff, count: 6}}, invalidPos, invalidPos, 31},
 	}
 	}
 
 
 	for n, i := range input {
 	for n, i := range input {
@@ -1238,7 +1243,7 @@ func TestIsCorrupted(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestSetRollover(t *testing.T) {
+func testSetRollover(t *testing.T, serial bool) {
 	ds, err := randomLocalStore()
 	ds, err := randomLocalStore()
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
@@ -1253,7 +1258,7 @@ func TestSetRollover(t *testing.T) {
 
 
 	// Allocate first half of the bits
 	// Allocate first half of the bits
 	for i := 0; i < numBits/2; i++ {
 	for i := 0; i < numBits/2; i++ {
-		_, err := hnd.SetAny(true)
+		_, err := hnd.SetAny(serial)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
 			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
 		}
 		}
@@ -1276,12 +1281,12 @@ func TestSetRollover(t *testing.T) {
 		}
 		}
 	}
 	}
 	if hnd.Unselected() != uint64(3*numBits/4) {
 	if hnd.Unselected() != uint64(3*numBits/4) {
-		t.Fatalf("Expected full sequence. Instead found %d free bits.\nSeed: %d.\n%s", hnd.unselected, seed, hnd)
+		t.Fatalf("Unexpected free bits: found %d free bits.\nSeed: %d.\n%s", hnd.unselected, seed, hnd)
 	}
 	}
 
 
 	//request to allocate for remaining half of the bits
 	//request to allocate for remaining half of the bits
 	for i := 0; i < numBits/2; i++ {
 	for i := 0; i < numBits/2; i++ {
-		_, err := hnd.SetAny(true)
+		_, err := hnd.SetAny(serial)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
 			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
 		}
 		}
@@ -1294,7 +1299,7 @@ func TestSetRollover(t *testing.T) {
 	}
 	}
 
 
 	for i := 0; i < numBits/4; i++ {
 	for i := 0; i < numBits/4; i++ {
-		_, err := hnd.SetAny(true)
+		_, err := hnd.SetAny(serial)
 		if err != nil {
 		if err != nil {
 			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
 			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
 		}
 		}
@@ -1302,7 +1307,7 @@ func TestSetRollover(t *testing.T) {
 	//Now requesting to allocate the unallocated random bits (qurter of the number of bits) should
 	//Now requesting to allocate the unallocated random bits (qurter of the number of bits) should
 	//leave no more bits that can be allocated.
 	//leave no more bits that can be allocated.
 	if hnd.Unselected() != 0 {
 	if hnd.Unselected() != 0 {
-		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), numBits/4)
+		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), 0)
 	}
 	}
 
 
 	err = hnd.Destroy()
 	err = hnd.Destroy()
@@ -1310,3 +1315,47 @@ func TestSetRollover(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 }
 }
+
+func TestSetRollover(t *testing.T) {
+	testSetRollover(t, false)
+}
+
+func TestSetRolloverSerial(t *testing.T) {
+	testSetRollover(t, true)
+}
+
+func TestGetFirstAvailableFromCurrent(t *testing.T) {
+	input := []struct {
+		mask    *sequence
+		bytePos uint64
+		bitPos  uint64
+		start   uint64
+		curr    uint64
+		end     uint64
+	}{
+		{&sequence{block: 0xffffffff, count: 2048}, invalidPos, invalidPos, 0, 0, 65536},
+		{&sequence{block: 0x0, count: 8}, 0, 0, 0, 0, 256},
+		{&sequence{block: 0x80000000, count: 8}, 1, 0, 0, 8, 256},
+		{&sequence{block: 0xC0000000, count: 8}, 0, 2, 0, 2, 256},
+		{&sequence{block: 0xE0000000, count: 8}, 0, 3, 0, 0, 256},
+		{&sequence{block: 0xFFFB1FFF, count: 8}, 2, 0, 14, 0, 256},
+		{&sequence{block: 0xFFFFFFFE, count: 8}, 3, 7, 0, 0, 256},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 14}}}, 4, 0, 0, 32, 512},
+		{&sequence{block: 0xfffeffff, count: 1, next: &sequence{block: 0xffffffff, count: 15}}, 1, 7, 0, 16, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0xffffffff, count: 1}}, 5, 7, 0, 16, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0xffffffff, count: 1}}, 9, 7, 0, 48, 512},
+		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0xffffffef, count: 14}}, 19, 3, 0, 124, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0x0fffffff, count: 1}}, 60, 0, 0, 480, 512},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffeffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 17, 7, 0, 124, 512},
+		{&sequence{block: 0xfffffffb, count: 1, next: &sequence{block: 0xffffffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 3, 5, 0, 124, 512},
+		{&sequence{block: 0xfffffffb, count: 1, next: &sequence{block: 0xfffeffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 13, 7, 0, 80, 512},
+	}
+
+	for n, i := range input {
+		bytePos, bitPos, _ := getAvailableFromCurrent(i.mask, i.start, i.curr, i.end)
+		if bytePos != i.bytePos || bitPos != i.bitPos {
+			t.Fatalf("Error in (%d) getFirstAvailable(). Expected (%d, %d). Got (%d, %d)", n, i.bytePos, i.bitPos, bytePos, bitPos)
+		}
+	}
+}

+ 4 - 4
libnetwork/ipam/allocator.go

@@ -402,15 +402,15 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
 			continue
 			continue
 		}
 		}
 		aSpace.Lock()
 		aSpace.Lock()
-		_, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
-		aSpace.Unlock()
-		if ok {
+		if _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]; ok {
+			aSpace.Unlock()
 			continue
 			continue
 		}
 		}
-
 		if !aSpace.contains(as, nw) {
 		if !aSpace.contains(as, nw) {
+			aSpace.Unlock()
 			return nw, nil
 			return nw, nil
 		}
 		}
+		aSpace.Unlock()
 	}
 	}
 
 
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)
 	return nil, types.NotFoundErrorf("could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network", v)

+ 94 - 28
libnetwork/ipam/allocator_test.go

@@ -8,6 +8,7 @@ import (
 	"math/rand"
 	"math/rand"
 	"net"
 	"net"
 	"strconv"
 	"strconv"
+	"sync"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -30,7 +31,10 @@ func init() {
 }
 }
 
 
 // OptionBoltdbWithRandomDBFile function returns a random dir for local store backend
 // OptionBoltdbWithRandomDBFile function returns a random dir for local store backend
-func randomLocalStore() (datastore.DataStore, error) {
+func randomLocalStore(needStore bool) (datastore.DataStore, error) {
+	if !needStore {
+		return nil, nil
+	}
 	tmp, err := ioutil.TempFile("", "libnetwork-")
 	tmp, err := ioutil.TempFile("", "libnetwork-")
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Error creating temp file: %v", err)
 		return nil, fmt.Errorf("Error creating temp file: %v", err)
@@ -50,17 +54,13 @@ func randomLocalStore() (datastore.DataStore, error) {
 	})
 	})
 }
 }
 
 
-func getAllocator() (*Allocator, error) {
+func getAllocator(store bool) (*Allocator, error) {
 	ipamutils.InitNetworks(nil)
 	ipamutils.InitNetworks(nil)
-	ds, err := randomLocalStore()
-	if err != nil {
-		return nil, err
-	}
-	a, err := NewAllocator(ds, nil)
+	ds, err := randomLocalStore(store)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return a, nil
+	return NewAllocator(ds, nil)
 }
 }
 
 
 func TestInt2IP2IntConversion(t *testing.T) {
 func TestInt2IP2IntConversion(t *testing.T) {
@@ -170,7 +170,7 @@ func TestPoolDataMarshal(t *testing.T) {
 }
 }
 
 
 func TestSubnetsMarshal(t *testing.T) {
 func TestSubnetsMarshal(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -217,7 +217,7 @@ func TestSubnetsMarshal(t *testing.T) {
 }
 }
 
 
 func TestAddSubnets(t *testing.T) {
 func TestAddSubnets(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -286,7 +286,7 @@ func TestAddSubnets(t *testing.T) {
 func TestAddReleasePoolID(t *testing.T) {
 func TestAddReleasePoolID(t *testing.T) {
 	var k0, k1, k2 SubnetKey
 	var k0, k1, k2 SubnetKey
 
 
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -448,7 +448,7 @@ func TestAddReleasePoolID(t *testing.T) {
 }
 }
 
 
 func TestPredefinedPool(t *testing.T) {
 func TestPredefinedPool(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -476,7 +476,7 @@ func TestPredefinedPool(t *testing.T) {
 }
 }
 
 
 func TestRemoveSubnet(t *testing.T) {
 func TestRemoveSubnet(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -519,7 +519,7 @@ func TestRemoveSubnet(t *testing.T) {
 }
 }
 
 
 func TestGetSameAddress(t *testing.T) {
 func TestGetSameAddress(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -549,7 +549,7 @@ func TestGetSameAddress(t *testing.T) {
 }
 }
 
 
 func TestGetAddressSubPoolEqualPool(t *testing.T) {
 func TestGetAddressSubPoolEqualPool(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -566,7 +566,7 @@ func TestGetAddressSubPoolEqualPool(t *testing.T) {
 }
 }
 
 
 func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 func TestRequestReleaseAddressFromSubPool(t *testing.T) {
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -697,8 +697,9 @@ func TestRequestReleaseAddressFromSubPool(t *testing.T) {
 
 
 func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) {
 	opts := map[string]string{
 	opts := map[string]string{
-		ipamapi.AllocSerialPrefix: "true"}
-	a, err := getAllocator()
+		ipamapi.AllocSerialPrefix: "true",
+	}
+	a, err := getAllocator(false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -848,7 +849,7 @@ func TestRequestSyntaxCheck(t *testing.T) {
 		err     error
 		err     error
 	)
 	)
 
 
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -950,7 +951,7 @@ func TestRelease(t *testing.T) {
 		subnet = "192.168.0.0/23"
 		subnet = "192.168.0.0/23"
 	)
 	)
 
 
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1057,7 +1058,7 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
 	)
 	)
 
 
 	lastIP := net.ParseIP(lastExpectedIP)
 	lastIP := net.ParseIP(lastExpectedIP)
-	a, err := getAllocator()
+	a, err := getAllocator(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1088,24 +1089,24 @@ func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
 }
 }
 
 
 func benchMarkRequest(subnet string, b *testing.B) {
 func benchMarkRequest(subnet string, b *testing.B) {
-	a, _ := getAllocator()
+	a, _ := getAllocator(true)
 	for n := 0; n < b.N; n++ {
 	for n := 0; n < b.N; n++ {
 		benchmarkRequest(b, a, subnet)
 		benchmarkRequest(b, a, subnet)
 	}
 	}
 }
 }
 
 
 func BenchmarkRequest_24(b *testing.B) {
 func BenchmarkRequest_24(b *testing.B) {
-	a, _ := getAllocator()
+	a, _ := getAllocator(true)
 	benchmarkRequest(b, a, "10.0.0.0/24")
 	benchmarkRequest(b, a, "10.0.0.0/24")
 }
 }
 
 
 func BenchmarkRequest_16(b *testing.B) {
 func BenchmarkRequest_16(b *testing.B) {
-	a, _ := getAllocator()
+	a, _ := getAllocator(true)
 	benchmarkRequest(b, a, "10.0.0.0/16")
 	benchmarkRequest(b, a, "10.0.0.0/16")
 }
 }
 
 
 func BenchmarkRequest_8(b *testing.B) {
 func BenchmarkRequest_8(b *testing.B) {
-	a, _ := getAllocator()
+	a, _ := getAllocator(true)
 	benchmarkRequest(b, a, "10.0.0.0/8")
 	benchmarkRequest(b, a, "10.0.0.0/8")
 }
 }
 
 
@@ -1115,7 +1116,7 @@ func TestAllocateRandomDeallocate(t *testing.T) {
 }
 }
 
 
 func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int) {
 func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int) {
-	ds, err := randomLocalStore()
+	ds, err := randomLocalStore(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1184,7 +1185,7 @@ func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int) {
 
 
 func TestRetrieveFromStore(t *testing.T) {
 func TestRetrieveFromStore(t *testing.T) {
 	num := 200
 	num := 200
-	ds, err := randomLocalStore()
+	ds, err := randomLocalStore(true)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -1319,7 +1320,7 @@ func runParallelTests(t *testing.T, instance int) {
 	// The first instance creates the allocator, gives the start
 	// The first instance creates the allocator, gives the start
 	// and finally checks the pools each instance was assigned
 	// and finally checks the pools each instance was assigned
 	if instance == first {
 	if instance == first {
-		allocator, err = getAllocator()
+		allocator, err = getAllocator(true)
 		if err != nil {
 		if err != nil {
 			t.Fatal(err)
 			t.Fatal(err)
 		}
 		}
@@ -1362,6 +1363,71 @@ func runParallelTests(t *testing.T, instance int) {
 	}
 	}
 }
 }
 
 
+func TestRequestReleaseAddressDuplicate(t *testing.T) {
+	a, err := getAllocator(false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	type IP struct {
+		ip  *net.IPNet
+		ref int
+	}
+	ips := []IP{}
+	allocatedIPs := []*net.IPNet{}
+	a.addrSpaces["rosso"] = &addrSpace{
+		id:      dsConfigKey + "/" + "rosso",
+		ds:      a.addrSpaces[localAddressSpace].ds,
+		alloc:   a.addrSpaces[localAddressSpace].alloc,
+		scope:   a.addrSpaces[localAddressSpace].scope,
+		subnets: map[SubnetKey]*PoolData{},
+	}
+	var wg sync.WaitGroup
+	opts := map[string]string{
+		ipamapi.AllocSerialPrefix: "true",
+	}
+	var l sync.Mutex
+
+	poolID, _, _, err := a.RequestPool("rosso", "198.168.0.0/23", "", nil, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for err == nil {
+		var c *net.IPNet
+		if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil {
+			l.Lock()
+			ips = append(ips, IP{c, 1})
+			l.Unlock()
+			allocatedIPs = append(allocatedIPs, c)
+			if len(allocatedIPs) > 500 {
+				i := rand.Intn(len(allocatedIPs) - 1)
+				wg.Add(1)
+				go func(ip *net.IPNet) {
+					if err = a.ReleaseAddress(poolID, ip.IP); err != nil {
+						t.Fatal(err)
+					}
+					l.Lock()
+					ips = append(ips, IP{ip, -1})
+					l.Unlock()
+					wg.Done()
+				}(allocatedIPs[i])
+				allocatedIPs = append(allocatedIPs[:i], allocatedIPs[i+1:]...)
+			}
+		}
+	}
+	wg.Wait()
+	refMap := make(map[string]int)
+	for _, ip := range ips {
+		refMap[ip.ip.String()] = refMap[ip.ip.String()] + ip.ref
+		if refMap[ip.ip.String()] < 0 {
+			t.Fatalf("IP %s was previously released", ip.ip.String())
+		}
+		if refMap[ip.ip.String()] > 1 {
+			t.Fatalf("IP %s was previously allocated", ip.ip.String())
+		}
+	}
+}
+
 func TestParallelPredefinedRequest1(t *testing.T) {
 func TestParallelPredefinedRequest1(t *testing.T) {
 	runParallelTests(t, 0)
 	runParallelTests(t, 0)
 }
 }