mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-07-25 02:21:01 +00:00
perf(stats): simplify username lookup index
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user