From dce115f3f5750d0cbf2fbf53b4c5654fd78f424b Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Wed, 1 Jul 2026 16:06:03 +0700 Subject: [PATCH] perf(stats): simplify username lookup index --- internal/modules/stats/startup.go | 48 ++++++++++++++++++-- internal/modules/stats/startup_mongo_test.go | 27 +++++++++-- internal/modules/stats/usage_store.go | 3 +- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/internal/modules/stats/startup.go b/internal/modules/stats/startup.go index e85db07..06df223 100644 --- a/internal/modules/stats/startup.go +++ b/internal/modules/stats/startup.go @@ -14,7 +14,14 @@ import ( "github.com/tiennm99/miti99bot/internal/systemstate" ) -const miscCommandRenameMigrationName = "stats-command-renames-misc-20260701" +const ( + miscCommandRenameMigrationName = "stats-command-renames-misc-20260701" + + statsCommandUsersIndexName = "stats_cmd_n_user" + statsUserCommandsIndexName = "stats_uid_n_cmd" + statsUsernameLookupIndexName = "stats_user_uid" + statsLegacyUserUpdatedIndexName = "stats_user_updated_at" +) type commandRename struct { old string @@ -34,6 +41,9 @@ func InitStore(ctx context.Context, statsColl, systemColl storage.Collection) er if err := ensureUsageIndexes(ctx, mongoColl); err != nil { return err } + if err := dropIndexIfExists(ctx, mongoColl, statsLegacyUserUpdatedIndexName); err != nil { + return err + } } if err := runMiscCommandRenameMigration(ctx, statsColl, systemColl); err != nil { return err @@ -130,15 +140,15 @@ func ensureUsageIndexes(ctx context.Context, coll *mongo.Collection) error { models := []mongo.IndexModel{ { Keys: bson.D{bsonField("cmd", 1), bsonField("n", -1), bsonField("user", 1)}, - Options: options.Index().SetName("stats_cmd_n_user"), + Options: options.Index().SetName(statsCommandUsersIndexName), }, { Keys: bson.D{bsonField("uid", 1), bsonField("n", -1), bsonField("cmd", 1)}, - Options: options.Index().SetName("stats_uid_n_cmd"), + Options: options.Index().SetName(statsUserCommandsIndexName), }, { - Keys: bson.D{bsonField("user", 1), bsonField("updatedAt", -1)}, - Options: options.Index().SetName("stats_user_updated_at"), + Keys: bson.D{bsonField("user", 1), bsonField("uid", 1)}, + Options: options.Index().SetName(statsUsernameLookupIndexName), }, } if _, err := coll.Indexes().CreateMany(ctx, models); err != nil { @@ -146,3 +156,31 @@ func ensureUsageIndexes(ctx context.Context, coll *mongo.Collection) error { } return nil } + +func dropIndexIfExists(ctx context.Context, coll *mongo.Collection, name string) error { + cur, err := coll.Indexes().List(ctx) + if err != nil { + return fmt.Errorf("stats list indexes before drop %s: %w", name, err) + } + defer func() { _ = cur.Close(ctx) }() + + for cur.Next(ctx) { + var doc struct { + Name string `bson:"name"` + } + if err := cur.Decode(&doc); err != nil { + return fmt.Errorf("stats decode index before drop %s: %w", name, err) + } + if doc.Name != name { + continue + } + if err := coll.Indexes().DropOne(ctx, name); err != nil { + return fmt.Errorf("stats drop legacy index %s: %w", name, err) + } + return nil + } + if err := cur.Err(); err != nil { + return fmt.Errorf("stats index cursor before drop %s: %w", name, err) + } + return nil +} diff --git a/internal/modules/stats/startup_mongo_test.go b/internal/modules/stats/startup_mongo_test.go index 78ad1a6..9e3de7a 100644 --- a/internal/modules/stats/startup_mongo_test.go +++ b/internal/modules/stats/startup_mongo_test.go @@ -7,6 +7,10 @@ import ( "testing" "time" + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/mongo/options" + "github.com/tiennm99/miti99bot/internal/storage" ) @@ -36,14 +40,24 @@ func TestInitStore_MongoCreatesIndexes(t *testing.T) { statsColl := provider.Collection("stats") systemColl := provider.Collection("system") - if err := InitStore(ctx, statsColl, systemColl); err != nil { - t.Fatalf("InitStore: %v", err) - } - rawStatsColl, ok := storage.MongoCollection(statsColl) if !ok { t.Fatal("stats collection is not Mongo-backed") } + if _, err := rawStatsColl.Indexes().CreateOne(ctx, mongo.IndexModel{ + Keys: bson.D{bsonField("user", 1), bsonField("updatedAt", -1)}, + Options: options.Index().SetName(statsLegacyUserUpdatedIndexName), + }); err != nil { + t.Fatalf("seed legacy index: %v", err) + } + + if err := InitStore(ctx, statsColl, systemColl); err != nil { + t.Fatalf("InitStore: %v", err) + } + if err := InitStore(ctx, statsColl, systemColl); err != nil { + t.Fatalf("InitStore second run: %v", err) + } + cur, err := rawStatsColl.Indexes().List(ctx) if err != nil { t.Fatalf("list indexes: %v", err) @@ -63,9 +77,12 @@ func TestInitStore_MongoCreatesIndexes(t *testing.T) { if err := cur.Err(); err != nil { t.Fatalf("index cursor: %v", err) } - for _, name := range []string{"stats_cmd_n_user", "stats_uid_n_cmd", "stats_user_updated_at"} { + for _, name := range []string{statsCommandUsersIndexName, statsUserCommandsIndexName, statsUsernameLookupIndexName} { if !found[name] { t.Fatalf("missing index %s; indexes=%v", name, found) } } + if found[statsLegacyUserUpdatedIndexName] { + t.Fatalf("legacy index %s was not dropped; indexes=%v", statsLegacyUserUpdatedIndexName, found) + } } diff --git a/internal/modules/stats/usage_store.go b/internal/modules/stats/usage_store.go index 896e7c4..9d81f4d 100644 --- a/internal/modules/stats/usage_store.go +++ b/internal/modules/stats/usage_store.go @@ -439,8 +439,7 @@ func (s *mongoUsageStore) userIDByUsername(ctx context.Context, username string) "deleted": bson.M{"$ne": true}, }, options.FindOne(). - SetProjection(bson.M{"uid": 1}). - SetSort(bson.D{bsonField("updatedAt", -1)}), + SetProjection(bson.M{"uid": 1}), ).Decode(&doc) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) {