Closed jvc56 closed 4 months ago
stores are getting out of hand, but consolidating them into a struct doesn't solve the problems they're causing.
.Get
returns an independently deserialized copy of the same game as the first game, with independent locks.but i don't have a good solution to this that doesn't essentially amount to a full rewrite.
These started as different stores because we didn't even add Postgres until several months after we started the project. We used to store games in memory, users were all anonymous, etc. Once we started adding persistence we started creating db stores gradually (while some things remained in memory until much later, like SoughtGame, some things moved to Redis..)
Some possible solutions:
db *gorm.DB
(or its standard lib SQL equivalent). Then we can pass one connection object to all storesreturn gameplay.TimedOut(ctx, b.gameStore, b.userStore, b.notorietyStore, b.listStatStore, b.tournamentStore, evt.UserId, evt.GameId)
implies that gameplay.TimedOut
cannot possibly be transactionally correct. the more ergonomic line hides this fact.
allStores
instead of just gameplayStores
. either way, we'll have to always defensively instantiate the whole set since it's not going to be clear which stores are required by any particular code path.allStores
into ctx
. then nobody knows which functions touch the db.GetBriefProfile
benefits from caching. but i'm fine with GetBriefProfile's cache, because it's readonly, updates happen separately and would just invalidate the record instead of also being updated non-transactionally, the cache is time-bound rather than size-bound, there's no behavior/locks in there, and the read is for user consumption (it doesn't affect updates). the other caches we have tend to be full-blown entity objects that can be evicted unexpectedly, come with locks, and they are sometimes updated in-place and may go out of sync if the db fails. it's those caches that are problematic.Tx
(not DB
) as argument so that the caller can compose. but that alone may not be good enough because for update
is situational.update
... returning
" (in more places than we currently do) instead of what it recommends "call the command; when it succeeds, call the query" (in a separate transaction of course, during which time other changes may have taken place). read queries may also benefit from joins rather than separate queries. or maybe i'm misunderstanding it.This is blocking the migration of action histories, which just adds more tech debt to this problem. I think we should make this a more urgent priority, even if that amounts to a complete rewrite.
There is a
type Stores struct {
UserStore user.Store
GameStore gameplay.GameStore
SoughtGameStore gameplay.SoughtGameStore
PresenceStore user.PresenceStore
ChatStore user.ChatStore
ListStatStore stats.ListStatStore
NotorietyStore mod.NotorietyStore
TournamentStore tournament.TournamentStore
ConfigStore config.ConfigStore
SessionStore sessions.SessionStore
}
in bus.go that gets passed around a few places. Can we add whatever you need there and pass it to more places? If we do that though, I may want to pass a pointer around.
We should weto this:
return gameplay.TimedOut(ctx, b.gameStore, b.userStore, b.notorietyStore, b.listStatStore, b.tournamentStore, evt.UserId, evt.GameId)
And replace it with something like this:
return gameplay.TimedOut(ctx, gameplayStores, evt.UserId, evt.GameId)
In all places where the stores are getting out of hand.