Skip to content

fix(storev2): hold read lock in GetCommitKVStore during concurrent map access#3517

Open
amir-deris wants to merge 4 commits into
mainfrom
amir/plt-416-fix-concurrent-map-access
Open

fix(storev2): hold read lock in GetCommitKVStore during concurrent map access#3517
amir-deris wants to merge 4 commits into
mainfrom
amir/plt-416-fix-concurrent-map-access

Conversation

@amir-deris
Copy link
Copy Markdown
Contributor

@amir-deris amir-deris commented May 28, 2026

Summary

  • Adds RLock/RUnlock to GetCommitKVStore in rootmulti/store.go so reads of the ckvStores map are properly synchronized against concurrent writes.
  • Adds TestGetCommitKVStoreNoDataRace to exercise the concurrent read/write path; run with go test -race ./sei-cosmos/storev2/rootmulti/....

Test plan

  • go test -race ./sei-cosmos/storev2/rootmulti/... passes with no data race reports

@cursor
Copy link
Copy Markdown

cursor Bot commented May 28, 2026

PR Summary

Medium Risk
Touches core multistore access paths used during commits and queries; incorrect locking could cause deadlocks or stale reads, but the change aligns getters with existing mutex usage elsewhere.

Overview
Adds RLock/RUnlock around reads of the ckvStores map in GetStore, GetKVStore, and GetCommitKVStore so concurrent readers do not race writers that replace or update that map during commit or load.

Adds TestGetCommitKVStore_ReaderRespectsWriteLock, which holds the write lock, proves GetCommitKVStore blocks until it is released, and checks the goroutine sees the updated store after the map is swapped.

Reviewed by Cursor Bugbot for commit fd1b008. Bugbot is set up for automated code reviews on this repo. Configure here.

@amir-deris amir-deris changed the title Added concurrent safe read for getComitKVStore and added test fix(storev2): hold read lock in GetCommitKVStore during concurrent map access May 28, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 28, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedMay 29, 2026, 5:55 PM

@amir-deris amir-deris requested review from bdchatham and masih May 28, 2026 19:03
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.20%. Comparing base (c4e1a2a) to head (fd1b008).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3517      +/-   ##
==========================================
- Coverage   59.05%   58.20%   -0.86%     
==========================================
  Files        2205     2132      -73     
  Lines      182317   173954    -8363     
==========================================
- Hits       107672   101252    -6420     
+ Misses      64945    63681    -1264     
+ Partials     9700     9021     -679     
Flag Coverage Δ
sei-chain-pr 63.56% <100.00%> (?)
sei-db 70.41% <ø> (-0.22%) ⬇️
sei-db-state-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-cosmos/storev2/rootmulti/store.go 65.42% <100.00%> (+0.33%) ⬆️

... and 74 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bdchatham
Copy link
Copy Markdown
Contributor

Could we tighten the test? Something like:

func TestGetCommitKVStore_ReaderRespectsWriteLock(t *testing.T) {
    store := &Store{
        storeKeys: map[string]types.StoreKey{},
        ckvStores: map[types.StoreKey]types.CommitKVStore{},
    }
    key := types.NewKVStoreKey("bank")
    store.storeKeys[key.Name()] = key
    store.ckvStores[key] = mem.NewStore()

    store.mtx.Lock()

    readDone := make(chan types.CommitKVStore, 1)
    go func() {
        readDone <- store.GetCommitKVStore(key)
    }()

    select {
    case <-readDone:
        t.Fatal("GetCommitKVStore returned while write lock held — RLock missing")
    case <-time.After(50 * time.Millisecond):
    }

    newVal := mem.NewStore()
    store.ckvStores = map[types.StoreKey]types.CommitKVStore{key: newVal}
    store.mtx.Unlock()

    require.Same(t, newVal, <-readDone)
}

This deterministically verifies the RLock is doing its job — the read blocks while the write lock is held, then observes the post-write value — instead of relying on the scheduler to overlap two unsynchronized goroutines and the race detector to notice.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e5a3ced. Configure here.

Comment thread sei-cosmos/storev2/rootmulti/store.go
@masih
Copy link
Copy Markdown
Collaborator

masih commented May 29, 2026

@codex review this pr

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants