Skip to content

Commit f96349e

Browse files
committed
Fix race with table init channel
The function to mark a table initialized wrongly closed the channel immediately. This could cause an observer to be woken up too early (before Commit() has finished) and get an uninitialized snapshot. Fix the timing issue by moving the closing into Commit(), next to the normal watch channel closing. This way the initialized table has been committed before the init watch channel closes. This issue was detected by `pkg/k8s/statedb_test.go` in cilium/cilium: statedb_test.go:263: Error Trace: /home/runner/work/cilium/cilium/pkg/k8s/statedb_test.go:263 /home/runner/work/cilium/cilium/pkg/k8s/statedb_test.go:120 Error: Not equal: expected: "obj1" actual : "garbage" Diff: --- Expected +++ Actual @@ -1 +1 @@ -obj1 +garbage Test: TestStateDBReflector/default Signed-off-by: Jussi Maki <[email protected]>
1 parent 574b83f commit f96349e

File tree

2 files changed

+14
-4
lines changed

2 files changed

+14
-4
lines changed

table.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,6 @@ func (t *genTable[Obj]) RegisterInitializer(txn WriteTxn, name string) func(Writ
216216
slices.Clone(table.pendingInitializers),
217217
func(n string) bool { return n == name },
218218
)
219-
if !table.initialized && len(table.pendingInitializers) == 0 {
220-
close(table.initWatchChan)
221-
table.initialized = true
222-
}
223219
}
224220
})
225221
}

txn.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,9 +459,18 @@ func (txn *txn) Commit() ReadTxn {
459459
root := *db.root.Load()
460460
root = slices.Clone(root)
461461

462+
var initChansToClose []chan struct{}
463+
462464
// Insert the modified tables into the root tree of tables.
463465
for pos, table := range txn.modifiedTables {
464466
if table != nil {
467+
// Check if tables become initialized. We close the channel only after
468+
// we've swapped in the new root so that one cannot get a snapshot of
469+
// an uninitialized table after observing the channel closing.
470+
if !table.initialized && len(table.pendingInitializers) == 0 {
471+
initChansToClose = append(initChansToClose, table.initWatchChan)
472+
table.initialized = true
473+
}
465474
root[pos] = *table
466475
}
467476
}
@@ -480,6 +489,11 @@ func (txn *txn) Commit() ReadTxn {
480489
txn.Notify()
481490
}
482491

492+
// Notify table initializations
493+
for _, ch := range initChansToClose {
494+
close(ch)
495+
}
496+
483497
txn.db.metrics.WriteTxnDuration(
484498
txn.handle,
485499
txn.tableNames,

0 commit comments

Comments
 (0)