diff options
| author | William Hergès <william@herges.fr> | 2026-01-17 16:31:25 +0100 |
|---|---|---|
| committer | William Hergès <william@herges.fr> | 2026-01-17 16:31:25 +0100 |
| commit | febb77607e81fbb182dd456733ea5adafda44ed4 (patch) | |
| tree | 3ff850a34d716df8315d3b9839768e6a4ff60a4c /user | |
| parent | 55befa3a53ab56bac31026b1b6099b2d31fd6d91 (diff) | |
perf(member): use stat for load
Diffstat (limited to 'user')
| -rw-r--r-- | user/member.go | 40 | ||||
| -rw-r--r-- | user/state.go | 69 | ||||
| -rw-r--r-- | user/xp.go | 18 |
3 files changed, 93 insertions, 34 deletions
diff --git a/user/member.go b/user/member.go index 9068a6f..690f7c5 100644 --- a/user/member.go +++ b/user/member.go @@ -1,6 +1,7 @@ package user import ( + "context" "time" "github.com/anhgelus/gokord" @@ -26,26 +27,47 @@ type CopaingAccess interface { GetXP() uint } -func GetCopaing(discordID string, guildID string) *Copaing { - c := Copaing{DiscordID: discordID, GuildID: guildID} - if err := c.Load(); err != nil { - panic(err) +func GetCopaing(ctx context.Context, discordID string, guildID string) *CopaingCached { + state := GetState(ctx) + cc, err := state.Copaing(guildID, discordID) + if err != nil { + c := Copaing{DiscordID: discordID, GuildID: guildID} + if err := c.Load(ctx); err != nil { + panic(err) + } + cc = FromCopaing(&c) } - return &c + return cc } -func (c *Copaing) Load() error { - return gokord.DB. +func (c *Copaing) Load(ctx context.Context) error { + err := gokord.DB. Where("discord_id = ? and guild_id = ?", c.DiscordID, c.GuildID). Preload("CopaingXPs"). FirstOrCreate(c). Error + if err != nil { + return err + } + state := GetState(ctx) + _, err = state.CopaingAdd(c, 0) + return err } -func (c *Copaing) Save() error { +func (c *Copaing) Save(ctx context.Context) error { + state := GetState(ctx) + _, err := state.CopaingAdd(c, 0) + if err != nil { + return err + } return gokord.DB.Save(c).Error } -func (c *Copaing) Delete() error { +func (c *Copaing) Delete(ctx context.Context) error { + state := GetState(ctx) + err := state.CopaingRemove(c) + if err != nil { + return err + } return gokord.DB.Where("guild_id = ? AND discord_id = ?", c.GuildID, c.DiscordID).Delete(c).Error } diff --git a/user/state.go b/user/state.go index bef2f53..07096db 100644 --- a/user/state.go +++ b/user/state.go @@ -1,6 +1,7 @@ package user import ( + "context" "sync" "github.com/nyttikord/gokord/state" @@ -14,6 +15,35 @@ type CopaingCached struct { XPToAdd uint } +// Copaing turns a CopaingCached into a Copaing. +// This operation is heavy. +func (cc *CopaingCached) Copaing(ctx context.Context) *Copaing { + c := Copaing{DiscordID: cc.DiscordID, GuildID: cc.GuildID} + if err := c.Load(ctx); err != nil { + panic(err) + } + return &c +} + +func (cc *CopaingCached) Save(ctx context.Context) error { + state := GetState(ctx) + + state.mu.Lock() + defer state.mu.Unlock() + + return state.storage.Write(KeyCopaingCachedRaw(cc.GuildID, cc.DiscordID), *cc) +} + +func FromCopaing(c *Copaing) *CopaingCached { + return &CopaingCached{ + ID: c.ID, + DiscordID: c.DiscordID, + GuildID: c.GuildID, + XPs: calcXP(c), + XPToAdd: 0, + } +} + const KeyCopaingCachedPrefix = "cc:" func KeyCopaingCached(c *Copaing) state.Key { @@ -29,6 +59,22 @@ type State struct { storage state.MapStorage[CopaingCached] } +func NewState() *State { + return &State{ + storage: state.MapStorage[CopaingCached]{}, + } +} + +const ContextKeyState = "state" + +func GetState(ctx context.Context) *State { + return ctx.Value(ContextKeyState).(*State) +} + +func SetState(ctx context.Context, state *State) context.Context { + return context.WithValue(ctx, ContextKeyState, state) +} + func (s *State) Copaing(guildID, copaingID string) (*CopaingCached, error) { s.mu.RLock() defer s.mu.RUnlock() @@ -42,26 +88,19 @@ func (s *State) Copaing(guildID, copaingID string) (*CopaingCached, error) { } // CopaingAdd does not call Copaing.Load! -func (s *State) CopaingAdd(c *Copaing, xpToAdd uint) error { - s.mu.Lock() - defer s.mu.Unlock() - - sum := calcXP(c) +func (s *State) CopaingAdd(c *Copaing, xpToAdd uint) (*CopaingCached, error) { var err error var cc *CopaingCached - if cc, err = s.Copaing(c.GuildID, c.DiscordID); err != nil { - cc.XPs = sum + if cc, err = s.Copaing(c.GuildID, c.DiscordID); err == nil { + cc.XPs = calcXP(c) cc.XPToAdd = xpToAdd } else { - cc = &CopaingCached{ - ID: c.ID, - DiscordID: c.DiscordID, - GuildID: c.GuildID, - XPs: sum, - XPToAdd: xpToAdd, - } + cc = FromCopaing(c) } - return s.storage.Write(KeyCopaingCached(c), *cc) + s.mu.Lock() + defer s.mu.Unlock() + + return cc, s.storage.Write(KeyCopaingCached(c), *cc) } func (s *State) CopaingRemove(c *Copaing) error { @@ -1,6 +1,7 @@ package user import ( + "context" "log/slog" "slices" "sync" @@ -25,17 +26,14 @@ func (c *cXP) GetXP() uint { return c.Cxp } -func (c *Copaing) AddXP(s bot.Session, m *user.Member, xp uint, fn func(uint, uint)) { - old, err := c.GetXP(s.Logger()) - if err != nil { - s.Logger().Error("getting xp", "error", err, "user", m.DisplayName(), "guild", c.GuildID) - return - } +func (cc *CopaingCached) AddXP(ctx context.Context, s bot.Session, m *user.Member, xp uint, fn func(uint, uint)) { + old := cc.XPs pastLevel := exp.Level(old) s.Logger().Debug("adding xp", "user", m.DisplayName(), "old", old, "to add", xp) - c.CopaingXPs = append(c.CopaingXPs, CopaingXP{CopaingID: c.ID, XP: xp, GuildID: c.GuildID}) - if err = c.Save(); err != nil { - s.Logger().Error("saving user", "error", err, "user", m.DisplayName(), "xp", xp, "guild", c.GuildID) + cc.XPs += xp + cc.XPToAdd += xp + if err := cc.Save(ctx); err != nil { + s.Logger().Error("saving user in state", "error", err, "user", m.DisplayName(), "xp", xp, "guild", cc.GuildID) return } newLevel := exp.Level(old + xp) @@ -91,7 +89,7 @@ func GetBestXP(logger *slog.Logger, guildId string, n uint, d int) ([]CopaingAcc } defer rows.Close() var l []*cXP - wg := sync.WaitGroup{} + var wg sync.WaitGroup for rows.Next() { var c Copaing err = gokord.DB.ScanRows(rows, &c) |
