Skip to content

Commit 5a03cdf

Browse files
fix: race in block-stm executor loop dropping assigned work (#26055)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent f988b49 commit 5a03cdf

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

internal/blockstm/executor.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ func NewExecutor(
4343
func (e *Executor) Run() error {
4444
var kind TaskKind
4545
version := InvalidTxnVersion
46-
for !e.scheduler.Done() {
46+
for {
4747
if !version.Valid() {
48+
if e.scheduler.Done() {
49+
return nil
50+
}
4851
// check for cancellation
4952
select {
5053
case <-e.ctx.Done():
@@ -65,7 +68,6 @@ func (e *Executor) Run() error {
6568
return fmt.Errorf("unknown task kind %v", kind)
6669
}
6770
}
68-
return nil
6971
}
7072

7173
func (e *Executor) TryExecute(version TxnVersion) (TxnVersion, TaskKind) {

internal/blockstm/stm_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,45 @@ func TestSTM(t *testing.T) {
184184
}
185185
}
186186

187+
// TestSTMHighContentionStress runs high-contention blocks many times.
188+
// With count=1, TestSTM passes because the bug is probabilistic.
189+
// With 200 iterations, the scheduler non-determinism triggers reliably.
190+
func TestSTMHighContentionStress(t *testing.T) {
191+
stores := map[storetypes.StoreKey]int{StoreKeyAuth: 0, StoreKeyBank: 1}
192+
testCases := []struct {
193+
name string
194+
blk *MockBlock
195+
executors int
196+
}{
197+
{"worstCaseBlock(100),5", worstCaseBlock(100), 5},
198+
{"testBlock(100,3),10", testBlock(100, 3), 10},
199+
}
200+
201+
for _, tc := range testCases {
202+
t.Run(tc.name, func(t *testing.T) {
203+
for i := 0; i < 200; i++ {
204+
storage := NewMultiMemDB(stores)
205+
require.NoError(t,
206+
ExecuteBlock(context.Background(), tc.blk.Size(), stores, storage, tc.executors, func(txn TxnIndex, store MultiStore) {
207+
tc.blk.ExecuteTx(txn, store, nil)
208+
}),
209+
)
210+
for _, err := range tc.blk.Results {
211+
require.NoError(t, err)
212+
}
213+
214+
crossCheck := NewMultiMemDB(stores)
215+
runSequential(crossCheck, tc.blk)
216+
217+
for store := range stores {
218+
require.True(t, StoreEqual(crossCheck.GetKVStore(store), storage.GetKVStore(store)),
219+
"iteration %d: parallel != sequential for store %s", i, store.Name())
220+
}
221+
}
222+
})
223+
}
224+
}
225+
187226
func StoreEqual(a, b storetypes.KVStore) bool {
188227
// compare with iterators
189228
iter1 := a.Iterator(nil, nil)

0 commit comments

Comments
 (0)