diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f610e47ada4..180b15a5b0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +### Bug Fixes + +* (store) [\#11117](https://github.com/cosmos/cosmos-sdk/pull/11117) Fix data race in store trace component + ## [v0.45.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.1) - 2022-02-03 ### Bug Fixes diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index a21a7381a424..e2f589094c58 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -68,8 +68,9 @@ type Store struct { pruneHeights []int64 initialVersion int64 - traceWriter io.Writer - traceContext types.TraceContext + traceWriter io.Writer + traceContext types.TraceContext + traceContextMutex sync.Mutex interBlockCache types.MultiStorePersistentCache @@ -370,7 +371,13 @@ func (rs *Store) SetTracer(w io.Writer) types.MultiStore { func (rs *Store) SetTracingContext(tc types.TraceContext) types.MultiStore { rs.traceContextMutex.Lock() defer rs.traceContextMutex.Unlock() - rs.traceContext = rs.traceContext.Merge(tc) + if rs.traceContext != nil { + for k, v := range tc { + rs.traceContext[k] = v + } + } else { + rs.traceContext = tc + } return rs } @@ -580,7 +587,7 @@ func (rs *Store) CacheMultiStore() types.CacheMultiStore { } stores[k] = store } - return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.getTracingContext()) + return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.getTracingContext(), rs.listeners) } // CacheMultiStoreWithVersion is analogous to CacheMultiStore except that it @@ -644,7 +651,7 @@ func (rs *Store) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStor cachedStores[key] = cacheStore } - return cachemulti.NewStore(rs.db, cachedStores, rs.keysByName, rs.traceWriter, rs.getTracingContext()), nil + return cachemulti.NewStore(rs.db, cachedStores, rs.keysByName, rs.traceWriter, rs.getTracingContext(), rs.listeners), nil } // GetStore returns a mounted Store for a given StoreKey. If the StoreKey does diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 31d24ed1205c..4e45adc49f9d 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -734,6 +734,63 @@ func TestCacheWraps(t *testing.T) { cacheWrappedWithTrace := multi.CacheWrapWithTrace(nil, nil) require.IsType(t, cachemulti.Store{}, cacheWrappedWithTrace) + + cacheWrappedWithListeners := multi.CacheWrapWithListeners(nil, nil) + require.IsType(t, cachemulti.Store{}, cacheWrappedWithListeners) +} + +func TestTraceConcurrency(t *testing.T) { + db := dbm.NewMemDB() + multi := newMultiStoreWithMounts(db, types.PruneNothing) + err := multi.LoadLatestVersion() + require.NoError(t, err) + + b := &bytes.Buffer{} + key := multi.keysByName["store1"] + tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) + + multi.SetTracer(b) + multi.SetTracingContext(tc) + + cms := multi.CacheMultiStore() + store1 := cms.GetKVStore(key) + cw := store1.CacheWrapWithTrace(b, tc) + _ = cw + require.NotNil(t, store1) + + stop := make(chan struct{}) + stopW := make(chan struct{}) + + go func(stop chan struct{}) { + for { + select { + case <-stop: + return + default: + store1.Set([]byte{1}, []byte{1}) + cms.Write() + } + } + }(stop) + + go func(stop chan struct{}) { + for { + select { + case <-stop: + return + default: + multi.SetTracingContext(tc) + } + } + }(stopW) + + time.Sleep(3 * time.Second) + stop <- struct{}{} + stopW <- struct{}{} +} + +func BenchmarkMultistoreSnapshot100K(b *testing.B) { + benchmarkMultistoreSnapshot(b, 10, 10000) } func TestTraceConcurrency(t *testing.T) {