mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-05-07 02:26:00 +08:00
feat: Optimize Scheduled Task (#2985)
* pb * fix: Modifying other fields while setting IsPrivateChat does not take effect * fix: quote message error revoke * refactoring scheduled tasks * refactoring scheduled tasks * refactoring scheduled tasks * refactoring scheduled tasks * refactoring scheduled tasks * refactoring scheduled tasks
This commit is contained in:
@@ -751,3 +751,53 @@ func (c *conversationServer) GetPinnedConversationIDs(ctx context.Context, req *
|
||||
}
|
||||
return &pbconversation.GetPinnedConversationIDsResp{ConversationIDs: conversationIDs}, nil
|
||||
}
|
||||
|
||||
func (c *conversationServer) ClearUserConversationMsg(ctx context.Context, req *pbconversation.ClearUserConversationMsgReq) (*pbconversation.ClearUserConversationMsgResp, error) {
|
||||
conversations, err := c.conversationDatabase.FindRandConversation(ctx, req.Timestamp, int(req.Limit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latestMsgDestructTime := time.UnixMilli(req.Timestamp)
|
||||
for i, conversation := range conversations {
|
||||
if conversation.IsMsgDestruct == false || conversation.MsgDestructTime == 0 {
|
||||
continue
|
||||
}
|
||||
rcpReq := &pbmsg.GetLastMessageSeqByTimeReq{ConversationID: conversation.ConversationID, Time: req.Timestamp - conversation.MsgDestructTime}
|
||||
resp, err := pbmsg.GetLastMessageSeqByTime.Invoke(ctx, rcpReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Seq <= 0 {
|
||||
log.ZDebug(ctx, "ClearUserConversationMsg GetLastMessageSeqByTime seq <= 0", "index", i, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID, "msgDestructTime", conversation.MsgDestructTime, "seq", resp.Seq)
|
||||
if err := c.setConversationMinSeqAndLatestMsgDestructTime(ctx, conversation.ConversationID, conversation.OwnerUserID, -1, latestMsgDestructTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
resp.Seq++
|
||||
if err := c.setConversationMinSeqAndLatestMsgDestructTime(ctx, conversation.ConversationID, conversation.OwnerUserID, resp.Seq, latestMsgDestructTime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "ClearUserConversationMsg set min seq", "index", i, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID, "seq", resp.Seq, "msgDestructTime", conversation.MsgDestructTime)
|
||||
}
|
||||
return &pbconversation.ClearUserConversationMsgResp{Count: int32(len(conversations))}, nil
|
||||
}
|
||||
|
||||
func (c *conversationServer) setConversationMinSeqAndLatestMsgDestructTime(ctx context.Context, conversationID string, ownerUserID string, minSeq int64, latestMsgDestructTime time.Time) error {
|
||||
update := map[string]any{
|
||||
"latest_msg_destruct_time": latestMsgDestructTime,
|
||||
}
|
||||
if minSeq >= 0 {
|
||||
req := &pbmsg.SetUserConversationMinSeqReq{ConversationID: conversationID, OwnerUserID: []string{ownerUserID}, MinSeq: minSeq}
|
||||
if _, err := pbmsg.SetUserConversationMinSeqCaller.Invoke(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
update["min_seq"] = minSeq
|
||||
}
|
||||
|
||||
if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{ownerUserID}, conversationID, update); err != nil {
|
||||
return err
|
||||
}
|
||||
c.conversationNotificationSender.ConversationChangeNotification(ctx, ownerUserID, []string{conversationID})
|
||||
return nil
|
||||
}
|
||||
|
||||
+44
-49
@@ -2,6 +2,7 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
pbconversation "github.com/openimsdk/protocol/conversation"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/wrapperspb"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
@@ -19,63 +19,50 @@ import (
|
||||
)
|
||||
|
||||
// hard delete in Database.
|
||||
func (m *msgServer) DestructMsgs(ctx context.Context, req *msg.DestructMsgsReq) (_ *msg.DestructMsgsResp, err error) {
|
||||
func (m *msgServer) DestructMsgs(ctx context.Context, req *msg.DestructMsgsReq) (*msg.DestructMsgsResp, error) {
|
||||
if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.Timestamp > time.Now().UnixMilli() {
|
||||
return nil, errs.ErrArgs.WrapMsg("request millisecond timestamp error")
|
||||
}
|
||||
var (
|
||||
docNum int
|
||||
msgNum int
|
||||
start = time.Now()
|
||||
getLimit = 5000
|
||||
)
|
||||
|
||||
destructMsg := func(ctx context.Context) (bool, error) {
|
||||
docIDs, err := m.MsgDatabase.GetDocIDs(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
msgs, err := m.MsgDatabase.GetBeforeMsg(ctx, req.Timestamp, docIDs, getLimit)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(msgs) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
index, err := m.MsgDatabase.DeleteDocMsgBefore(ctx, req.Timestamp, msg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(index) == 0 {
|
||||
return false, errs.ErrInternalServer.WrapMsg("delete doc msg failed")
|
||||
}
|
||||
|
||||
docNum++
|
||||
msgNum += len(index)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
_, err = destructMsg(ctx)
|
||||
docs, err := m.MsgDatabase.GetRandBeforeMsg(ctx, req.Timestamp, int(req.Limit))
|
||||
if err != nil {
|
||||
log.ZError(ctx, "clear msg failed", err, "docNum", docNum, "msgNum", msgNum, "cost", time.Since(start))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "clearing message", "docNum", docNum, "msgNum", msgNum, "cost", time.Since(start))
|
||||
|
||||
return &msg.DestructMsgsResp{}, nil
|
||||
for i, doc := range docs {
|
||||
if err := m.MsgDatabase.DeleteDoc(ctx, doc.DocID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "DestructMsgs delete doc", "index", i, "docID", doc.DocID)
|
||||
index := strings.LastIndex(doc.DocID, ":")
|
||||
if index < 0 {
|
||||
continue
|
||||
}
|
||||
var minSeq int64
|
||||
for _, model := range doc.Msg {
|
||||
if model.Msg == nil {
|
||||
continue
|
||||
}
|
||||
if model.Msg.Seq > minSeq {
|
||||
minSeq = model.Msg.Seq
|
||||
}
|
||||
}
|
||||
if minSeq <= 0 {
|
||||
continue
|
||||
}
|
||||
conversationID := doc.DocID[:index]
|
||||
if conversationID == "" {
|
||||
continue
|
||||
}
|
||||
minSeq++
|
||||
if err := m.MsgDatabase.SetMinSeq(ctx, conversationID, minSeq); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "DestructMsgs delete doc set min seq", "index", i, "docID", doc.DocID, "conversationID", conversationID, "setMinSeq", minSeq)
|
||||
}
|
||||
return &msg.DestructMsgsResp{Count: int32(len(docs))}, nil
|
||||
}
|
||||
|
||||
// soft delete for user self
|
||||
func (m *msgServer) ClearMsg(ctx context.Context, req *msg.ClearMsgReq) (_ *msg.ClearMsgResp, err error) {
|
||||
func (m *msgServer) ClearMsg(ctx context.Context, req *msg.ClearMsgReq) (*msg.ClearMsgResp, error) {
|
||||
temp := convert.ConversationsPb2DB(req.Conversations)
|
||||
|
||||
batchNum := 100
|
||||
@@ -134,3 +121,11 @@ func (m *msgServer) ClearMsg(ctx context.Context, req *msg.ClearMsgReq) (_ *msg.
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *msgServer) GetLastMessageSeqByTime(ctx context.Context, req *msg.GetLastMessageSeqByTimeReq) (*msg.GetLastMessageSeqByTimeResp, error) {
|
||||
seq, err := m.MsgDatabase.GetLastMessageSeqByTime(ctx, req.ConversationID, req.Time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg.GetLastMessageSeqByTimeResp{Seq: seq}, nil
|
||||
}
|
||||
|
||||
+22
-77
@@ -19,17 +19,14 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/protocol/third"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
@@ -288,87 +285,35 @@ func (t *thirdServer) apiAddress(prefix, name string) string {
|
||||
}
|
||||
|
||||
func (t *thirdServer) DeleteOutdatedData(ctx context.Context, req *third.DeleteOutdatedDataReq) (*third.DeleteOutdatedDataResp, error) {
|
||||
var conf config.Third
|
||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
engine := t.config.RpcConfig.Object.Enable
|
||||
expireTime := time.UnixMilli(req.ExpireTime)
|
||||
|
||||
findPagination := &sdkws.RequestPagination{
|
||||
PageNumber: 1,
|
||||
ShowNumber: 500,
|
||||
}
|
||||
|
||||
// Find all expired data in S3 database
|
||||
total, models, err := t.s3dataBase.FindNeedDeleteObjectByDB(ctx, expireTime, req.ObjectGroup, findPagination)
|
||||
if err != nil && errs.Unwrap(err) != mongo.ErrNoDocuments {
|
||||
return nil, errs.Wrap(err)
|
||||
models, err := t.s3dataBase.FindExpirationObject(ctx, engine, expireTime, req.ObjectGroup, int64(req.Limit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
log.ZDebug(ctx, "Not have OutdatedData", "delete Total", total)
|
||||
return &third.DeleteOutdatedDataResp{Count: int32(total)}, nil
|
||||
}
|
||||
|
||||
needDelObjectKeys := make([]string, len(models))
|
||||
for _, model := range models {
|
||||
needDelObjectKeys = append(needDelObjectKeys, model.Key)
|
||||
}
|
||||
|
||||
// Remove duplicate keys, have the same key use in different models
|
||||
needDelObjectKeys = datautil.Distinct(needDelObjectKeys)
|
||||
|
||||
for _, key := range needDelObjectKeys {
|
||||
// Find all models by key
|
||||
keyModels, err := t.s3dataBase.FindModelsByKey(ctx, key)
|
||||
if err != nil && errs.Unwrap(err) != mongo.ErrNoDocuments {
|
||||
for i, obj := range models {
|
||||
if err := t.s3dataBase.DeleteSpecifiedData(ctx, engine, []string{obj.Name}); err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
// check keyModels, if all keyModels.
|
||||
needDelKey := true // Default can delete
|
||||
for _, keymodel := range keyModels {
|
||||
// If group is empty or CreateTime is after expireTime, can't delete this key
|
||||
if keymodel.Group == "" || keymodel.CreateTime.After(expireTime) {
|
||||
needDelKey = false
|
||||
break
|
||||
}
|
||||
if err := t.s3dataBase.DelS3Key(ctx, engine, obj.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If this object is not referenced by not expire data, delete it
|
||||
if needDelKey && t.minio != nil {
|
||||
// If have a thumbnail, delete it
|
||||
thumbnailKey, _ := t.getMinioImageThumbnailKey(ctx, key)
|
||||
if thumbnailKey != "" {
|
||||
err := t.s3dataBase.DeleteObject(ctx, thumbnailKey)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "Delete thumbnail object is error:", errs.Wrap(err), "thumbnailKey", thumbnailKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete object
|
||||
err = t.s3dataBase.DeleteObject(ctx, key)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "Delete object is error", errs.Wrap(err), "object key", key)
|
||||
}
|
||||
|
||||
// Delete cache key
|
||||
err = t.s3dataBase.DelS3Key(ctx, conf.Object.Enable, key)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "Delete cache key is error:", errs.Wrap(err), "cache S3 key:", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle delete data in S3 database
|
||||
for _, model := range models {
|
||||
// Delete all expired data row in S3 database
|
||||
err := t.s3dataBase.DeleteSpecifiedData(ctx, model.Engine, model.Name)
|
||||
count, err := t.s3dataBase.GetKeyCount(ctx, engine, obj.Key)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
return nil, err
|
||||
}
|
||||
log.ZDebug(ctx, "delete s3 object record", "index", i, "s3", obj, "count", count)
|
||||
if count == 0 {
|
||||
if err := t.s3.DeleteObject(ctx, obj.Key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "DeleteOutdatedData", "delete Total", total)
|
||||
|
||||
return &third.DeleteOutdatedDataResp{Count: int32(total)}, nil
|
||||
return &third.DeleteOutdatedDataResp{Count: int32(len(models))}, nil
|
||||
}
|
||||
|
||||
type FormDataMate struct {
|
||||
|
||||
@@ -44,7 +44,7 @@ type thirdServer struct {
|
||||
userRpcClient rpcclient.UserRpcClient
|
||||
defaultExpire time.Duration
|
||||
config *Config
|
||||
minio *minio.Minio
|
||||
s3 s3.Interface
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -79,13 +79,11 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
|
||||
// Select the oss method according to the profile policy
|
||||
enable := config.RpcConfig.Object.Enable
|
||||
var (
|
||||
o s3.Interface
|
||||
minioCli *minio.Minio
|
||||
o s3.Interface
|
||||
)
|
||||
switch enable {
|
||||
case "minio":
|
||||
minioCli, err = minio.NewMinio(ctx, redis.NewMinioCache(rdb), *config.MinioConfig.Build())
|
||||
o = minioCli
|
||||
o, err = minio.NewMinio(ctx, redis.NewMinioCache(rdb), *config.MinioConfig.Build())
|
||||
case "cos":
|
||||
o, err = cos.NewCos(*config.RpcConfig.Object.Cos.Build())
|
||||
case "oss":
|
||||
@@ -105,15 +103,11 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg
|
||||
s3dataBase: controller.NewS3Database(rdb, o, s3db),
|
||||
defaultExpire: time.Hour * 24 * 7,
|
||||
config: config,
|
||||
minio: minioCli,
|
||||
s3: o,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *thirdServer) getMinioImageThumbnailKey(ctx context.Context, name string) (string, error) {
|
||||
return t.minio.GetImageThumbnailKey(ctx, name)
|
||||
}
|
||||
|
||||
func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) {
|
||||
err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user