diff --git a/internal/modules/lol/startup.go b/internal/modules/lol/startup.go index 0351da4..136d0f1 100644 --- a/internal/modules/lol/startup.go +++ b/internal/modules/lol/startup.go @@ -25,9 +25,6 @@ const ( // no-op. func InitStore(ctx context.Context, lolColl storage.Collection) error { if mongoColl, ok := storage.MongoCollection(lolColl); ok { - if err := deleteMatchCacheDocs(ctx, mongoColl); err != nil { - return err - } if err := ensureMatchCacheTTLIndex(ctx, mongoColl); err != nil { return err } @@ -36,9 +33,6 @@ func InitStore(ctx context.Context, lolColl storage.Collection) error { } func ensureMatchCacheTTLIndex(ctx context.Context, coll *mongo.Collection) error { - if err := dropConflictingMatchCacheTTLIndex(ctx, coll); err != nil { - return err - } model := mongo.IndexModel{ Keys: bson.D{{Key: matchCacheTTLField, Value: 1}}, Options: options.Index(). @@ -52,102 +46,9 @@ func ensureMatchCacheTTLIndex(ctx context.Context, coll *mongo.Collection) error return nil } -func dropConflictingMatchCacheTTLIndex(ctx context.Context, coll *mongo.Collection) error { - cur, err := coll.Indexes().List(ctx) - if err != nil { - return fmt.Errorf("list lol indexes: %w", err) - } - defer func() { _ = cur.Close(ctx) }() - - for cur.Next(ctx) { - var doc bson.M - if err := cur.Decode(&doc); err != nil { - return fmt.Errorf("decode lol index: %w", err) - } - if doc["name"] != matchCacheTTLIndexName { - continue - } - if matchCacheTTLIndexMatches(doc) { - return nil - } - if err := coll.Indexes().DropOne(ctx, matchCacheTTLIndexName); err != nil { - return fmt.Errorf("drop old lol match cache ttl index: %w", err) - } - return nil - } - if err := cur.Err(); err != nil { - return fmt.Errorf("iterate lol indexes: %w", err) - } - return nil -} - -func matchCacheTTLIndexMatches(doc bson.M) bool { - if !singleAscendingKey(doc["key"], matchCacheTTLField) { - return false - } - if got, ok := int64BSON(doc["expireAfterSeconds"]); !ok || got != int64(matchCacheTTL/time.Second) { - return false - } - partial, ok := bsonDocument(doc["partialFilterExpression"]) - if !ok { - return false - } - idFilter, ok := bsonDocument(partial["_id"]) - if !ok { - return false - } - return idFilter["$gte"] == matchCacheKeyPrefix && idFilter["$lt"] == matchCacheKeyUpper -} - -func deleteMatchCacheDocs(ctx context.Context, coll *mongo.Collection) error { - if _, err := coll.DeleteMany(ctx, bson.D{{Key: "_id", Value: matchCacheIDRange()}}); err != nil { - return fmt.Errorf("delete old lol match cache docs: %w", err) - } - return nil -} - func matchCacheIDRange() bson.D { return bson.D{ {Key: "$gte", Value: matchCacheKeyPrefix}, {Key: "$lt", Value: matchCacheKeyUpper}, } } - -func singleAscendingKey(v any, field string) bool { - doc, ok := bsonDocument(v) - if !ok || len(doc) != 1 { - return false - } - got, ok := int64BSON(doc[field]) - return ok && got == 1 -} - -func bsonDocument(v any) (bson.M, bool) { - switch d := v.(type) { - case bson.M: - return d, true - case map[string]any: - return bson.M(d), true - case bson.D: - out := bson.M{} - for _, e := range d { - out[e.Key] = e.Value - } - return out, true - default: - return nil, false - } -} - -func int64BSON(v any) (int64, bool) { - switch n := v.(type) { - case int: - return int64(n), true - case int32: - return int64(n), true - case int64: - return n, true - default: - return 0, false - } -} diff --git a/internal/modules/lol/startup_mongo_test.go b/internal/modules/lol/startup_mongo_test.go index 3bde778..4a2e7e8 100644 --- a/internal/modules/lol/startup_mongo_test.go +++ b/internal/modules/lol/startup_mongo_test.go @@ -2,22 +2,17 @@ package lol import ( "context" - "errors" "fmt" "os" "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" ) func TestInitStore_MongoCreatesMatchCacheTTLIndex(t *testing.T) { - const legacyFetchedAtField = "fetchedAt" - uri := os.Getenv("MONGODB_TEST_URL") if uri == "" { t.Skip("MONGODB_TEST_URL not set; skipping MongoDB integration test") @@ -46,41 +41,6 @@ func TestInitStore_MongoCreatesMatchCacheTTLIndex(t *testing.T) { t.Fatal("lol collection is not Mongo-backed") } - legacyFrom := time.Date(2026, 5, 9, 0, 0, 0, 0, time.UTC) - legacyTo := legacyFrom.Add(24 * time.Hour) - legacyMatchID := cacheKey(legacyFrom, legacyTo) - legacyFetchedAt := time.Date(2026, 6, 1, 2, 3, 4, 0, time.UTC) - _, err = rawLolColl.Indexes().CreateOne(ctx, mongo.IndexModel{ - Keys: bson.D{{Key: legacyFetchedAtField, Value: 1}}, - Options: options.Index(). - SetName(matchCacheTTLIndexName). - SetExpireAfterSeconds(int32(matchCacheTTL / time.Second)). - SetPartialFilterExpression(bson.D{{Key: "_id", Value: matchCacheIDRange()}}), - }) - if err != nil { - t.Fatalf("create legacy ttl index: %v", err) - } - _, err = rawLolColl.InsertMany(ctx, []any{ - bson.D{ - {Key: "_id", Value: legacyMatchID}, - {Key: "version", Value: int64(1)}, - {Key: "updatedAt", Value: legacyFetchedAt}, - {Key: "ts", Value: legacyFetchedAt.UnixMilli()}, - {Key: legacyFetchedAtField, Value: legacyFetchedAt}, - {Key: "events", Value: bson.A{}}, - }, - bson.D{ - {Key: "_id", Value: "subscribers"}, - {Key: "version", Value: int64(1)}, - {Key: "updatedAt", Value: legacyFetchedAt}, - {Key: legacyFetchedAtField, Value: legacyFetchedAt}, - {Key: "subscribers", Value: bson.A{}}, - }, - }) - if err != nil { - t.Fatalf("insert legacy docs: %v", err) - } - if err := InitStore(ctx, lolColl); err != nil { t.Fatalf("InitStore: %v", err) } @@ -134,16 +94,6 @@ func TestInitStore_MongoCreatesMatchCacheTTLIndex(t *testing.T) { if idFilter["$gte"] != matchCacheKeyPrefix || idFilter["$lt"] != matchCacheKeyUpper { t.Fatalf("partial _id filter = %#v, want [%q, %q)", idFilter, matchCacheKeyPrefix, matchCacheKeyUpper) } - - var matchDoc bson.M - err = rawLolColl.FindOne(ctx, bson.M{"_id": legacyMatchID}).Decode(&matchDoc) - if !errors.Is(err, mongo.ErrNoDocuments) { - t.Fatalf("legacy match doc lookup err = %v, want ErrNoDocuments; doc=%#v", err, matchDoc) - } - subscriberDoc := rawLolDoc(t, ctx, rawLolColl, "subscribers") - if _, ok := subscriberDoc[legacyFetchedAtField]; !ok { - t.Fatalf("non-match doc was deleted or changed unexpectedly: %#v", subscriberDoc) - } } func bsonDoc(v any) (bson.M, bool) { @@ -175,12 +125,3 @@ func int64FromBSON(v any) (int64, bool) { return 0, false } } - -func rawLolDoc(t *testing.T, ctx context.Context, coll *mongo.Collection, id string) bson.M { - t.Helper() - var doc bson.M - if err := coll.FindOne(ctx, bson.M{"_id": id}).Decode(&doc); err != nil { - t.Fatalf("find raw lol doc %s: %v", id, err) - } - return doc -}