Skip to content
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

Merged
merged 22 commits into from
Jun 24, 2024

Conversation

sontrinh16
Copy link
Member

@sontrinh16 sontrinh16 commented May 13, 2024

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...

  • included the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

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...

  • confirmed the correct type prefix in the PR title
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage

Summary by CodeRabbit

  • New Features
    • Introduced new functions to handle store initialization with pruning configurations and backend mounting.
    • Added test scenarios to validate store behavior under different conditions, including pruning and restart scenarios.

Copy link
Contributor

coderabbitai bot commented May 13, 2024

Walkthrough

The changes introduce new functions newStoreWithPruneConfig and newStoreWithBackendMount to enhance the store initialization process within the RootStoreTestSuite. These additions aim to facilitate specific configurations for pruning and backend mounting, essential for store management. Additionally, new test functions have been added to validate pruning functionality and store behavior across various scenarios, ensuring robustness and stability.

Changes

File Change Summary
store/v2/root/store_test.go Added imports, global variables, and functions for store configuration, pruning, and new test cases

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
Loading

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?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sontrinh16 sontrinh16 marked this pull request as ready for review May 16, 2024 05:07
@sontrinh16 sontrinh16 requested a review from a team as a code owner May 16, 2024 05:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits Files that changed from the base of the PR and between 072a95e and 220269e.
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"

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 of newStoreWithBackendMount.

Adding comments will improve maintainability and understandability of the code, especially for new contributors.

Review Details

Configuration used: .coderabbit.yml
Review profile: CHILL

Commits Files that changed from the base of the PR and between 220269e and 6959f2a.
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 of TestMultiStore_Pruning_SameHeightsTwice.


536-650: Ensure that TestMultiStoreRestart correctly handles state after restarts.


63-80: Ensure proper error handling in newStoreWithPruneConfig.

Comment on lines 464 to 534
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)
}
}
Copy link
Contributor

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.

Comment on lines 341 to 411
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)
}
}
}
Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits Files that changed from the base of the PR and between 6959f2a and 5c1d5ef.
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

@sontrinh16
Copy link
Member Author

i will add a test for TestUnevenStoresHeightCheck after this PR get merged

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits Files that changed from the base of the PR and between 5c1d5ef and b0dea14.
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) {
Copy link
Member

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

Copy link
Member

Choose a reason for hiding this comment

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

ping

Copy link
Member

@tac0turtle tac0turtle left a 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?

@sontrinh16
Copy link
Member Author

is this pr still needed?

is resolve first item of this issue

Copy link
Contributor

@cool-develope cool-develope left a 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?

store/v2/root/store_test.go Outdated Show resolved Hide resolved
@sontrinh16
Copy link
Member Author

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between b0dea14 and 7c4cbf9.

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 variable testStoreKeys.

This variable is used to iterate over multiple store keys in the new test functions, which simplifies the setup process.


66-89: New function newStoreWithPruneConfig 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 function newStoreWithBackendMount 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 function TestPrune 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 function TestMultiStore_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 function TestMultiStore_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 function TestMultiStoreRestart 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.

@sontrinh16
Copy link
Member Author

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

add TestHashStableWithEmptyCommit

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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: The TestMultiStore_PruningRestart function tests the pruning behavior after a simulated restart. It's well-structured and makes good use of the ShouldPrune 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: The TestMultiStoreRestart 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

Commits

Files that changed from the base of the PR and between 7c4cbf9 and 76c74ac.

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: Added time 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 of testStoreKeys 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 function newStoreWithBackendMount is straightforward and correctly sets up the store with backend mounting. It properly handles errors and uses the NoError assertion to ensure that no errors occur during the setup.


426-478: The TestMultiStore_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: The TestHashStableWithEmptyCommit 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.

Comment on lines 351 to 424
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)
}
}
}
Copy link
Contributor

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.

Suggested change
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)
}
}

store/v2/root/store_test.go Outdated Show resolved Hide resolved
store/v2/root/store_test.go Show resolved Hide resolved
store/v2/root/store_test.go Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between 76c74ac and e8bcb37.

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 of testStoreKeys 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 function newStoreWithBackendMount 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 function TestHashStableWithEmptyCommit 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 function TestPrune effectively tests various pruning scenarios. However, consider using require.Eventually() instead of time.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 the cosmossdk.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 file store/v2/root/store_test.go. The usage is confirmed by the presence of proof.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.go

Length of output: 292


431-487: The function TestMultiStore_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.

Comment on lines +489 to +577
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)
}
}
Copy link
Contributor

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.

Comment on lines +67 to +90
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
}
Copy link
Contributor

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.

Suggested change
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
}

Comment on lines +579 to +711
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")
}
Copy link
Contributor

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.

Suggested change
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")
}

Copy link
Contributor

@cool-develope cool-develope left a comment

Choose a reason for hiding this comment

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

lgtm

@sontrinh16 sontrinh16 added this pull request to the merge queue Jun 24, 2024
Merged via the queue into main with commit 5762b0b Jun 24, 2024
68 checks passed
@sontrinh16 sontrinh16 deleted the migrate_v1_tests branch June 24, 2024 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants