mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-09-07 22:20:56 +00:00
refactor(lol): remove match cache cleanup logic
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user