From fc8fe744f2a3f2852534330b1b07e673dedb27bb Mon Sep 17 00:00:00 2001 From: aiae Date: Fri, 9 Oct 2026 10:53:03 +0300 Subject: [PATCH 1/2] Increase the scan count --- db/scoreboard.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/scoreboard.go b/db/scoreboard.go index 80180c7..013f0ab 100644 --- a/db/scoreboard.go +++ b/db/scoreboard.go @@ -19,7 +19,7 @@ func UpdateScoreboardCache(s *Score, m *Map) error { } for _, pattern := range patterns { - iter := Redis.Scan(RedisCtx, 0, pattern, 100).Iterator() + iter := Redis.Scan(RedisCtx, 0, pattern, 10000).Iterator() for iter.Next(RedisCtx) { key := iter.Val() str, err := Redis.Get(RedisCtx, key).Result() From 64a026e0bca3b942d9418e95490f392af8be42e9 Mon Sep 17 00:00:00 2001 From: aiae Date: Fri, 9 Oct 2026 19:37:27 +0300 Subject: [PATCH 2/2] Use api v2 to delete scoreboard cache --- config.example.json | 3 +- config/config.go | 1 + db/scoreboard.go | 86 ++++++++++++++++----------------------------- 3 files changed, 33 insertions(+), 57 deletions(-) diff --git a/config.example.json b/config.example.json index 9244061..bc564f1 100644 --- a/config.example.json +++ b/config.example.json @@ -35,5 +35,6 @@ }, "api_secret_key": "", "api_base_url": "http://localhost:8082", + "internal_api_secret": "", "api_bot_jwt": "" -} \ No newline at end of file +} diff --git a/config/config.go b/config/config.go index eebb1dc..97a44ef 100644 --- a/config/config.go +++ b/config/config.go @@ -24,6 +24,7 @@ type Configuration struct { DiscordWebhookSubmissionErrors ConfigurationDiscordWebhook `json:"discord_webhook_submission_errors"` DiscordWebhookAnticheat ConfigurationDiscordWebhook `json:"discord_webhook_anticheat"` APIBaseUrl string `json:"api_base_url"` + InternalAPISecret string `json:"internal_api_secret"` APISecretKey string `json:"api_secret_key"` APIBotJWT string `json:"api_bot_jwt"` } diff --git a/db/scoreboard.go b/db/scoreboard.go index 013f0ab..d714686 100644 --- a/db/scoreboard.go +++ b/db/scoreboard.go @@ -1,71 +1,45 @@ package db import ( - "encoding/json" "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" - "github.com/go-redis/redis/v8" + "github.com/Swan/Nameless/config" ) -type scoreboardScore struct { - PerformanceRating float64 `json:"performance_rating"` +var scoreboardCacheClient = &http.Client{ + Timeout: 10 * time.Second, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, } -// UpdateScoreboardCache Updates the redis cache for a particular score -func UpdateScoreboardCache(s *Score, m *Map) error { - patterns := []string{ - fmt.Sprintf("quaver:scores:%v_*", m.Id), - fmt.Sprintf("quaver:scoreboard:%v:*", m.MD5), +// UpdateScoreboardCache clears a map's scoreboard cache through api-v2. +func UpdateScoreboardCache(_ *Score, m *Map) error { + endpoint := strings.TrimRight(config.Data.APIBaseUrl, "/") + + "/v2/private/scoreboards/" + url.PathEscape(m.MD5) + "/cache" + req, err := http.NewRequest(http.MethodDelete, endpoint, nil) + if err != nil { + return fmt.Errorf("create scoreboard cache invalidation request: %w", err) } + req.Header.Set("X-Internal-Secret", config.Data.InternalAPISecret) + req.Header.Set("Accept", "application/json") - for _, pattern := range patterns { - iter := Redis.Scan(RedisCtx, 0, pattern, 10000).Iterator() - for iter.Next(RedisCtx) { - key := iter.Val() - str, err := Redis.Get(RedisCtx, key).Result() - - if err != nil { - // The key does not exist anymore. - if err == redis.Nil { - continue - } - - return err - } - - var scores []scoreboardScore - err = json.Unmarshal([]byte(str), &scores) - - if err != nil { - return err - } - - if len(scores) < 50 { - err = Redis.Del(RedisCtx, key).Err() - - if err != nil { - return err - } - - continue - } - - for _, score := range scores { - if s.PerformanceRating > score.PerformanceRating { - err = Redis.Del(RedisCtx, key).Err() - - if err != nil { - return err - } - - break - } - } - } + response, err := scoreboardCacheClient.Do(req) + if err != nil { + return fmt.Errorf("invalidate scoreboard cache: %w", err) + } + defer response.Body.Close() - if err := iter.Err(); err != nil { - return err - } + if response.StatusCode != http.StatusOK { + return fmt.Errorf("scoreboard cache invalidation failed with status - %v", response.StatusCode) + } + if _, err := io.Copy(io.Discard, response.Body); err != nil { + return fmt.Errorf("read scoreboard cache invalidation response: %w", err) } return nil