-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(store/v2): Migrate some v1 tests for rootmultistore #20368
Conversation
WalkthroughThe changes introduce new functions Changes
Sequence Diagram(s)sequenceDiagram
participant TestSuite as RootStoreTestSuite
participant PruneConfig as PruneOptions
participant Backend as BackendMount
participant Store as VersionedDatabase
participant Committer as Committer
TestSuite->>PruneConfig: Create PruneOptions
TestSuite->>TestSuite: Call newStoreWithPruneConfig(PruneConfig)
TestSuite->>Store: Initialize store
TestSuite->>TestSuite: Return configured store
TestSuite->>Store: Create VersionedDatabase
TestSuite->>Committer: Create Committer
TestSuite->>Backend: Call newStoreWithBackendMount(Store, Committer)
TestSuite->>Backend: Configure Backend Mount
TestSuite->>TestSuite: Return store with Backend Mount
TestSuite->>TestSuite: Run TestPrune
TestSuite->>TestSuite: Run TestMultiStore_Pruning_SameHeightsTwice
TestSuite->>TestSuite: Run TestMultiStore_PruningRestart
TestSuite->>TestSuite: Run TestMultiStoreRestart
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review Details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (2 hunks)
Files not reviewed due to errors (1)
- store/v2/root/store_test.go (no review received)
Additional Context Used
Path-based Instructions (1)
store/v2/root/store_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (1)
store/v2/root/store_test.go (1)
82-89
: Consider adding detailed comments explaining the purpose ofnewStoreWithBackendMount
.Adding comments will improve maintainability and understandability of the code, especially for new contributors.
Review Details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (2 hunks)
Additional Context Used
Path-based Instructions (1)
store/v2/root/store_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"
Additional comments not posted (3)
store/v2/root/store_test.go (3)
413-462
: Verify the behavior ofTestMultiStore_Pruning_SameHeightsTwice
.
536-650
: Ensure thatTestMultiStoreRestart
correctly handles state after restarts.
63-80
: Ensure proper error handling innewStoreWithPruneConfig
.
store/v2/root/store_test.go
Outdated
func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { | ||
// perform changes | ||
cs := corestore.NewChangeset() | ||
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false) | ||
|
||
pruneOpt := store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 11, | ||
} | ||
|
||
noopLog := log.NewNopLogger() | ||
|
||
sqliteDB, err := sqlite.New(s.T().TempDir()) | ||
s.Require().NoError(err) | ||
|
||
ss := storage.NewStorageStore(sqliteDB, &pruneOpt, noopLog) | ||
|
||
tree := iavl.NewIavlTree(dbm.NewMemDB(), noopLog, iavl.DefaultConfig()) | ||
|
||
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, dbm.NewMemDB(), &pruneOpt, noopLog) | ||
s.Require().NoError(err) | ||
|
||
s.newStoreWithBackendMount(ss, sc) | ||
s.Require().NoError(s.rootStore.LoadLatestVersion()) | ||
|
||
// Commit enough to build up heights to prune, where on the next block we should | ||
// batch delete. | ||
for i := uint64(0); i < 10; i++ { | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
} | ||
|
||
latestVer, err := s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune := pruneOpt.ShouldPrune(latestVer) | ||
s.Require().False(ok) | ||
s.Require().Equal(uint64(0), actualHeightToPrune) | ||
|
||
// "restart" | ||
s.newStoreWithBackendMount(ss, sc) | ||
err = s.rootStore.LoadLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
latestVer, err = s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) | ||
s.Require().False(ok) | ||
s.Require().Equal(uint64(0), actualHeightToPrune) | ||
|
||
// commit one more block and ensure the heights have been pruned | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
|
||
latestVer, err = s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) | ||
s.Require().True(ok) | ||
s.Require().Equal(uint64(8), actualHeightToPrune) | ||
|
||
for v := uint64(1); v <= actualHeightToPrune; v++ { | ||
err := s.rootStore.LoadVersion(v) | ||
s.Require().Error(err, "expected error when loading height: %d", v) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for potential optimizations in TestMultiStore_PruningRestart
.
The repeated setup and teardown within the test could be optimized by using setup and teardown hooks or by abstracting common logic into helper functions.
store/v2/root/store_test.go
Outdated
func (s *RootStoreTestSuite) TestPrune() { | ||
// perform changes | ||
cs := corestore.NewChangeset() | ||
for i := 0; i < 10; i++ { | ||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | ||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | ||
|
||
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
numVersions int64 | ||
po store.PruneOptions | ||
deleted []uint64 | ||
saved []uint64 | ||
}{ | ||
{"prune nothing", 10, *store.DefaultPruneOptions(), nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | ||
{"prune everything", 12, store.PruneOptions{ | ||
KeepRecent: 1, | ||
Interval: 10, | ||
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}}, | ||
{"prune some; no batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 1, | ||
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}}, | ||
{"prune some; small batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 3, | ||
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}}, | ||
{"prune some; large batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 11, | ||
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
|
||
s.newStoreWithPruneConfig(&tc.po) | ||
|
||
// write keys over multiple versions | ||
for i := int64(0); i < tc.numVersions; i++ { | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
} | ||
|
||
for _, v := range tc.saved { | ||
ro, err := s.rootStore.StateAt(v) | ||
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name) | ||
|
||
for i := 0; i < 10; i++ { | ||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | ||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | ||
|
||
reader, err := ro.GetReader(testStoreKeyBytes) | ||
s.Require().NoError(err) | ||
result, err := reader.Get([]byte(key)) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name) | ||
} | ||
} | ||
|
||
for _, v := range tc.deleted { | ||
_, err := s.rootStore.StateAt(v) | ||
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review the logic in TestPrune
for potential simplifications.
The test case structure and the repeated setup for each test scenario could be simplified or abstracted to reduce redundancy and improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review Details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (3 hunks)
Files skipped from review as they are similar to previous changes (1)
- store/v2/root/store_test.go
i will add a test for TestUnevenStoresHeightCheck after this PR get merged |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review Details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (3 hunks)
Files skipped from review as they are similar to previous changes (1)
- store/v2/root/store_test.go
@@ -58,6 +58,34 @@ func (s *RootStoreTestSuite) SetupTest() { | |||
s.rootStore = rs | |||
} | |||
|
|||
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruneOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go doc would be useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this pr still needed?
is resolve first item of this issue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only deal with pruning test in this PR?
there is also TestUnevenStoresHeightCheck but i'm waiting for the store keys upgrade PR get merged first |
… into migrate_v1_tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (4 hunks)
Additional context used
Path-based instructions (1)
store/v2/root/store_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"
Additional comments not posted (8)
store/v2/root/store_test.go (8)
6-6
: Added import of "time" package.This is necessary for the new tests that involve timing and delays.
28-28
: Added global variabletestStoreKeys
.This variable is used to iterate over multiple store keys in the new test functions, which simplifies the setup process.
66-89
: New functionnewStoreWithPruneConfig
added.This function sets up a new store with a specific pruning configuration, which is used in multiple new test cases. It encapsulates the setup logic nicely, improving code reusability and maintainability.
91-98
: New functionnewStoreWithBackendMount
added.This function is crucial for setting up a store with a backend mount, used in tests that simulate store restarts. It helps in isolating setup logic and making the tests cleaner.
350-423
: New test functionTestPrune
added.This test function evaluates various pruning scenarios to ensure that the store behaves correctly under different configurations. It's well-structured with clear test cases and proper cleanup.
425-477
: New test functionTestMultiStore_Pruning_SameHeightsTwice
added.This function tests the idempotency of the pruning process by attempting to prune the same heights twice. It's a good addition to ensure robustness in the pruning logic.
479-555
: New test functionTestMultiStore_PruningRestart
added.This test checks the behavior of the store after a simulated restart, ensuring that pruning states are correctly handled across restarts. It's a critical test for verifying the resilience of the pruning feature.
557-677
: New test functionTestMultiStoreRestart
added.This function tests the store's ability to recover and correctly handle state after a restart. It's essential for ensuring data integrity and system reliability after unexpected shutdowns or reboots.
add TestHashStableWithEmptyCommit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (2)
store/v2/root/store_test.go (2)
480-556
: TheTestMultiStore_PruningRestart
function tests the pruning behavior after a simulated restart. It's well-structured and makes good use of theShouldPrune
method to verify the pruning logic. Consider adding more inline comments to explain the purpose of each major step, especially around the simulated restart, to improve readability for future maintainers.
558-678
: TheTestMultiStoreRestart
function tests the store's behavior after a restart, ensuring that data remains consistent and accessible. The function is well-implemented, but adding more inline comments could help explain the test's flow and the significance of each operation, enhancing understandability.
Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (5 hunks)
Additional context used
Path-based instructions (1)
store/v2/root/store_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"
Additional comments not posted (5)
store/v2/root/store_test.go (5)
6-6
: Addedtime
import to handle delays in asynchronous operations, such as pruning. Ensure that this import is necessary and used appropriately throughout the tests.
29-29
: Introduction oftestStoreKeys
global variable to manage store keys in a centralized manner. This is a good practice as it enhances maintainability and reusability across different test cases.
92-99
: The functionnewStoreWithBackendMount
is straightforward and correctly sets up the store with backend mounting. It properly handles errors and uses theNoError
assertion to ensure that no errors occur during the setup.
426-478
: TheTestMultiStore_Pruning_SameHeightsTwice
function effectively tests pruning the same heights twice to ensure idempotency. The use of constants for configuration values enhances clarity and maintainability.
680-709
: TheTestHashStableWithEmptyCommit
function checks the stability of the hash after an empty commit, which is a critical aspect to test. The implementation is correct, and the assertions are appropriate for the test's objectives.
store/v2/root/store_test.go
Outdated
func (s *RootStoreTestSuite) TestPrune() { | ||
// perform changes | ||
cs := corestore.NewChangeset() | ||
for i := 0; i < 10; i++ { | ||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | ||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | ||
|
||
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
numVersions int64 | ||
po store.PruneOptions | ||
deleted []uint64 | ||
saved []uint64 | ||
}{ | ||
{"prune nothing", 10, *store.DefaultPruneOptions(), nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | ||
{"prune everything", 12, store.PruneOptions{ | ||
KeepRecent: 1, | ||
Interval: 10, | ||
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}}, | ||
{"prune some; no batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 1, | ||
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}}, | ||
{"prune some; small batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 3, | ||
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}}, | ||
{"prune some; large batch", 10, store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 11, | ||
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
tc := tc | ||
|
||
s.newStoreWithPruneConfig(&tc.po) | ||
|
||
// write keys over multiple versions | ||
for i := int64(0); i < tc.numVersions; i++ { | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
} | ||
|
||
// wait for async pruning process to finish | ||
time.Sleep(100 * time.Millisecond) | ||
|
||
for _, v := range tc.saved { | ||
ro, err := s.rootStore.StateAt(v) | ||
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name) | ||
|
||
for i := 0; i < 10; i++ { | ||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | ||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | ||
|
||
reader, err := ro.GetReader(testStoreKeyBytes) | ||
s.Require().NoError(err) | ||
result, err := reader.Get([]byte(key)) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name) | ||
} | ||
} | ||
|
||
for _, v := range tc.deleted { | ||
_, err := s.rootStore.StateAt(v) | ||
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TestPrune
function is comprehensive and tests various pruning scenarios. It uses table-driven tests, which is a best practice for covering multiple cases succinctly. However, consider abstracting the repeated setup logic into a helper function to enhance readability and reduce redundancy.
+ func (s *RootStoreTestSuite) setupPruneTestCase(cs *corestore.Changeset, tc testCase) {
+ for i := int64(0); i < tc.numVersions; i++ {
+ cHash, err := s.rootStore.Commit(cs)
+ s.Require().NoError(err)
+ s.Require().NotNil(cHash)
+ }
+ }
+
for _, tc := range testCases {
tc := tc
s.newStoreWithPruneConfig(&tc.po)
s.setupPruneTestCase(cs, tc)
// wait for async pruning process to finish
time.Sleep(100 * time.Millisecond)
// Test saved and deleted versions
}
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (s *RootStoreTestSuite) TestPrune() { | |
// perform changes | |
cs := corestore.NewChangeset() | |
for i := 0; i < 10; i++ { | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | |
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
} | |
testCases := []struct { | |
name string | |
numVersions int64 | |
po store.PruneOptions | |
deleted []uint64 | |
saved []uint64 | |
}{ | |
{"prune nothing", 10, *store.DefaultPruneOptions(), nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | |
{"prune everything", 12, store.PruneOptions{ | |
KeepRecent: 1, | |
Interval: 10, | |
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}}, | |
{"prune some; no batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 1, | |
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}}, | |
{"prune some; small batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 3, | |
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}}, | |
{"prune some; large batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 11, | |
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | |
} | |
for _, tc := range testCases { | |
tc := tc | |
s.newStoreWithPruneConfig(&tc.po) | |
// write keys over multiple versions | |
for i := int64(0); i < tc.numVersions; i++ { | |
// execute Commit | |
cHash, err := s.rootStore.Commit(cs) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
} | |
// wait for async pruning process to finish | |
time.Sleep(100 * time.Millisecond) | |
for _, v := range tc.saved { | |
ro, err := s.rootStore.StateAt(v) | |
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name) | |
for i := 0; i < 10; i++ { | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | |
reader, err := ro.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result, err := reader.Get([]byte(key)) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name) | |
} | |
} | |
for _, v := range tc.deleted { | |
_, err := s.rootStore.StateAt(v) | |
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name) | |
} | |
} | |
} | |
func (s *RootStoreTestSuite) TestPrune() { | |
// perform changes | |
cs := corestore.NewChangeset() | |
for i := 0; i < 10; i++ { | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | |
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
} | |
testCases := []struct { | |
name string | |
numVersions int64 | |
po store.PruneOptions | |
deleted []uint64 | |
saved []uint64 | |
}{ | |
{"prune nothing", 10, *store.DefaultPruneOptions(), nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | |
{"prune everything", 12, store.PruneOptions{ | |
KeepRecent: 1, | |
Interval: 10, | |
}, []uint64{1, 2, 3, 4, 5, 6, 7, 8}, []uint64{9, 10, 11, 12}}, | |
{"prune some; no batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 1, | |
}, []uint64{1, 2, 3, 4, 6, 5, 7}, []uint64{8, 9, 10}}, | |
{"prune some; small batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 3, | |
}, []uint64{1, 2, 3, 4, 5, 6}, []uint64{7, 8, 9, 10}}, | |
{"prune some; large batch", 10, store.PruneOptions{ | |
KeepRecent: 2, | |
Interval: 11, | |
}, nil, []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, | |
} | |
for _, tc := range testCases { | |
tc := tc | |
s.newStoreWithPruneConfig(&tc.po) | |
s.setupPruneTestCase(cs, tc) | |
// wait for async pruning process to finish | |
time.Sleep(100 * time.Millisecond) | |
for _, v := range tc.saved { | |
ro, err := s.rootStore.StateAt(v) | |
s.Require().NoError(err, "expected no error when loading height %d at test %s", v, tc.name) | |
for i := 0; i < 10; i++ { | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099 | |
reader, err := ro.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result, err := reader.Get([]byte(key)) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(val), result, "value should be equal for test: %s", tc.name) | |
} | |
} | |
for _, v := range tc.deleted { | |
_, err := s.rootStore.StateAt(v) | |
s.Require().Error(err, "expected error when loading height %d at test %s", v, tc.name) | |
} | |
} | |
} | |
func (s *RootStoreTestSuite) setupPruneTestCase(cs *corestore.Changeset, tc testCase) { | |
for i := int64(0); i < tc.numVersions; i++ { | |
cHash, err := s.rootStore.Commit(cs) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Files selected for processing (1)
- store/v2/root/store_test.go (5 hunks)
Additional context used
Path-based instructions (1)
store/v2/root/store_test.go (2)
Pattern
**/*.go
: Review the Golang code for conformity with the Uber Golang style guide, highlighting any deviations.
Pattern
**/*_test.go
: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request"
Additional comments not posted (6)
store/v2/root/store_test.go (6)
29-29
: The addition oftestStoreKeys
as a global variable is appropriate for reusability across multiple test functions. Ensure that all test keys are utilized effectively in the suite to justify their global scope.
92-99
: The functionnewStoreWithBackendMount
is well-implemented for setting up a store with a backend mount configuration. Ensure that all parameters (ss
,sc
,pm
) are properly utilized within the function to avoid any unused variable issues.
713-742
: The functionTestHashStableWithEmptyCommit
checks the hash stability with an empty commit, which is a critical aspect of ensuring data integrity. The implementation appears correct. Ensure comprehensive testing around this to prevent data corruption scenarios.
351-429
: The functionTestPrune
effectively tests various pruning scenarios. However, consider usingrequire.Eventually()
instead oftime.Sleep
to handle asynchronous operations, as this approach is more reliable and can handle variations in execution time better.- time.Sleep(100 * time.Millisecond) // This is prone to errors in a busy pipeline + require.Eventually(func() bool { + return checkAllPruned() // Implement this method to check if pruning is done + }, 10*time.Second, 100*time.Millisecond, "Pruning did not complete in the expected time")Likely invalid or redundant comment.
17-17
: The addition of thecosmossdk.io/store/v2/proof
import should be scrutinized to ensure it is used within the file. If it is not used, consider removing it to keep the code clean.Verification successful
The import statement for
"cosmossdk.io/store/v2/proof"
is indeed used in the filestore/v2/root/store_test.go
. The usage is confirmed by the presence ofproof.CommitID{}
in the code.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that the `proof` import is used in the file. ast-grep --lang go --pattern $'import "cosmossdk.io/store/v2/proof" { $$$ }'Length of output: 544
Script:
#!/bin/bash # Description: Verify that the `proof` import is used in the file. # First, check if the import exists. ast-grep --lang go --pattern $'import "cosmossdk.io/store/v2/proof"' store/v2/root/store_test.go # Then, check if `proof` is used in the file. rg 'proof' store/v2/root/store_test.goLength of output: 292
431-487
: The functionTestMultiStore_Pruning_SameHeightsTwice
tests the scenario of pruning the same heights twice. Ensure that the logic within this test is robust against potential race conditions or state inconsistencies, especially when dealing with asynchronous pruning operations.
func (s *RootStoreTestSuite) TestMultiStore_PruningRestart() { | ||
// perform changes | ||
cs := corestore.NewChangeset() | ||
cs.Add(testStoreKeyBytes, []byte("key"), []byte("val"), false) | ||
|
||
pruneOpt := &store.PruneOptions{ | ||
KeepRecent: 2, | ||
Interval: 11, | ||
} | ||
|
||
noopLog := log.NewNopLogger() | ||
|
||
mdb1 := dbm.NewMemDB() | ||
mdb2 := dbm.NewMemDB() | ||
sqliteDB, err := sqlite.New(s.T().TempDir()) | ||
s.Require().NoError(err) | ||
ss := storage.NewStorageStore(sqliteDB, noopLog) | ||
|
||
tree := iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) | ||
sc, err := commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, mdb2, noopLog) | ||
s.Require().NoError(err) | ||
|
||
pm := pruning.NewManager(sc, ss, pruneOpt, pruneOpt) | ||
|
||
s.newStoreWithBackendMount(ss, sc, pm) | ||
s.Require().NoError(s.rootStore.LoadLatestVersion()) | ||
|
||
// Commit enough to build up heights to prune, where on the next block we should | ||
// batch delete. | ||
for i := uint64(0); i < 10; i++ { | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
} | ||
|
||
latestVer, err := s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune := pruneOpt.ShouldPrune(latestVer) | ||
s.Require().False(ok) | ||
s.Require().Equal(uint64(0), actualHeightToPrune) | ||
|
||
// "restart" | ||
sqliteDB, err = sqlite.New(s.T().TempDir()) | ||
s.Require().NoError(err) | ||
ss = storage.NewStorageStore(sqliteDB, noopLog) | ||
|
||
tree = iavl.NewIavlTree(mdb1, noopLog, iavl.DefaultConfig()) | ||
sc, err = commitment.NewCommitStore(map[string]commitment.Tree{testStoreKey: tree}, mdb2, noopLog) | ||
s.Require().NoError(err) | ||
|
||
pm = pruning.NewManager(sc, ss, pruneOpt, pruneOpt) | ||
|
||
s.newStoreWithBackendMount(ss, sc, pm) | ||
err = s.rootStore.LoadLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
latestVer, err = s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) | ||
s.Require().False(ok) | ||
s.Require().Equal(uint64(0), actualHeightToPrune) | ||
|
||
// commit one more block and ensure the heights have been pruned | ||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
|
||
latestVer, err = s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
|
||
ok, actualHeightToPrune = pruneOpt.ShouldPrune(latestVer) | ||
s.Require().True(ok) | ||
s.Require().Equal(uint64(8), actualHeightToPrune) | ||
|
||
for v := uint64(1); v <= actualHeightToPrune; v++ { | ||
checkErr := func() bool { | ||
if err = s.rootStore.LoadVersion(v); err != nil { | ||
return true | ||
} | ||
return false | ||
} | ||
// wait for async pruning process to finish | ||
s.Require().Eventually(checkErr, 2*time.Second, 100*time.Millisecond, "expected error when loading height: %d", v) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function TestMultiStore_PruningRestart
simulates a restart scenario to test pruning behavior. It is crucial to ensure that the state is correctly maintained across restarts. Consider adding more assertions to verify the state integrity after a restart.
+ // After restart, verify the state integrity
+ s.Require().Equal(expectedState, actualState, "State mismatch after restart")
Committable suggestion was skipped due to low confidence.
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruneOptions) { | ||
noopLog := log.NewNopLogger() | ||
|
||
sqliteDB, err := sqlite.New(s.T().TempDir()) | ||
s.Require().NoError(err) | ||
ss := storage.NewStorageStore(sqliteDB, noopLog) | ||
|
||
mdb := dbm.NewMemDB() | ||
multiTrees := make(map[string]commitment.Tree) | ||
for _, storeKey := range testStoreKeys { | ||
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) | ||
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | ||
} | ||
|
||
sc, err := commitment.NewCommitStore(multiTrees, dbm.NewMemDB(), noopLog) | ||
s.Require().NoError(err) | ||
|
||
pm := pruning.NewManager(sc, ss, config, config) | ||
|
||
rs, err := New(noopLog, ss, sc, pm, nil, nil) | ||
s.Require().NoError(err) | ||
|
||
s.rootStore = rs | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function newStoreWithPruneConfig
correctly sets up a new store with a specified pruning configuration. Consider adding error handling for the iavl.NewIavlTree
call within the loop to ensure robustness in case of failures during tree creation.
+ err := error(nil) // Initialize error
for _, storeKey := range testStoreKeys {
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey))
- multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
+ tree, err := iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig())
+ if err != nil {
+ s.T().Fatal(err)
+ }
+ multiTrees[storeKey] = tree
}
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruneOptions) { | |
noopLog := log.NewNopLogger() | |
sqliteDB, err := sqlite.New(s.T().TempDir()) | |
s.Require().NoError(err) | |
ss := storage.NewStorageStore(sqliteDB, noopLog) | |
mdb := dbm.NewMemDB() | |
multiTrees := make(map[string]commitment.Tree) | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) | |
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
} | |
sc, err := commitment.NewCommitStore(multiTrees, dbm.NewMemDB(), noopLog) | |
s.Require().NoError(err) | |
pm := pruning.NewManager(sc, ss, config, config) | |
rs, err := New(noopLog, ss, sc, pm, nil, nil) | |
s.Require().NoError(err) | |
s.rootStore = rs | |
} | |
func (s *RootStoreTestSuite) newStoreWithPruneConfig(config *store.PruneOptions) { | |
noopLog := log.NewNopLogger() | |
sqliteDB, err := sqlite.New(s.T().TempDir()) | |
s.Require().NoError(err) | |
ss := storage.NewStorageStore(sqliteDB, noopLog) | |
mdb := dbm.NewMemDB() | |
multiTrees := make(map[string]commitment.Tree) | |
err = nil // Initialize error | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb, []byte(storeKey)) | |
tree, err := iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
if err != nil { | |
s.T().Fatal(err) | |
} | |
multiTrees[storeKey] = tree | |
} | |
sc, err := commitment.NewCommitStore(multiTrees, dbm.NewMemDB(), noopLog) | |
s.Require().NoError(err) | |
pm := pruning.NewManager(sc, ss, config, config) | |
rs, err := New(noopLog, ss, sc, pm, nil, nil) | |
s.Require().NoError(err) | |
s.rootStore = rs | |
} |
val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1 | ||
|
||
cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | ||
|
||
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | ||
val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1 | ||
|
||
cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | ||
|
||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
|
||
latestVer, err := s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
s.Require().Equal(uint64(i), latestVer) | ||
} | ||
|
||
// more changes | ||
cs1 := corestore.NewChangeset() | ||
key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | ||
val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1 | ||
|
||
cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | ||
|
||
key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | ||
val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1 | ||
|
||
cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | ||
|
||
// execute Commit | ||
cHash, err := s.rootStore.Commit(cs1) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
|
||
latestVer, err := s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
s.Require().Equal(uint64(3), latestVer) | ||
|
||
cs2 := corestore.NewChangeset() | ||
key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099 | ||
val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1 | ||
|
||
cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | ||
|
||
// execute Commit | ||
cHash, err = s.rootStore.Commit(cs2) | ||
s.Require().NoError(err) | ||
s.Require().NotNil(cHash) | ||
|
||
latestVer, err = s.rootStore.GetLatestVersion() | ||
s.Require().NoError(err) | ||
s.Require().Equal(uint64(4), latestVer) | ||
|
||
_, ro1, err := s.rootStore.StateLatest() | ||
s.Require().Nil(err) | ||
reader1, err := ro1.GetReader(testStoreKeyBytes) | ||
s.Require().NoError(err) | ||
result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3))) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal") | ||
|
||
// "restart" | ||
multiTrees = make(map[string]commitment.Tree) | ||
for _, storeKey := range testStoreKeys { | ||
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) | ||
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | ||
} | ||
|
||
sc, err = commitment.NewCommitStore(multiTrees, mdb2, noopLog) | ||
s.Require().NoError(err) | ||
|
||
pm = pruning.NewManager(sc, ss, nil, nil) | ||
|
||
s.newStoreWithBackendMount(ss, sc, pm) | ||
err = s.rootStore.LoadLatestVersion() | ||
s.Require().Nil(err) | ||
|
||
latestVer, ro, err := s.rootStore.StateLatest() | ||
s.Require().Nil(err) | ||
s.Require().Equal(uint64(4), latestVer) | ||
reader, err := ro.GetReader(testStoreKeyBytes) | ||
s.Require().NoError(err) | ||
result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3))) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal") | ||
|
||
reader, err = ro.GetReader(testStoreKey2Bytes) | ||
s.Require().NoError(err) | ||
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2))) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal") | ||
|
||
reader, err = ro.GetReader(testStoreKey3Bytes) | ||
s.Require().NoError(err) | ||
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4))) | ||
s.Require().NoError(err) | ||
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function TestMultiStoreRestart
tests the restart functionality of the store. It is well-implemented, but consider adding comments to clarify the steps involved, especially where complex operations like state verification post-restart are performed.
+ // Restart the store and load the latest version
err = s.rootStore.LoadLatestVersion()
+ // Verify the state after restart
latestVer, ro, err := s.rootStore.StateLatest()
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (s *RootStoreTestSuite) TestMultiStoreRestart() { | |
noopLog := log.NewNopLogger() | |
sqliteDB, err := sqlite.New(s.T().TempDir()) | |
s.Require().NoError(err) | |
ss := storage.NewStorageStore(sqliteDB, noopLog) | |
mdb1 := dbm.NewMemDB() | |
mdb2 := dbm.NewMemDB() | |
multiTrees := make(map[string]commitment.Tree) | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) | |
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
} | |
sc, err := commitment.NewCommitStore(multiTrees, mdb2, noopLog) | |
s.Require().NoError(err) | |
pm := pruning.NewManager(sc, ss, nil, nil) | |
s.newStoreWithBackendMount(ss, sc, pm) | |
s.Require().NoError(s.rootStore.LoadLatestVersion()) | |
// perform changes | |
for i := 1; i < 3; i++ { | |
cs := corestore.NewChangeset() | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d_%03d", i, 1) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err := s.rootStore.Commit(cs) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err := s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(i), latestVer) | |
} | |
// more changes | |
cs1 := corestore.NewChangeset() | |
key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1 | |
cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1 | |
cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err := s.rootStore.Commit(cs1) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err := s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(3), latestVer) | |
cs2 := corestore.NewChangeset() | |
key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1 | |
cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err = s.rootStore.Commit(cs2) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err = s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(4), latestVer) | |
_, ro1, err := s.rootStore.StateLatest() | |
s.Require().Nil(err) | |
reader1, err := ro1.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal") | |
// "restart" | |
multiTrees = make(map[string]commitment.Tree) | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) | |
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
} | |
sc, err = commitment.NewCommitStore(multiTrees, mdb2, noopLog) | |
s.Require().NoError(err) | |
pm = pruning.NewManager(sc, ss, nil, nil) | |
s.newStoreWithBackendMount(ss, sc, pm) | |
err = s.rootStore.LoadLatestVersion() | |
s.Require().Nil(err) | |
latestVer, ro, err := s.rootStore.StateLatest() | |
s.Require().Nil(err) | |
s.Require().Equal(uint64(4), latestVer) | |
reader, err := ro.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal") | |
reader, err = ro.GetReader(testStoreKey2Bytes) | |
s.Require().NoError(err) | |
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal") | |
reader, err = ro.GetReader(testStoreKey3Bytes) | |
s.Require().NoError(err) | |
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal") | |
} | |
func (s *RootStoreTestSuite) TestMultiStoreRestart() { | |
noopLog := log.NewNopLogger() | |
sqliteDB, err := sqlite.New(s.T().TempDir()) | |
s.Require().NoError(err) | |
ss := storage.NewStorageStore(sqliteDB, noopLog) | |
mdb1 := dbm.NewMemDB() | |
mdb2 := dbm.NewMemDB() | |
multiTrees := make(map[string]commitment.Tree) | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) | |
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
} | |
sc, err := commitment.NewCommitStore(multiTrees, mdb2, noopLog) | |
s.Require().NoError(err) | |
pm := pruning.NewManager(sc, ss, nil, nil) | |
s.newStoreWithBackendMount(ss, sc, pm) | |
s.Require().NoError(s.rootStore.LoadLatestVersion()) | |
// perform changes | |
for i := 1; i < 3; i++ { | |
cs := corestore.NewChangeset() | |
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d_%03d", i, 1) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", i, 2) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", i) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", i, 3) // val000_1, val001_1, ..., val099_1 | |
cs.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err := s.rootStore.Commit(cs) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err := s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(i), latestVer) | |
} | |
// more changes | |
cs1 := corestore.NewChangeset() | |
key := fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | |
val := fmt.Sprintf("val%03d_%03d", 3, 1) // val000_1, val001_1, ..., val099_1 | |
cs1.Add(testStoreKeyBytes, []byte(key), []byte(val), false) | |
key = fmt.Sprintf("key%03d", 3) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", 3, 2) // val000_1, val001_1, ..., val099_1 | |
cs1.Add(testStoreKey2Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err := s.rootStore.Commit(cs1) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err := s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(3), latestVer) | |
cs2 := corestore.NewChangeset() | |
key = fmt.Sprintf("key%03d", 4) // key000, key001, ..., key099 | |
val = fmt.Sprintf("val%03d_%03d", 4, 3) // val000_1, val001_1, ..., val099_1 | |
cs2.Add(testStoreKey3Bytes, []byte(key), []byte(val), false) | |
// execute Commit | |
cHash, err = s.rootStore.Commit(cs2) | |
s.Require().NoError(err) | |
s.Require().NotNil(cHash) | |
latestVer, err = s.rootStore.GetLatestVersion() | |
s.Require().NoError(err) | |
s.Require().Equal(uint64(4), latestVer) | |
_, ro1, err := s.rootStore.StateLatest() | |
s.Require().Nil(err) | |
reader1, err := ro1.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result1, err := reader1.Get([]byte(fmt.Sprintf("key%03d", 3))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result1, "value should be equal") | |
// "restart" | |
multiTrees = make(map[string]commitment.Tree) | |
for _, storeKey := range testStoreKeys { | |
prefixDB := dbm.NewPrefixDB(mdb1, []byte(storeKey)) | |
multiTrees[storeKey] = iavl.NewIavlTree(prefixDB, noopLog, iavl.DefaultConfig()) | |
} | |
sc, err = commitment.NewCommitStore(multiTrees, mdb2, noopLog) | |
s.Require().NoError(err) | |
pm = pruning.NewManager(sc, ss, nil, nil) | |
s.newStoreWithBackendMount(ss, sc, pm) | |
// Restart the store and load the latest version | |
err = s.rootStore.LoadLatestVersion() | |
s.Require().Nil(err) | |
// Verify the state after restart | |
latestVer, ro, err := s.rootStore.StateLatest() | |
s.Require().Nil(err) | |
s.Require().Equal(uint64(4), latestVer) | |
reader, err := ro.GetReader(testStoreKeyBytes) | |
s.Require().NoError(err) | |
result, err := reader.Get([]byte(fmt.Sprintf("key%03d", 3))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 3, 1)), result, "value should be equal") | |
reader, err = ro.GetReader(testStoreKey2Bytes) | |
s.Require().NoError(err) | |
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 2))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 2, 2)), result, "value should be equal") | |
reader, err = ro.GetReader(testStoreKey3Bytes) | |
s.Require().NoError(err) | |
result, err = reader.Get([]byte(fmt.Sprintf("key%03d", 4))) | |
s.Require().NoError(err) | |
s.Require().Equal([]byte(fmt.Sprintf("val%03d_%03d", 4, 3)), result, "value should be equal") | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Description
Closes: #XXXX
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!
in the type prefix if API or client breaking changeCHANGELOG.md
Reviewers Checklist
All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.
I have...
Summary by CodeRabbit