Skip to content

implement CommitKVStore using a migration router#3382

Open
cody-littley wants to merge 3 commits intomainfrom
cjl/router-kv-store
Open

implement CommitKVStore using a migration router#3382
cody-littley wants to merge 3 commits intomainfrom
cjl/router-kv-store

Conversation

@cody-littley
Copy link
Copy Markdown
Contributor

Describe your changes and provide context

Implement the CommitKVStore interface using a migration.Router. This will allow us to implement the composite DB's GetChildStoreByName() method.

Note that this is not a permanent solution. The long term solution will be to remove the GetChildStoreByName() api entirely. But that can be an incremental cleanup, and does not block MVP deployment.

Testing performed to validate your change

unit tests

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedMay 5, 2026, 4:33 PM

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

❌ Patch coverage is 95.23810% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.09%. Comparing base (ab2b373) to head (de24540).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
sei-db/state_db/sc/migration/router_kvstore.go 95.23% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #3382   +/-   ##
=======================================
  Coverage   59.08%   59.09%           
=======================================
  Files        2099     2100    +1     
  Lines      172988   173030   +42     
=======================================
+ Hits       102212   102251   +39     
- Misses      61911    61914    +3     
  Partials     8865     8865           
Flag Coverage Δ
sei-chain-pr 82.77% <95.23%> (?)
sei-db 70.41% <ø> (-0.22%) ⬇️

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

Files with missing lines Coverage Δ
sei-db/state_db/sc/migration/router_kvstore.go 95.23% <95.23%> (ø)

... and 1 file with indirect coverage changes

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

// Close is illegal during the standard CommitKVStore lifecycle for this type:
// the wrapped Router is owned by the caller and must outlive this view.
func (r *RouterCommitKVStore) Close() error {
return fmt.Errorf("RouterCommitKVStore.Close: illegal during standard lifecycle")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should we panic instead of error here to match testing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Test was incorrect, and actually failing. Fixed the test. IMO, better to return error than to panic, especially when the interface already has an error return value.

router Router,
storeName string,
versionProvider func() int64,
) *RouterCommitKVStore {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

context.Background() is hardcoded, do we need any outer ctx for per-Set cancellation? not sure

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In the main feature branch where I've got all of this code integrated, I actually removed the goroutines from the routers (race conditions + premature optimization 😅). Would you be ok with me addressing that issue in the feature branch instead of here? I can make the next follow up PR be the one that contains those changes.

}

func (r *RouterCommitKVStore) Set(key []byte, value []byte) {
r.applyOne(&proto.KVPair{Key: key, Value: value})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Set/Remove issues one ApplyChangeSets per call, In the prev PR, MigrationManager.ApplyChangeSets advances exactly one migration batch per call. A single cosmos-sdk transaction can issue dozens of Set calls; that becomes dozens of migration batches per commit

Copy link
Copy Markdown
Contributor Author

@cody-littley cody-littley May 5, 2026

Choose a reason for hiding this comment

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

As far as I can tell, there are no callers to Set() or Delete() anywhere in production code, only in unit tests.

The goal of router_kvstore.go was to implement the current interface in a way that didn't require a huge refactor. My plan after that is to incrementally remove methods (like these ones) one at a time, until the entire CommitKVStore interface can be fully deleted.

I added the following documentation to telegraph our intentions:

// Planed for future deprecation but not yet deprecated.
type CommitKVStore interface {
	// Planed for future deprecation but not yet deprecated.
	// This is the proper method to call to get a value from the store.
	Get(key []byte) []byte

	// Planed for future deprecation but not yet deprecated. This is the proper method to call to check
	// if a key exists in the store.
	Has(key []byte) bool

	// Deprected: do not call in production code, use CommitKVStore.ApplyChangeSets() instead.
	Set(key, value []byte)

	// Deprected: do not call in production code, use CommitKVStore.ApplyChangeSets() instead.
	Remove(key []byte)

	// Deprected but safe to call
	Version() int64

	// Deprected: do not call in production code, may panic on stores backed by flatKV
	RootHash() []byte

	// Partially deprecated: may panic if called on a store that does not support iteration (e.g. evm/ after migration)
	Iterator(start, end []byte, ascending bool) dbm.Iterator

	// Partially deprecated: may panic if called on a store that does not support proofs (e.g. evm/ after migration)
	GetProof(key []byte) *ics23.CommitmentProof

	// deprecated: some implementations always return errors, and ones that don't mean you are closing something
	// that shouldn't be closed directly
	io.Closer
}


func (r *RouterCommitKVStore) Set(key []byte, value []byte) {
r.applyOne(&proto.KVPair{Key: key, Value: value})
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sei KVStore impls (storev2/commitment/Store.Set, cachekv.Set) only append to an in-memory changeset; the real write happens at Commit.

how about RouterCommitKVStore buffers writes and flushes once at commit time (needs a Commit/Flush hook, mirroring storev2/commitment/Store).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this topic is probably worth a discussion. Currently FlatKV also only writes to an in-memory buffer.

This Set() method isn't used in production, only unit tests. And the plan is to delete it. My current goal is to make unit tests pass, and I'm less concerned with performance implications.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you add me to this discussion?

return found
}

func (r *RouterCommitKVStore) Set(key []byte, value []byte) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how do we ensure this actually works instead of baseapp wraps runTx in recover() and turns the panic into a per-tx error, then keeps processing

// If this method returns an error, it is not safe to attempt to retry. An error should be considered // fatal, and should result in any managed databases being shut down and crash recovered.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We should discuss this with execution folk. They should NEVER treat a DB error/panic as a transaction failure, as continuing after an error probably means data corruption and a later app hash break.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants