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

CosmosSDK State Watching Features - KVStore reading and listening capabilities - KVstore listening #7888

Closed
4 tasks
i-norden opened this issue Nov 10, 2020 · 5 comments · Fixed by #8012
Closed
4 tasks
Labels

Comments

@i-norden
Copy link
Contributor

i-norden commented Nov 10, 2020

Summary

The next step for completing KVStore reading and listening capabilities is to expose KVStore listening capabilities at the MultiStore level. This can be done in a manner that closely mirrors the current state tracing using the tracekv.Store, with the proposed difference being that we can set specific listeners and contexts for each KVStore. This makes it simpler to handle routing for different KVStores and provides control over which KVStores are exposed and in what manner.

Problem Definition

Currently, KVStore data can be remotely accessed through Queries which proceed through Tendermint and the ABCI. In addition to these request/response queries, it would be beneficial to have a means of listening to state changes as they occur by some pub-sub or streaming mechanism.

The changes proposed here are the first step towards achieving that, by enabling listening to KVStore changes at the MultiStore level. Another issue will be opened to propose and discuss the best way to stream the data out externally. As this is outside the ABCI standard, the data will not be relayed over Tendermint but rather some auxiliary server exposed by baseapp.

What problems may be addressed by introducing this feature?

Realtime data availability

What benefits does the SDK stand to gain by including this feature?

Tools for state listening should be beneficial for many cosmos applications, as a fundamental means of improving data availability/accessibility

Are there any disadvantages of including this feature?

If an application developer opts to use these features to expose data, they need to be aware of the ramifications/risks of that data exposure as it pertains to the specifics of their application

Proposal

  1. MultiStore interface would be updated with these methods:
ListeningEnabled(key types.StoreKey) bool
SetListener(key types.StoreKey, w io.Writer) Multistore
SetListeningContext(key types.StoreKey, lc types.TraceContext) Multistore
  1. The root multistore would be updated to include a new internal mapping and the new interface methods for setting/checking it
type listener struct {
       writer io.Writer
       context types.TraceContext
}

type Store struct {
	...
        listeners map[types.StoreKey]listener
}
  1. Reuse the tracekv.Store type, wrapping around individual KVStores in the same manner as is done for tracing during calls to GetKVStore (Alternatively could create a new listenkv.Store if we want to introduce a custom context, use a different payload type (rather than traceOperation), and/or use different encoding)
func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore {
	store := rs.stores[key].(types.KVStore)

	if rs.TracingEnabled() {
		store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext)
	}
        if rs.ListeningEnabled(key) {
		store = tracekv.NewStore(store, rs.listeners[key].writer, rs.listeners[key].context)
	}

	return store
}
  1. The io.Writers listening to particular KVStores can then be set (using the multistore's SetListener() method) and further exposed at the baseapp layer, the precise mechanism by which we externally expose and route these listeners is TBD

  2. Update cachemulti to fit the new MultiStore interface. Reuse store.CacheWrapWithTrace to listen to cache layer KVStorage changes in the same manner the stores are wrapped for tracing. Add an additional flag/control structure to the root multistore, so that listening in the cache layer can be turned on/off independently from the root layer (as opposed to how tracing is forwarded from root to cache wrap).


For Admin Use

  • Not duplicate issue
  • Appropriate labels applied
  • Appropriate contributors tagged
  • Contributor assigned/self-assigned
@alexanderbez
Copy link
Contributor

  • SetListener and SetListeningContext probably should not need to return a Multistore.
  • How do you see TraceContext being used for emitted data?

@alexanderbez
Copy link
Contributor

alexanderbez commented Nov 11, 2020

I think this is pretty straight forward. Feel free to whip up an ADR for this 👍

Note, the ADR should also entail how this would be configured, enabled/disabled by clients and app developers.

@i-norden
Copy link
Contributor Author

i-norden commented Nov 11, 2020

Thanks @alexanderbez! I'll do that shortly.

  • SetListener and SetListeningContext probably should not need to return a Multistore.

Roger that! That was following off SetTracer but it can just modifying itself without returning itself

  • How do you see TraceContext being used for emitted data?

I was imagining it being used to optionally provide metadata in the streamed responses or for setting flags for downstream routing. I can't think of a great example of when one would want to tack the same metadata onto each payload they stream to a consumer, but if one wanted to use the same io.Writer for multiple KVStores they could use distinct TraceContexts for each KVStore to enable routing/sorting the payloads off that single channel.

@amaury1093
Copy link
Contributor

I have one question:

SetListener(key types.StoreKey, w io.Writer) Multistore

As I understand, this listens to all changes of a whole Store. Can I get more fine-tuned listening? e.g. listen to when a specific key in the store changes value? Or listen to any prefix/array of prefixes?

The use case i'm thinking is a dapp showing your live balance. It would use ws/gRPC streaming to listen to one particular key in the bank store (the account's balance). There could then be many "smaller" listeners to any subset of a store, instead of a handful of listeners listening to the whole store.

@i-norden
Copy link
Contributor Author

i-norden commented Nov 23, 2020

Hi @amaurymartiny I really like that idea! I can imagine many instances where we only want to listen to a subset of data, and while we could perform this filtering at a level above the listener I think it makes more sense to be able to filter the data before sending it out of the MultiStore.

I'll add this to the ADR I am drafting, in this case we will create a new Store type (i.e. ListenStore) which allows us to define blacklisted and/or whitelisted keys or key prefixes for a specific io.Writer we load into it.

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 a pull request may close this issue.

3 participants