package cache import ( "Open_IM/pkg/common/db/relation" relationTb "Open_IM/pkg/common/db/table/relation" "Open_IM/pkg/common/tracelog" "Open_IM/pkg/utils" "context" "encoding/json" "github.com/dtm-labs/rockscache" "github.com/go-redis/redis/v8" "time" ) const ( friendExpireTime = time.Second * 60 * 60 * 12 friendIDsKey = "FRIEND_IDS:" TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:" friendKey = "FRIEND_INFO:" ) // args fn will exec when no data in cache type FriendCache interface { GetFriendIDs(ctx context.Context, ownerUserID string, fn func(ctx context.Context, ownerUserID string) (friendIDs []string, err error)) (friendIDs []string, err error) // call when friendID List changed DelFriendIDs(ctx context.Context, ownerUserID string) (err error) // get single friendInfo from cache GetFriend(ctx context.Context, ownerUserID, friendUserID string, fn func(ctx context.Context, ownerUserID, friendUserID string) (friend *relationTb.FriendModel, err error)) (friend *relationTb.FriendModel, err error) // del friend when friend info changed DelFriend(ctx context.Context, ownerUserID, friendUserID string) (err error) } type FriendCacheRedis struct { friendDB *relation.FriendGorm expireTime time.Duration rcClient *rockscache.Client } func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB *relation.FriendGorm, options rockscache.Options) *FriendCacheRedis { return &FriendCacheRedis{ friendDB: friendDB, expireTime: friendExpireTime, rcClient: rockscache.NewClient(rdb, options), } } func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string { return friendIDsKey + ownerUserID } func (f *FriendCacheRedis) getTwoWayFriendsIDsKey(ownerUserID string) string { return TwoWayFriendsIDsKey + ownerUserID } func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string { return friendKey + ownerUserID + "-" + friendUserID } func (f *FriendCacheRedis) GetFriendIDs(ctx context.Context, ownerUserID string, fn func(ctx context.Context, ownerUserID string) (friendIDs []string, err error)) (friendIDs []string, err error) { getFriendIDs := func() (string, error) { friendIDs, err := f.friendDB.GetFriendIDs(ctx, ownerUserID) if err != nil { return "", err } bytes, err := json.Marshal(friendIDs) if err != nil { return "", utils.Wrap(err, "") } return string(bytes), nil } defer func() { tracelog.SetCtxDebug(ctx, utils.GetFuncName(1), err, "ownerUserID", ownerUserID, "friendIDs", friendIDs) }() friendIDsStr, err := f.rcClient.Fetch(f.getFriendIDsKey(ownerUserID), f.expireTime, getFriendIDs) if err != nil { return nil, err } err = json.Unmarshal([]byte(friendIDsStr), &friendIDs) return friendIDs, utils.Wrap(err, "") } func (f *FriendCacheRedis) DelFriendIDs(ctx context.Context, ownerUserID string) (err error) { defer func() { tracelog.SetCtxDebug(ctx, utils.GetFuncName(1), err, "ownerUserID", ownerUserID) }() return f.rcClient.TagAsDeleted(f.getFriendIDsKey(ownerUserID)) } func (f *FriendCacheRedis) GetTwoWayFriendIDs(ctx context.Context, ownerUserID string) (twoWayFriendIDs []string, err error) { friendIDs, err := f.GetFriendIDs(ctx, ownerUserID) if err != nil { return nil, err } for _, friendID := range friendIDs { friendFriendID, err := f.GetFriendIDs(ctx, friendID) if err != nil { return nil, err } if utils.IsContain(ownerUserID, friendFriendID) { twoWayFriendIDs = append(twoWayFriendIDs, ownerUserID) } } return twoWayFriendIDs, nil } func (f *FriendCacheRedis) DelTwoWayFriendIDs(ctx context.Context, ownerUserID string) (err error) { defer func() { tracelog.SetCtxDebug(ctx, utils.GetFuncName(1), err, "ownerUserID", ownerUserID) }() return f.rcClient.TagAsDeleted(f.getTwoWayFriendsIDsKey(ownerUserID)) } func (f *FriendCacheRedis) GetFriend(ctx context.Context, ownerUserID, friendUserID string, fn func(ctx context.Context, ownerUserID, friendUserID string) (friend *relationTb.FriendModel, err error)) (friend *relationTb.FriendModel, err error) { getFriend := func() (string, error) { friend, err = f.friendDB.Take(ctx, ownerUserID, friendUserID) if err != nil { return "", err } bytes, err := json.Marshal(friend) if err != nil { return "", utils.Wrap(err, "") } return string(bytes), nil } friendStr, err := f.rcClient.Fetch(f.getFriendKey(ownerUserID, friendUserID), f.expireTime, getFriend) if err != nil { return nil, err } friend = &relationTb.FriendModel{} err = json.Unmarshal([]byte(friendStr), friend) return friend, utils.Wrap(err, "") } func (f *FriendCacheRedis) DelFriend(ctx context.Context, ownerUserID, friendUserID string) (err error) { defer func() { tracelog.SetCtxDebug(ctx, utils.GetFuncName(1), err, "ownerUserID", ownerUserID, "friendUserID", friendUserID) }() return f.rcClient.TagAsDeleted(f.getFriendKey(ownerUserID, friendUserID)) }