Skip to content

Commit ec65f2d

Browse files
committed
libn/networkdb: fix data race in GetTableByNetwork
The function was accessing the index map without holding the mutex, so it would race any mutation to the database indexes. Fetch the reference to the tree's root while holding a read lock. Since the radix tree is immutable, taking a reference to the root is equivalent to starting a read-only database transaction, providing a consistent view of the data at a snapshot in time, even as the live state is mutated concurrently. Also optimize the WalkTable function by leveraging the immutability of the radix tree. Signed-off-by: Cory Snider <[email protected]>
1 parent d71afd7 commit ec65f2d

2 files changed

Lines changed: 25 additions & 13 deletions

File tree

libnetwork/networkdb/networkdb.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,11 @@ type TableElem struct {
429429
// GetTableByNetwork walks the networkdb by the give table and network id and
430430
// returns a map of keys and values
431431
func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]*TableElem {
432+
nDB.RLock()
433+
root := nDB.indexes[byNetwork].Root()
434+
nDB.RUnlock()
432435
entries := make(map[string]*TableElem)
433-
nDB.indexes[byTable].Root().WalkPrefix([]byte(fmt.Sprintf("/%s/%s", tname, nid)), func(k []byte, v *entry) bool {
436+
root.WalkPrefix([]byte(fmt.Sprintf("/%s/%s", tname, nid)), func(k []byte, v *entry) bool {
434437
if v.deleting {
435438
return false
436439
}
@@ -585,21 +588,14 @@ func (nDB *NetworkDB) deleteNodeTableEntries(node string) {
585588
// value. The walk stops if the passed function returns a true.
586589
func (nDB *NetworkDB) WalkTable(tname string, fn func(string, string, []byte, bool) bool) error {
587590
nDB.RLock()
588-
values := make(map[string]*entry)
589-
nDB.indexes[byTable].Root().WalkPrefix([]byte("/"+tname), func(path []byte, v *entry) bool {
590-
values[string(path)] = v
591-
return false
592-
})
591+
root := nDB.indexes[byTable].Root()
593592
nDB.RUnlock()
594-
595-
for k, v := range values {
596-
params := strings.Split(k[1:], "/")
593+
root.WalkPrefix([]byte("/"+tname), func(path []byte, v *entry) bool {
594+
params := strings.Split(string(path[1:]), "/")
597595
nid := params[1]
598596
key := params[2]
599-
if fn(nid, key, v.value, v.deleting) {
600-
return nil
601-
}
602-
}
597+
return fn(nid, key, v.value, v.deleting)
598+
})
603599

604600
return nil
605601
}

libnetwork/networkdb/networkdb_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,22 @@ func TestNetworkDBCRUDMediumCluster(t *testing.T) {
428428

429429
dbs := createNetworkDBInstances(t, n, "node", DefaultConfig())
430430

431+
// Shake out any data races.
432+
done := make(chan struct{})
433+
defer close(done)
434+
for _, db := range dbs {
435+
go func(db *NetworkDB) {
436+
for {
437+
select {
438+
case <-done:
439+
return
440+
default:
441+
}
442+
_ = db.GetTableByNetwork("test_table", "network1")
443+
}
444+
}(db)
445+
}
446+
431447
for i := 0; i < n; i++ {
432448
for j := 0; j < n; j++ {
433449
if i == j {

0 commit comments

Comments
 (0)