Files
backend/internal/repository/document_repo/tree_cache.go
T
2026-07-15 22:42:31 +08:00

138 lines
3.0 KiB
Go

package document_repo
import (
"context"
"strconv"
"strings"
"time"
"github.com/XingfenD/yoresee_doc/internal/model"
"github.com/XingfenD/yoresee_doc/pkg/cache"
"github.com/XingfenD/yoresee_doc/pkg/key"
"github.com/redis/go-redis/v9"
"github.com/sirupsen/logrus"
"golang.org/x/sync/singleflight"
"gorm.io/gorm"
)
const (
subtreeCacheTTL = 5 * time.Minute
subtreeEmptyTTL = 1 * time.Minute
subtreeCacheMaxDepth = 3
)
var subtreeCacheSF singleflight.Group
func splitPathPrefixes(path string) []string {
parts := strings.Split(path, ".")
out := make([]string, 0, len(parts))
for i := range parts {
out = append(out, strings.Join(parts[:i+1], "."))
}
return out
}
func getDocPathByID(tx *gorm.DB, docID int64) (string, error) {
type docPath struct {
Path string
}
var result docPath
err := tx.Model(&model.Document{}).
Select("path").
Where("id = ?", docID).
Take(&result).Error
if err != nil {
return "", err
}
return result.Path, nil
}
func (r *DocumentRepository) GetPathByID(id int64) (string, error) {
return getDocPathByID(r.db, id)
}
func (r *DocumentRepository) GetPathByIDWithTx(tx *gorm.DB, id int64) (string, error) {
return getDocPathByID(tx, id)
}
func (r *DocumentRepository) getSubtreeVersion(ctx context.Context, path string) (int64, error) {
if r.redis == nil {
return 0, nil
}
val, err := r.redis.Get(ctx, key.KeyDocSubtreeVersion(path)).Result()
if err == redis.Nil {
return 0, nil
}
if err != nil {
return 0, err
}
version, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return 0, nil
}
return version, nil
}
func (r *DocumentRepository) BumpSubtreeVersionsByPath(ctx context.Context, path string) error {
if r.redis == nil {
return nil
}
prefixes := splitPathPrefixes(path)
pipe := r.redis.Pipeline()
for _, prefix := range prefixes {
pipe.Incr(ctx, key.KeyDocSubtreeVersion(prefix))
}
_, err := pipe.Exec(ctx)
if err == redis.Nil {
return nil
}
return err
}
func getCachedSubtreeIDs(ctx context.Context, key string) ([]int64, bool, error) {
var ids []int64
ok, err := cache.GetJSON(ctx, key, &ids)
if err != nil {
return nil, false, err
}
if !ok {
return nil, false, nil
}
return ids, true, nil
}
func setCachedSubtreeIDs(ctx context.Context, key string, ids []int64) {
ttl := subtreeCacheTTL
if len(ids) == 0 {
ttl = subtreeEmptyTTL
}
if err := cache.SetJSON(ctx, key, ids, ttl); err != nil {
logrus.Warnf("set subtree cache failed, key=%s, err=%v", key, err)
}
}
func (r *DocumentRepository) fetchDocumentsByIDs(ids []int64) ([]*model.Document, error) {
if len(ids) == 0 {
return []*model.Document{}, nil
}
var docs []*model.Document
if err := r.db.Model(&model.Document{}).Where("id IN ?", ids).Find(&docs).Error; err != nil {
return nil, err
}
docMap := make(map[int64]*model.Document, len(docs))
for _, doc := range docs {
docMap[doc.ID] = doc
}
ordered := make([]*model.Document, 0, len(ids))
for _, id := range ids {
if doc, ok := docMap[id]; ok {
ordered = append(ordered, doc)
}
}
return ordered, nil
}