-
Notifications
You must be signed in to change notification settings - Fork 29
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
WIP: Cached KV-based store #384
Conversation
@levb Before I jump into the code: Is there a rough overview of the design of the cached store? |
@hanzei not yet, still WIP in brief - for a given The upcoming single-writer version will send the same cluster messages, but the writes will be serialized on the master, with no need for clusterMutex.Lock() |
Codecov ReportBase: 21.06% // Head: 23.73% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## lev-MM-48793-bot-subs-simplified #384 +/- ##
====================================================================
+ Coverage 21.06% 23.73% +2.66%
====================================================================
Files 81 85 +4
Lines 5473 5579 +106
====================================================================
+ Hits 1153 1324 +171
+ Misses 4181 4091 -90
- Partials 139 164 +25
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
What are the advantages of that approach vs the current |
Hypothetically:
1/5 I'd much prefer to submit both implementations, with tests to demonstrate the differences. |
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.
A few comments. I will post my high-level ones in Mattermost for a broader discussion.
server/store/cached_store.go
Outdated
Data []IndexEntry[T] | ||
} | ||
|
||
type Index[T any] map[string]*IndexEntry[T] |
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.
Does Index
need to be part of the API?
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.
Well, there's no real external API since these are wrapped in AppStore
and the likes, but I am making some types public with the intent of go-documenting them.
server/store/cached_store.go
Outdated
|
||
func MakeCachedStore[T any](name string, api plugin.API, mmapi *pluginapi.Client, logger utils.Logger) (*CachedStore[T], error) { | ||
s := &CachedStore[T]{ | ||
name: 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.
Can name
be an empty string?
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.
👍 will resolve. In theory, it could - in practice it should not be.
server/store/cached_store.go
Outdated
type cachedStoreEvent[T any] struct { | ||
Key string `json:"key"` | ||
Method string `json:"method"` | ||
SentAt time.Time `json:"sent_at"` |
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.
What is SentAt
used for?
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.
Nothing ATM, debugging and metrics later.
server/store/cached_store.go
Outdated
prevIndex, err := s.syncFromKV(true) | ||
if err != nil { | ||
return err | ||
} |
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.
Why is the sync with the KV store needed here?
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.
It just verifies that the in-memory cache is indeed the same as the KV state, based on the index hash; will not reload unless the hashes differ (which they never should)
Co-authored-by: Ben Schumacher <[email protected]>
… into lev-hashstore
…in-apps into lev-hashstore
… into lev-hashstore
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.
I have some small details I want to discuss, but I have to spend a bit more time reviewing the code.
server/config/service.go
Outdated
conf.AllowHTTPApps = true | ||
} | ||
|
||
conf.AWSAccessKey = os.Getenv(upaws.AccessEnvVar) |
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.
Why not separating code that has different responsibilities? I understand it may be because It's not used anywhere else. It could help for legibility (AWS Cloud config (142-171?), Proxy setup (103-122).
server/store/cached_store.go
Outdated
if cachedIndex.hash() != prevPersistedIndex.hash() { | ||
add, change, remove := cachedIndex.compareTo(prevPersistedIndex) | ||
if logWarnings { | ||
s.logger.Warnf("stale cache for %s, rebuilding. extra keys: %v, missing keys:%v, different values for keys:%v", s.name, remove, add, change) |
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.
Can't we log the warning and control from the outside if we want to see that level of logging and remove logWarnings
?
Co-authored-by: Ben Schumacher <[email protected]>
… into lev-hashstore
…fied' into lev-hashstore
@@ -52,7 +52,7 @@ type service struct { | |||
mattermostConfig *model.Config | |||
} | |||
|
|||
func MakeService(mm *pluginapi.Client, pliginManifest model.Manifest, botUserID string, telemetry *telemetry.Telemetry, i18nBundle *i18n.Bundle, log utils.Logger) (Service, error) { |
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.
@hanzei I took out your logging fix because I think the logging changes in this PR already handle the invalid config use case. I tested with a missing SiteURL, and get the initial logging (ensureBot), followed by the expected activate error (invalid config) logged at INFO, then exit.
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.
…ost/mattermost-plugin-apps into lev-hashstore
…ost/mattermost-plugin-apps into lev-hashstore
…ost/mattermost-plugin-apps into lev-hashstore
… into lev-better-log
…gin-apps into lev-hashstore
Summary
A cluster aware, plugin-KV based list store for things like
App
's andSubscription
's.pluginapi.cluster.Mutex
+ local testsAppStore
to use itTicket Link
TODO - several tickets
TODO
Clean()
to the CachedStore API to use indebug clean
MessageHasBeenPosted
, separate PR?