mirror of
https://github.com/openimsdk/open-im-server.git
synced 2025-10-28 06:22:12 +08:00
Merge branch 'openimsdk:main' into main
This commit is contained in:
commit
492f7a0ee9
@ -11,11 +11,20 @@ templates:
|
||||
- /etc/alertmanager/email.tmpl
|
||||
|
||||
route:
|
||||
group_by: ['alertname']
|
||||
group_by: [ 'alertname' ]
|
||||
group_wait: 5s
|
||||
group_interval: 5s
|
||||
repeat_interval: 5m
|
||||
receiver: email
|
||||
routes:
|
||||
- matchers:
|
||||
- alertname = "XXX"
|
||||
group_by: [ 'instance' ]
|
||||
group_wait: 5s
|
||||
group_interval: 5s
|
||||
repeat_interval: 5m
|
||||
receiver: email
|
||||
|
||||
receivers:
|
||||
- name: email
|
||||
email_configs:
|
||||
|
||||
5356
config/grafana-template/Demo.json
Normal file
5356
config/grafana-template/Demo.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,3 +20,25 @@ groups:
|
||||
annotations:
|
||||
summary: "Increase in MsgInsertRedisFailedCounter or MsgInsertMongoFailedCounter detected"
|
||||
description: "Either MsgInsertRedisFailedCounter or MsgInsertMongoFailedCounter has increased in the last 5 minutes, indicating failures in message insert operations to Redis or MongoDB,maybe the redis or mongodb is crash."
|
||||
|
||||
- name: registrations_few
|
||||
rules:
|
||||
- alert: RegistrationsFew
|
||||
expr: increase(user_login_total[1h]) == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: info
|
||||
annotations:
|
||||
summary: "Too few registrations within the time frame"
|
||||
description: "The number of registrations in the last hour is 0. There might be some issues."
|
||||
|
||||
- name: messages_few
|
||||
rules:
|
||||
- alert: MessagesFew
|
||||
expr: (increase(single_chat_msg_process_success_total[1h])+increase(group_chat_msg_process_success_total[1h])) == 0
|
||||
for: 1m
|
||||
labels:
|
||||
severity: info
|
||||
annotations:
|
||||
summary: "Too few messages within the time frame"
|
||||
description: "The number of messages sent in the last hour is 0. There might be some issues."
|
||||
|
||||
2
go.mod
2
go.mod
@ -12,7 +12,7 @@ require (
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.30
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.38
|
||||
github.com/openimsdk/tools v0.0.49-alpha.51
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
|
||||
4
go.sum
4
go.sum
@ -319,8 +319,8 @@ github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
|
||||
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
|
||||
github.com/openimsdk/gomake v0.0.14-alpha.5 h1:VY9c5x515lTfmdhhPjMvR3BBRrRquAUCFsz7t7vbv7Y=
|
||||
github.com/openimsdk/gomake v0.0.14-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.30 h1:OXzCIpDpIY/GI6h1SDYWN51OS9Xv/BcHaOwq8whPKqI=
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.30/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8=
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.38 h1:kVZCHIXg/el8YJFoIBWhZu1sbbTUqmzgF4l0W3sUH24=
|
||||
github.com/openimsdk/protocol v0.0.69-alpha.38/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8=
|
||||
github.com/openimsdk/tools v0.0.49-alpha.51 h1:JTPEetVSNOczw1n+XjiPozaH2SBPQAc+9VlPE41wEeY=
|
||||
github.com/openimsdk/tools v0.0.49-alpha.51/go.mod h1:h1cYmfyaVtgFbKmb1Cfsl8XwUOMTt8ubVUQrdGtsUh4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
|
||||
@ -19,8 +19,6 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/tools/a2r"
|
||||
"github.com/openimsdk/tools/apiresp"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
type GroupApi rpcclient.Group
|
||||
@ -148,45 +146,7 @@ func (o *GroupApi) GetIncrementalGroupMember(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (o *GroupApi) GetIncrementalGroupMemberBatch(c *gin.Context) {
|
||||
type BatchIncrementalReq struct {
|
||||
UserID string `json:"user_id"`
|
||||
List []*group.GetIncrementalGroupMemberReq `json:"list"`
|
||||
}
|
||||
type BatchIncrementalResp struct {
|
||||
List map[string]*group.GetIncrementalGroupMemberResp `json:"list"`
|
||||
}
|
||||
req, err := a2r.ParseRequestNotCheck[BatchIncrementalReq](c)
|
||||
if err != nil {
|
||||
apiresp.GinError(c, err)
|
||||
return
|
||||
}
|
||||
resp := &BatchIncrementalResp{
|
||||
List: make(map[string]*group.GetIncrementalGroupMemberResp),
|
||||
}
|
||||
var (
|
||||
changeCount int
|
||||
)
|
||||
for _, req := range req.List {
|
||||
if _, ok := resp.List[req.GroupID]; ok {
|
||||
continue
|
||||
}
|
||||
res, err := o.Client.GetIncrementalGroupMember(c, req)
|
||||
if err != nil {
|
||||
if len(resp.List) == 0 {
|
||||
apiresp.GinError(c, err)
|
||||
} else {
|
||||
log.ZError(c, "group incr sync versopn", err, "groupID", req.GroupID, "success", len(resp.List))
|
||||
apiresp.GinSuccess(c, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
resp.List[req.GroupID] = res
|
||||
changeCount += len(res.Insert) + len(res.Delete) + len(res.Update)
|
||||
if changeCount >= 200 {
|
||||
break
|
||||
}
|
||||
}
|
||||
apiresp.GinSuccess(c, resp)
|
||||
a2r.Call(group.GroupClient.BatchGetIncrementalGroupMember, o.Client, c)
|
||||
}
|
||||
|
||||
func (o *GroupApi) GetFullGroupMemberUserIDs(c *gin.Context) {
|
||||
|
||||
@ -75,8 +75,8 @@ type Client struct {
|
||||
token string
|
||||
hbCtx context.Context
|
||||
hbCancel context.CancelFunc
|
||||
subLock sync.Mutex
|
||||
subUserIDs map[string]struct{}
|
||||
subLock *sync.Mutex
|
||||
subUserIDs map[string]struct{} // client conn subscription list
|
||||
}
|
||||
|
||||
// ResetClient updates the client's state with new connection and context information.
|
||||
@ -94,14 +94,21 @@ func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, longConnServer
|
||||
c.closedErr = nil
|
||||
c.token = ctx.GetToken()
|
||||
c.hbCtx, c.hbCancel = context.WithCancel(c.ctx)
|
||||
c.subLock = new(sync.Mutex)
|
||||
if c.subUserIDs != nil {
|
||||
clear(c.subUserIDs)
|
||||
}
|
||||
c.subUserIDs = make(map[string]struct{})
|
||||
}
|
||||
|
||||
func (c *Client) pingHandler(_ string) error {
|
||||
func (c *Client) pingHandler(appData string) error {
|
||||
if err := c.conn.SetReadDeadline(pongWait); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.writePongMsg()
|
||||
log.ZDebug(c.ctx, "ping Handler Success.", "appData", appData)
|
||||
|
||||
return c.writePongMsg(appData)
|
||||
}
|
||||
|
||||
func (c *Client) pongHandler(_ string) error {
|
||||
@ -156,7 +163,7 @@ func (c *Client) readMessage() {
|
||||
return
|
||||
|
||||
case PingMessage:
|
||||
err := c.writePongMsg()
|
||||
err := c.writePongMsg("")
|
||||
log.ZError(c.ctx, "writePongMsg", err)
|
||||
|
||||
case CloseMessage:
|
||||
@ -244,13 +251,11 @@ func (c *Client) setAppBackgroundStatus(ctx context.Context, req *Req) ([]byte,
|
||||
}
|
||||
|
||||
func (c *Client) close() {
|
||||
c.w.Lock()
|
||||
defer c.w.Unlock()
|
||||
if c.closed.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
c.w.Lock()
|
||||
defer c.w.Unlock()
|
||||
|
||||
c.closed.Store(true)
|
||||
c.conn.Close()
|
||||
c.hbCancel() // Close server-initiated heartbeat.
|
||||
@ -311,6 +316,14 @@ func (c *Client) KickOnlineMessage() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) PushUserOnlineStatus(data []byte) error {
|
||||
resp := Resp{
|
||||
ReqIdentifier: WsSubUserOnlineStatus,
|
||||
Data: data,
|
||||
}
|
||||
return c.writeBinaryMsg(resp)
|
||||
}
|
||||
|
||||
func (c *Client) writeBinaryMsg(resp Resp) error {
|
||||
if c.closed.Load() {
|
||||
return nil
|
||||
@ -378,7 +391,7 @@ func (c *Client) writePingMsg() error {
|
||||
return c.conn.WriteMessage(PingMessage, nil)
|
||||
}
|
||||
|
||||
func (c *Client) writePongMsg() error {
|
||||
func (c *Client) writePongMsg(appData string) error {
|
||||
if c.closed.Load() {
|
||||
return nil
|
||||
}
|
||||
@ -391,5 +404,5 @@ func (c *Client) writePongMsg() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.conn.WriteMessage(PongMessage, nil)
|
||||
return c.conn.WriteMessage(PongMessage, []byte(appData))
|
||||
}
|
||||
|
||||
@ -2,15 +2,11 @@ package msggateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/openimsdk/tools/utils/idutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (ws *WsServer) subscriberUserOnlineStatusChanges(ctx context.Context, userID string, platformIDs []int32) {
|
||||
@ -45,33 +41,19 @@ func (ws *WsServer) SubUserOnlineStatus(ctx context.Context, client *Client, dat
|
||||
return proto.Marshal(&resp)
|
||||
}
|
||||
|
||||
type subClient struct {
|
||||
clients map[string]*Client
|
||||
}
|
||||
|
||||
func newSubscription() *Subscription {
|
||||
return &Subscription{
|
||||
userIDs: make(map[string]*subClient),
|
||||
}
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
lock sync.RWMutex
|
||||
userIDs map[string]*subClient
|
||||
type subClient struct {
|
||||
clients map[string]*Client
|
||||
}
|
||||
|
||||
func (s *Subscription) GetClient(userID string) []*Client {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
cs, ok := s.userIDs[userID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
clients := make([]*Client, 0, len(cs.clients))
|
||||
for _, client := range cs.clients {
|
||||
clients = append(clients, client)
|
||||
}
|
||||
return clients
|
||||
type Subscription struct {
|
||||
lock sync.RWMutex
|
||||
userIDs map[string]*subClient // subscribe to the user's client connection
|
||||
}
|
||||
|
||||
func (s *Subscription) DelClient(client *Client) {
|
||||
@ -99,6 +81,20 @@ func (s *Subscription) DelClient(client *Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Subscription) GetClient(userID string) []*Client {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
cs, ok := s.userIDs[userID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
clients := make([]*Client, 0, len(cs.clients))
|
||||
for _, client := range cs.clients {
|
||||
clients = append(clients, client)
|
||||
}
|
||||
return clients
|
||||
}
|
||||
|
||||
func (s *Subscription) Sub(client *Client, addUserIDs, delUserIDs []string) {
|
||||
if len(addUserIDs)+len(delUserIDs) == 0 {
|
||||
return
|
||||
@ -121,6 +117,7 @@ func (s *Subscription) Sub(client *Client, addUserIDs, delUserIDs []string) {
|
||||
continue
|
||||
}
|
||||
client.subUserIDs[userID] = struct{}{}
|
||||
add[userID] = struct{}{}
|
||||
}
|
||||
client.subLock.Unlock()
|
||||
if len(del)+len(add) == 0 {
|
||||
@ -154,28 +151,16 @@ func (ws *WsServer) pushUserIDOnlineStatus(ctx context.Context, userID string, p
|
||||
if len(clients) == 0 {
|
||||
return
|
||||
}
|
||||
msgContent, err := json.Marshal(platformIDs)
|
||||
onlineStatus, err := proto.Marshal(&sdkws.SubUserOnlineStatusTips{
|
||||
Subscribers: []*sdkws.SubUserOnlineStatusElem{{UserID: userID, OnlinePlatformIDs: platformIDs}},
|
||||
})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "pushUserIDOnlineStatus json.Marshal", err)
|
||||
return
|
||||
}
|
||||
now := time.Now().UnixMilli()
|
||||
msgID := idutil.GetMsgIDByMD5(userID)
|
||||
msg := &sdkws.MsgData{
|
||||
SendID: userID,
|
||||
ClientMsgID: msgID,
|
||||
ServerMsgID: msgID,
|
||||
SenderPlatformID: constant.AdminPlatformID,
|
||||
SessionType: constant.NotificationChatType,
|
||||
ContentType: constant.UserSubscribeOnlineStatusNotification,
|
||||
Content: msgContent,
|
||||
SendTime: now,
|
||||
CreateTime: now,
|
||||
}
|
||||
for _, client := range clients {
|
||||
msg.RecvID = client.UserID
|
||||
if err := client.PushMessage(ctx, msg); err != nil {
|
||||
log.ZError(ctx, "UserSubscribeOnlineStatusNotification push failed", err, "userID", client.UserID, "platformID", client.PlatformID, "changeUserID", userID, "content", msgContent)
|
||||
if err := client.PushUserOnlineStatus(onlineStatus); err != nil {
|
||||
log.ZError(ctx, "UserSubscribeOnlineStatusNotification push failed", err, "userID", client.UserID, "platformID", client.PlatformID, "changeUserID", userID, "changePlatformID", platformIDs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,9 +358,7 @@ func (ws *WsServer) unregisterClient(client *Client) {
|
||||
prommetrics.OnlineUserGauge.Dec()
|
||||
}
|
||||
ws.onlineUserConnNum.Add(-1)
|
||||
client.subLock.Lock()
|
||||
clear(client.subUserIDs)
|
||||
client.subLock.Unlock()
|
||||
ws.subscription.DelClient(client)
|
||||
//ws.SetUserOnlineStatus(client.ctx, client, constant.Offline)
|
||||
log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num",
|
||||
ws.onlineUserNum.Load(), "online user conn Num",
|
||||
|
||||
@ -2,6 +2,7 @@ package conversation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/hashutil"
|
||||
@ -40,7 +41,6 @@ func (c *conversationServer) GetIncrementalConversation(ctx context.Context, req
|
||||
Find: func(ctx context.Context, conversationIDs []string) ([]*conversation.Conversation, error) {
|
||||
return c.getConversations(ctx, req.UserID, conversationIDs)
|
||||
},
|
||||
ID: func(elem *conversation.Conversation) string { return elem.GroupID },
|
||||
Resp: func(version *model.VersionLog, delIDs []string, insertList, updateList []*conversation.Conversation, full bool) *conversation.GetIncrementalConversationResp {
|
||||
return &conversation.GetIncrementalConversationResp{
|
||||
VersionID: version.ID.Hex(),
|
||||
|
||||
@ -16,6 +16,8 @@ package friend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx"
|
||||
|
||||
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
|
||||
@ -191,10 +193,37 @@ func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context
|
||||
f.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) setVersion(ctx context.Context, version *uint64, versionID *string, collName string, id string) {
|
||||
versions := versionctx.GetVersionLog(ctx).Get()
|
||||
for _, coll := range versions {
|
||||
if coll.Name == collName && coll.Doc.DID == id {
|
||||
*version = uint64(coll.Doc.Version)
|
||||
*versionID = coll.Doc.ID.Hex()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) setSortVersion(ctx context.Context, version *uint64, versionID *string, collName string, id string, sortVersion *uint64) {
|
||||
versions := versionctx.GetVersionLog(ctx).Get()
|
||||
for _, coll := range versions {
|
||||
if coll.Name == collName && coll.Doc.DID == id {
|
||||
*version = uint64(coll.Doc.Version)
|
||||
*versionID = coll.Doc.ID.Hex()
|
||||
for _, elem := range coll.Doc.Logs {
|
||||
if elem.EID == relationtb.VersionSortChangeID {
|
||||
*sortVersion = uint64(elem.Version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) {
|
||||
tips := sdkws.FriendInfoChangedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.FromUserID = fromUserID
|
||||
tips.FromToUserID.ToUserID = toUserID
|
||||
f.setSortVersion(ctx, &tips.FriendVersion, &tips.FriendVersionID, database.FriendVersionName, toUserID, &tips.FriendSortVersion)
|
||||
f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/hashutil"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"slices"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
@ -52,25 +53,40 @@ func (s *friendServer) GetIncrementalFriends(ctx context.Context, req *relation.
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var sortVersion uint64
|
||||
opt := incrversion.Option[*sdkws.FriendInfo, relation.GetIncrementalFriendsResp]{
|
||||
Ctx: ctx,
|
||||
VersionKey: req.UserID,
|
||||
VersionID: req.VersionID,
|
||||
VersionNumber: req.Version,
|
||||
Version: s.db.FindFriendIncrVersion,
|
||||
Ctx: ctx,
|
||||
VersionKey: req.UserID,
|
||||
VersionID: req.VersionID,
|
||||
VersionNumber: req.Version,
|
||||
Version: func(ctx context.Context, ownerUserID string, version uint, limit int) (*model.VersionLog, error) {
|
||||
vl, err := s.db.FindFriendIncrVersion(ctx, ownerUserID, version, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vl.Logs = slices.DeleteFunc(vl.Logs, func(elem model.VersionLogElem) bool {
|
||||
if elem.EID == model.VersionSortChangeID {
|
||||
vl.LogLen--
|
||||
sortVersion = uint64(elem.Version)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
return vl, nil
|
||||
},
|
||||
CacheMaxVersion: s.db.FindMaxFriendVersionCache,
|
||||
Find: func(ctx context.Context, ids []string) ([]*sdkws.FriendInfo, error) {
|
||||
return s.getFriend(ctx, req.UserID, ids)
|
||||
},
|
||||
ID: func(elem *sdkws.FriendInfo) string { return elem.FriendUser.UserID },
|
||||
Resp: func(version *model.VersionLog, deleteIds []string, insertList, updateList []*sdkws.FriendInfo, full bool) *relation.GetIncrementalFriendsResp {
|
||||
return &relation.GetIncrementalFriendsResp{
|
||||
VersionID: version.ID.Hex(),
|
||||
Version: uint64(version.Version),
|
||||
Full: full,
|
||||
Delete: deleteIds,
|
||||
Insert: insertList,
|
||||
Update: updateList,
|
||||
VersionID: version.ID.Hex(),
|
||||
Version: uint64(version.Version),
|
||||
Full: full,
|
||||
Delete: deleteIds,
|
||||
Insert: insertList,
|
||||
Update: updateList,
|
||||
SortVersion: sortVersion,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -306,6 +306,21 @@ func (g *GroupNotificationSender) setVersion(ctx context.Context, version *uint6
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) setSortVersion(ctx context.Context, version *uint64, versionID *string, collName string, id string, sortVersion *uint64) {
|
||||
versions := versionctx.GetVersionLog(ctx).Get()
|
||||
for _, coll := range versions {
|
||||
if coll.Name == collName && coll.Doc.DID == id {
|
||||
*version = uint64(coll.Doc.Version)
|
||||
*versionID = coll.Doc.ID.Hex()
|
||||
for _, elem := range coll.Doc.Logs {
|
||||
if elem.EID == model.VersionSortChangeID {
|
||||
*sortVersion = uint64(elem.Version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) {
|
||||
var err error
|
||||
defer func() {
|
||||
@ -707,7 +722,7 @@ func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Con
|
||||
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return
|
||||
}
|
||||
g.setVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID)
|
||||
g.setSortVersion(ctx, &tips.GroupMemberVersion, &tips.GroupMemberVersionID, database.GroupMemberVersionName, tips.Group.GroupID, &tips.GroupSortVersion)
|
||||
g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips)
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/incrversion"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
|
||||
@ -10,7 +11,8 @@ import (
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
pbgroup "github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"slices"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
)
|
||||
|
||||
func (s *groupServer) GetFullGroupMemberUserIDs(ctx context.Context, req *pbgroup.GetFullGroupMemberUserIDsReq) (*pbgroup.GetFullGroupMemberUserIDsResp, error) {
|
||||
@ -63,7 +65,10 @@ func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgrou
|
||||
if group.Status == constant.GroupStatusDismissed {
|
||||
return nil, servererrs.ErrDismissedAlready.Wrap()
|
||||
}
|
||||
var hasGroupUpdate bool
|
||||
var (
|
||||
hasGroupUpdate bool
|
||||
sortVersion uint64
|
||||
)
|
||||
opt := incrversion.Option[*sdkws.GroupMemberFullInfo, pbgroup.GetIncrementalGroupMemberResp]{
|
||||
Ctx: ctx,
|
||||
VersionKey: req.GroupID,
|
||||
@ -74,14 +79,20 @@ func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgrou
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vl.Logs = slices.DeleteFunc(vl.Logs, func(elem model.VersionLogElem) bool {
|
||||
if elem.EID == "" {
|
||||
logs := make([]model.VersionLogElem, 0, len(vl.Logs))
|
||||
for i, log := range vl.Logs {
|
||||
switch log.EID {
|
||||
case model.VersionGroupChangeID:
|
||||
vl.LogLen--
|
||||
hasGroupUpdate = true
|
||||
return true
|
||||
case model.VersionSortChangeID:
|
||||
vl.LogLen--
|
||||
sortVersion = uint64(log.Version)
|
||||
default:
|
||||
logs = append(logs, vl.Logs[i])
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
vl.Logs = logs
|
||||
if vl.LogLen > 0 {
|
||||
hasGroupUpdate = true
|
||||
}
|
||||
@ -91,15 +102,15 @@ func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgrou
|
||||
Find: func(ctx context.Context, ids []string) ([]*sdkws.GroupMemberFullInfo, error) {
|
||||
return s.getGroupMembersInfo(ctx, req.GroupID, ids)
|
||||
},
|
||||
ID: func(elem *sdkws.GroupMemberFullInfo) string { return elem.UserID },
|
||||
Resp: func(version *model.VersionLog, delIDs []string, insertList, updateList []*sdkws.GroupMemberFullInfo, full bool) *pbgroup.GetIncrementalGroupMemberResp {
|
||||
return &pbgroup.GetIncrementalGroupMemberResp{
|
||||
VersionID: version.ID.Hex(),
|
||||
Version: uint64(version.Version),
|
||||
Full: full,
|
||||
Delete: delIDs,
|
||||
Insert: insertList,
|
||||
Update: updateList,
|
||||
VersionID: version.ID.Hex(),
|
||||
Version: uint64(version.Version),
|
||||
Full: full,
|
||||
Delete: delIDs,
|
||||
Insert: insertList,
|
||||
Update: updateList,
|
||||
SortVersion: sortVersion,
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -121,6 +132,150 @@ func (s *groupServer) GetIncrementalGroupMember(ctx context.Context, req *pbgrou
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *groupServer) BatchGetIncrementalGroupMember(ctx context.Context, req *pbgroup.BatchGetIncrementalGroupMemberReq) (resp *pbgroup.BatchGetIncrementalGroupMemberResp, err error) {
|
||||
type VersionInfo struct {
|
||||
GroupID string
|
||||
VersionID string
|
||||
VersionNumber uint64
|
||||
}
|
||||
|
||||
var groupIDs []string
|
||||
|
||||
groupsVersionMap := make(map[string]*VersionInfo)
|
||||
groupsMap := make(map[string]*model.Group)
|
||||
hasGroupUpdateMap := make(map[string]bool)
|
||||
sortVersionMap := make(map[string]uint64)
|
||||
|
||||
var targetKeys, versionIDs []string
|
||||
var versionNumbers []uint64
|
||||
|
||||
var requestBodyLen int
|
||||
|
||||
for _, group := range req.ReqList {
|
||||
groupsVersionMap[group.GroupID] = &VersionInfo{
|
||||
GroupID: group.GroupID,
|
||||
VersionID: group.VersionID,
|
||||
VersionNumber: group.Version,
|
||||
}
|
||||
|
||||
groupIDs = append(groupIDs, group.GroupID)
|
||||
}
|
||||
|
||||
groups, err := s.db.FindGroup(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
if group.Status == constant.GroupStatusDismissed {
|
||||
err = servererrs.ErrDismissedAlready.Wrap()
|
||||
log.ZError(ctx, "This group is Dismissed Already", err, "group is", group.GroupID)
|
||||
|
||||
delete(groupsVersionMap, group.GroupID)
|
||||
} else {
|
||||
groupsMap[group.GroupID] = group
|
||||
}
|
||||
}
|
||||
|
||||
for groupID, vInfo := range groupsVersionMap {
|
||||
targetKeys = append(targetKeys, groupID)
|
||||
versionIDs = append(versionIDs, vInfo.VersionID)
|
||||
versionNumbers = append(versionNumbers, vInfo.VersionNumber)
|
||||
}
|
||||
|
||||
opt := incrversion.BatchOption[[]*sdkws.GroupMemberFullInfo, pbgroup.BatchGetIncrementalGroupMemberResp]{
|
||||
Ctx: ctx,
|
||||
TargetKeys: targetKeys,
|
||||
VersionIDs: versionIDs,
|
||||
VersionNumbers: versionNumbers,
|
||||
Versions: func(ctx context.Context, groupIDs []string, versions []uint64, limits []int) (map[string]*model.VersionLog, error) {
|
||||
vLogs, err := s.db.BatchFindMemberIncrVersion(ctx, groupIDs, versions, limits)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
for groupID, vlog := range vLogs {
|
||||
vlogElems := make([]model.VersionLogElem, 0, len(vlog.Logs))
|
||||
for i, log := range vlog.Logs {
|
||||
switch log.EID {
|
||||
case model.VersionGroupChangeID:
|
||||
vlog.LogLen--
|
||||
hasGroupUpdateMap[groupID] = true
|
||||
case model.VersionSortChangeID:
|
||||
vlog.LogLen--
|
||||
sortVersionMap[groupID] = uint64(log.Version)
|
||||
default:
|
||||
vlogElems = append(vlogElems, vlog.Logs[i])
|
||||
}
|
||||
}
|
||||
vlog.Logs = vlogElems
|
||||
if vlog.LogLen > 0 {
|
||||
hasGroupUpdateMap[groupID] = true
|
||||
}
|
||||
}
|
||||
|
||||
return vLogs, nil
|
||||
},
|
||||
CacheMaxVersions: s.db.BatchFindMaxGroupMemberVersionCache,
|
||||
Find: func(ctx context.Context, groupID string, ids []string) ([]*sdkws.GroupMemberFullInfo, error) {
|
||||
memberInfo, err := s.getGroupMembersInfo(ctx, groupID, ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return memberInfo, err
|
||||
},
|
||||
Resp: func(versions map[string]*model.VersionLog, deleteIdsMap map[string][]string, insertListMap, updateListMap map[string][]*sdkws.GroupMemberFullInfo, fullMap map[string]bool) *pbgroup.BatchGetIncrementalGroupMemberResp {
|
||||
resList := make(map[string]*pbgroup.GetIncrementalGroupMemberResp)
|
||||
|
||||
for groupID, versionLog := range versions {
|
||||
resList[groupID] = &pbgroup.GetIncrementalGroupMemberResp{
|
||||
VersionID: versionLog.ID.Hex(),
|
||||
Version: uint64(versionLog.Version),
|
||||
Full: fullMap[groupID],
|
||||
Delete: deleteIdsMap[groupID],
|
||||
Insert: insertListMap[groupID],
|
||||
Update: updateListMap[groupID],
|
||||
SortVersion: sortVersionMap[groupID],
|
||||
}
|
||||
|
||||
requestBodyLen += len(insertListMap[groupID]) + len(updateListMap[groupID]) + len(deleteIdsMap[groupID])
|
||||
if requestBodyLen > 200 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &pbgroup.BatchGetIncrementalGroupMemberResp{
|
||||
RespList: resList,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = opt.Build()
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
for groupID, val := range resp.RespList {
|
||||
if val.Full || hasGroupUpdateMap[groupID] {
|
||||
count, err := s.db.FindGroupMemberNum(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
owner, err := s.db.TakeGroupOwner(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.RespList[groupID].Group = s.groupDB2PB(groupsMap[groupID], owner.UserID, count)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.GetIncrementalJoinGroupReq) (*pbgroup.GetIncrementalJoinGroupResp, error) {
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
|
||||
return nil, err
|
||||
@ -133,7 +288,6 @@ func (s *groupServer) GetIncrementalJoinGroup(ctx context.Context, req *pbgroup.
|
||||
Version: s.db.FindJoinIncrVersion,
|
||||
CacheMaxVersion: s.db.FindMaxJoinGroupVersionCache,
|
||||
Find: s.getGroupsInfo,
|
||||
ID: func(elem *sdkws.GroupInfo) string { return elem.GroupID },
|
||||
Resp: func(version *model.VersionLog, delIDs []string, insertList, updateList []*sdkws.GroupInfo, full bool) *pbgroup.GetIncrementalJoinGroupResp {
|
||||
return &pbgroup.GetIncrementalJoinGroupResp{
|
||||
VersionID: version.ID.Hex(),
|
||||
|
||||
207
internal/rpc/incrversion/batch_option.go
Normal file
207
internal/rpc/incrversion/batch_option.go
Normal file
@ -0,0 +1,207 @@
|
||||
package incrversion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type BatchOption[A, B any] struct {
|
||||
Ctx context.Context
|
||||
TargetKeys []string
|
||||
VersionIDs []string
|
||||
VersionNumbers []uint64
|
||||
//SyncLimit int
|
||||
Versions func(ctx context.Context, dIds []string, versions []uint64, limits []int) (map[string]*model.VersionLog, error)
|
||||
CacheMaxVersions func(ctx context.Context, dIds []string) (map[string]*model.VersionLog, error)
|
||||
Find func(ctx context.Context, dId string, ids []string) (A, error)
|
||||
Resp func(versionsMap map[string]*model.VersionLog, deleteIdsMap map[string][]string, insertListMap, updateListMap map[string]A, fullMap map[string]bool) *B
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) newError(msg string) error {
|
||||
return errs.ErrInternalServer.WrapMsg(msg)
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) check() error {
|
||||
if o.Ctx == nil {
|
||||
return o.newError("opt ctx is nil")
|
||||
}
|
||||
if len(o.TargetKeys) == 0 {
|
||||
return o.newError("targetKeys is empty")
|
||||
}
|
||||
if o.Versions == nil {
|
||||
return o.newError("func versions is nil")
|
||||
}
|
||||
if o.Find == nil {
|
||||
return o.newError("func find is nil")
|
||||
}
|
||||
if o.Resp == nil {
|
||||
return o.newError("func resp is nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) validVersions() []bool {
|
||||
valids := make([]bool, len(o.VersionIDs))
|
||||
for i, versionID := range o.VersionIDs {
|
||||
objID, err := primitive.ObjectIDFromHex(versionID)
|
||||
valids[i] = (err == nil && (!objID.IsZero()) && o.VersionNumbers[i] > 0)
|
||||
}
|
||||
return valids
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) equalIDs(objIDs []primitive.ObjectID) []bool {
|
||||
equals := make([]bool, len(o.VersionIDs))
|
||||
for i, versionID := range o.VersionIDs {
|
||||
equals[i] = versionID == objIDs[i].Hex()
|
||||
}
|
||||
return equals
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) getVersions(tags *[]int) (versions map[string]*model.VersionLog, err error) {
|
||||
var dIDs []string
|
||||
var versionNums []uint64
|
||||
var limits []int
|
||||
|
||||
valids := o.validVersions()
|
||||
|
||||
if o.CacheMaxVersions == nil {
|
||||
for i, valid := range valids {
|
||||
if valid {
|
||||
(*tags)[i] = tagQuery
|
||||
dIDs = append(dIDs, o.TargetKeys[i])
|
||||
versionNums = append(versionNums, o.VersionNumbers[i])
|
||||
limits = append(limits, syncLimit)
|
||||
} else {
|
||||
(*tags)[i] = tagFull
|
||||
dIDs = append(dIDs, o.TargetKeys[i])
|
||||
versionNums = append(versionNums, 0)
|
||||
limits = append(limits, 0)
|
||||
}
|
||||
}
|
||||
|
||||
versions, err = o.Versions(o.Ctx, dIDs, versionNums, limits)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
return versions, nil
|
||||
|
||||
} else {
|
||||
caches, err := o.CacheMaxVersions(o.Ctx, o.TargetKeys)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
objIDs := make([]primitive.ObjectID, len(o.VersionIDs))
|
||||
|
||||
for i, versionID := range o.VersionIDs {
|
||||
objID, _ := primitive.ObjectIDFromHex(versionID)
|
||||
objIDs[i] = objID
|
||||
}
|
||||
|
||||
equals := o.equalIDs(objIDs)
|
||||
for i, valid := range valids {
|
||||
if !valid {
|
||||
(*tags)[i] = tagFull
|
||||
} else if !equals[i] {
|
||||
(*tags)[i] = tagFull
|
||||
} else if o.VersionNumbers[i] == uint64(caches[o.TargetKeys[i]].Version) {
|
||||
(*tags)[i] = tagEqual
|
||||
} else {
|
||||
(*tags)[i] = tagQuery
|
||||
dIDs = append(dIDs, o.TargetKeys[i])
|
||||
versionNums = append(versionNums, o.VersionNumbers[i])
|
||||
limits = append(limits, syncLimit)
|
||||
|
||||
delete(caches, o.TargetKeys[i])
|
||||
}
|
||||
}
|
||||
|
||||
if dIDs != nil {
|
||||
versionMap, err := o.Versions(o.Ctx, dIDs, versionNums, limits)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
for k, v := range versionMap {
|
||||
caches[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
versions = caches
|
||||
}
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
func (o *BatchOption[A, B]) Build() (*B, error) {
|
||||
if err := o.check(); err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
tags := make([]int, len(o.TargetKeys))
|
||||
versions, err := o.getVersions(&tags)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
fullMap := make(map[string]bool)
|
||||
for i, tag := range tags {
|
||||
switch tag {
|
||||
case tagQuery:
|
||||
vLog := versions[o.TargetKeys[i]]
|
||||
fullMap[o.TargetKeys[i]] = vLog.ID.Hex() != o.VersionIDs[i] || uint64(vLog.Version) < o.VersionNumbers[i] || len(vLog.Logs) != vLog.LogLen
|
||||
case tagFull:
|
||||
fullMap[o.TargetKeys[i]] = true
|
||||
case tagEqual:
|
||||
fullMap[o.TargetKeys[i]] = false
|
||||
default:
|
||||
panic(fmt.Errorf("undefined tag %d", tag))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
insertIdsMap = make(map[string][]string)
|
||||
deleteIdsMap = make(map[string][]string)
|
||||
updateIdsMap = make(map[string][]string)
|
||||
)
|
||||
|
||||
for _, targetKey := range o.TargetKeys {
|
||||
if !fullMap[targetKey] {
|
||||
version := versions[targetKey]
|
||||
insertIds, deleteIds, updateIds := version.DeleteAndChangeIDs()
|
||||
insertIdsMap[targetKey] = insertIds
|
||||
deleteIdsMap[targetKey] = deleteIds
|
||||
updateIdsMap[targetKey] = updateIds
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
insertListMap = make(map[string]A)
|
||||
updateListMap = make(map[string]A)
|
||||
)
|
||||
|
||||
for targetKey, insertIds := range insertIdsMap {
|
||||
if len(insertIds) > 0 {
|
||||
insertList, err := o.Find(o.Ctx, targetKey, insertIds)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
insertListMap[targetKey] = insertList
|
||||
}
|
||||
}
|
||||
|
||||
for targetKey, updateIds := range updateIdsMap {
|
||||
if len(updateIds) > 0 {
|
||||
updateList, err := o.Find(o.Ctx, targetKey, updateIds)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
updateListMap[targetKey] = updateList
|
||||
}
|
||||
}
|
||||
|
||||
return o.Resp(versions, deleteIdsMap, insertListMap, updateListMap, fullMap), nil
|
||||
}
|
||||
@ -3,6 +3,7 @@ package incrversion
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
@ -20,7 +21,7 @@ const syncLimit = 200
|
||||
const (
|
||||
tagQuery = iota + 1
|
||||
tagFull
|
||||
tageEqual
|
||||
tagEqual
|
||||
)
|
||||
|
||||
type Option[A, B any] struct {
|
||||
@ -33,7 +34,6 @@ type Option[A, B any] struct {
|
||||
Version func(ctx context.Context, dId string, version uint, limit int) (*model.VersionLog, error)
|
||||
//SortID func(ctx context.Context, dId string) ([]string, error)
|
||||
Find func(ctx context.Context, ids []string) ([]A, error)
|
||||
ID func(elem A) string
|
||||
Resp func(version *model.VersionLog, deleteIds []string, insertList, updateList []A, full bool) *B
|
||||
}
|
||||
|
||||
@ -60,9 +60,6 @@ func (o *Option[A, B]) check() error {
|
||||
if o.Find == nil {
|
||||
return o.newError("func find is nil")
|
||||
}
|
||||
if o.ID == nil {
|
||||
return o.newError("func id is nil")
|
||||
}
|
||||
if o.Resp == nil {
|
||||
return o.newError("func resp is nil")
|
||||
}
|
||||
@ -100,7 +97,7 @@ func (o *Option[A, B]) getVersion(tag *int) (*model.VersionLog, error) {
|
||||
return cache, nil
|
||||
}
|
||||
if o.VersionNumber == uint64(cache.Version) {
|
||||
*tag = tageEqual
|
||||
*tag = tagEqual
|
||||
return cache, nil
|
||||
}
|
||||
*tag = tagQuery
|
||||
@ -123,7 +120,7 @@ func (o *Option[A, B]) Build() (*B, error) {
|
||||
full = version.ID.Hex() != o.VersionID || uint64(version.Version) < o.VersionNumber || len(version.Logs) != version.LogLen
|
||||
case tagFull:
|
||||
full = true
|
||||
case tageEqual:
|
||||
case tagEqual:
|
||||
full = false
|
||||
default:
|
||||
panic(fmt.Errorf("undefined tag %d", tag))
|
||||
|
||||
@ -3,7 +3,6 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
pbuser "github.com/openimsdk/protocol/user"
|
||||
)
|
||||
|
||||
@ -38,23 +37,6 @@ func (s *userServer) getUsersOnlineStatus(ctx context.Context, userIDs []string)
|
||||
|
||||
// SubscribeOrCancelUsersStatus Subscribe online or cancel online users.
|
||||
func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbuser.SubscribeOrCancelUsersStatusReq) (*pbuser.SubscribeOrCancelUsersStatusResp, error) {
|
||||
if req.Genre == constant.SubscriberUser {
|
||||
err := s.db.SubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var status []*pbuser.OnlineStatus
|
||||
status, err = s.getUsersOnlineStatus(ctx, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.SubscribeOrCancelUsersStatusResp{StatusList: status}, nil
|
||||
} else if req.Genre == constant.Unsubscribe {
|
||||
err := s.db.UnsubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &pbuser.SubscribeOrCancelUsersStatusResp{}, nil
|
||||
}
|
||||
|
||||
@ -82,34 +64,12 @@ func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatu
|
||||
if err := s.online.SetUserOnline(ctx, req.UserID, online, offline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list, err := s.db.GetSubscribedList(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, userID := range list {
|
||||
tips := &sdkws.UserStatusChangeTips{
|
||||
FromUserID: req.UserID,
|
||||
ToUserID: userID,
|
||||
Status: req.Status,
|
||||
PlatformID: req.PlatformID,
|
||||
}
|
||||
s.userNotificationSender.UserStatusChangeNotification(ctx, tips)
|
||||
}
|
||||
|
||||
return &pbuser.SetUserStatusResp{}, nil
|
||||
}
|
||||
|
||||
// GetSubscribeUsersStatus Get the online status of subscribers.
|
||||
func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, req *pbuser.GetSubscribeUsersStatusReq) (*pbuser.GetSubscribeUsersStatusResp, error) {
|
||||
userList, err := s.db.GetAllSubscribeList(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
onlineStatusList, err := s.getUsersOnlineStatus(ctx, userList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.GetSubscribeUsersStatusResp{StatusList: onlineStatusList}, nil
|
||||
return &pbuser.GetSubscribeUsersStatusResp{}, nil
|
||||
}
|
||||
|
||||
func (s *userServer) SetUserOnlineStatus(ctx context.Context, req *pbuser.SetUserOnlineStatusReq) (*pbuser.SetUserOnlineStatusResp, error) {
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo"
|
||||
@ -93,8 +94,7 @@ func Start(ctx context.Context, config *Config, client registry.SvcDiscoveryRegi
|
||||
return err
|
||||
}
|
||||
userCache := redis.NewUserCacheRedis(rdb, &config.LocalCacheConfig, userDB, redis.GetRocksCacheOptions())
|
||||
userMongoDB := mgo.NewUserMongoDriver(mgocli.GetDB())
|
||||
database := controller.NewUserDatabase(userDB, userCache, mgocli.GetTx(), userMongoDB)
|
||||
database := controller.NewUserDatabase(userDB, userCache, mgocli.GetTx())
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client, config.Share.RpcRegisterName.Friend)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
|
||||
@ -311,6 +311,8 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prommetrics.UserRegisterCounter.Add(float64(len(users)))
|
||||
|
||||
s.webhookAfterUserRegister(ctx, &s.config.WebhooksConfig.AfterUserRegister, req)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
10
pkg/common/prommetrics/grpc_user.go
Normal file
10
pkg/common/prommetrics/grpc_user.go
Normal file
@ -0,0 +1,10 @@
|
||||
package prommetrics
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
var (
|
||||
UserRegisterCounter = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "user_register_total",
|
||||
Help: "The number of user login",
|
||||
})
|
||||
)
|
||||
@ -52,6 +52,8 @@ func GetGrpcCusMetrics(registerName string, share *config.Share) []prometheus.Co
|
||||
return []prometheus.Collector{MsgOfflinePushFailedCounter}
|
||||
case share.RpcRegisterName.Auth:
|
||||
return []prometheus.Collector{UserLoginCounter}
|
||||
case share.RpcRegisterName.User:
|
||||
return []prometheus.Collector{UserRegisterCounter}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
1
pkg/common/storage/cache/group.go
vendored
1
pkg/common/storage/cache/group.go
vendored
@ -64,5 +64,6 @@ type GroupCache interface {
|
||||
DelMaxGroupMemberVersion(groupIDs ...string) GroupCache
|
||||
DelMaxJoinGroupVersion(userIDs ...string) GroupCache
|
||||
FindMaxGroupMemberVersion(ctx context.Context, groupID string) (*model.VersionLog, error)
|
||||
BatchFindMaxGroupMemberVersion(ctx context.Context, groupIDs []string) ([]*model.VersionLog, error)
|
||||
FindMaxJoinGroupVersion(ctx context.Context, userID string) (*model.VersionLog, error)
|
||||
}
|
||||
|
||||
18
pkg/common/storage/cache/redis/group.go
vendored
18
pkg/common/storage/cache/redis/group.go
vendored
@ -17,6 +17,8 @@ package redis
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
@ -28,7 +30,6 @@ import (
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -390,6 +391,21 @@ func (g *GroupCacheRedis) FindMaxGroupMemberVersion(ctx context.Context, groupID
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) BatchFindMaxGroupMemberVersion(ctx context.Context, groupIDs []string) ([]*model.VersionLog, error) {
|
||||
return batchGetCache2(ctx, g.rcClient, g.expireTime, groupIDs,
|
||||
func(groupID string) string {
|
||||
return g.getGroupMemberMaxVersionKey(groupID)
|
||||
}, func(versionLog *model.VersionLog) string {
|
||||
return versionLog.DID
|
||||
}, func(ctx context.Context, groupIDs []string) ([]*model.VersionLog, error) {
|
||||
// create two slices with len is groupIDs, just need 0
|
||||
versions := make([]uint, len(groupIDs))
|
||||
limits := make([]int, len(groupIDs))
|
||||
|
||||
return g.groupMemberDB.BatchFindMemberIncrVersion(ctx, groupIDs, versions, limits)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) FindMaxJoinGroupVersion(ctx context.Context, userID string) (*model.VersionLog, error) {
|
||||
return getCache(ctx, g.rcClient, g.getJoinGroupMaxVersionKey(userID), g.expireTime, func(ctx context.Context) (*model.VersionLog, error) {
|
||||
return g.groupMemberDB.FindJoinIncrVersion(ctx, userID, 0, 0)
|
||||
|
||||
@ -63,7 +63,7 @@ func (s *seqConversationCacheRedis) batchGetMaxSeq(ctx context.Context, keys []s
|
||||
for i, key := range keys {
|
||||
result[i] = pipe.HGet(ctx, key, "CURR")
|
||||
}
|
||||
if _, err := pipe.Exec(ctx); err != nil {
|
||||
if _, err := pipe.Exec(ctx); err != nil && !errors.Is(err, redis.Nil) {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
var notFoundKey []string
|
||||
|
||||
@ -16,17 +16,19 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
redis2 "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/common"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||
"github.com/openimsdk/protocol/constant"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
"github.com/openimsdk/tools/db/tx"
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"github.com/openimsdk/tools/utils/datautil"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
@ -108,6 +110,7 @@ type GroupDatabase interface {
|
||||
DeleteGroupMemberHash(ctx context.Context, groupIDs []string) error
|
||||
|
||||
FindMemberIncrVersion(ctx context.Context, groupID string, version uint, limit int) (*model.VersionLog, error)
|
||||
BatchFindMemberIncrVersion(ctx context.Context, groupIDs []string, versions []uint64, limits []int) (map[string]*model.VersionLog, error)
|
||||
FindJoinIncrVersion(ctx context.Context, userID string, version uint, limit int) (*model.VersionLog, error)
|
||||
MemberGroupIncrVersion(ctx context.Context, groupID string, userIDs []string, state int32) error
|
||||
|
||||
@ -115,6 +118,7 @@ type GroupDatabase interface {
|
||||
//FindSortJoinGroupIDs(ctx context.Context, userID string) ([]string, error)
|
||||
|
||||
FindMaxGroupMemberVersionCache(ctx context.Context, groupID string) (*model.VersionLog, error)
|
||||
BatchFindMaxGroupMemberVersionCache(ctx context.Context, groupIDs []string) (map[string]*model.VersionLog, error)
|
||||
FindMaxJoinGroupVersionCache(ctx context.Context, userID string) (*model.VersionLog, error)
|
||||
|
||||
SearchJoinGroup(ctx context.Context, userID string, keyword string, pagination pagination.Pagination) (int64, []*model.Group, error)
|
||||
@ -498,6 +502,29 @@ func (g *groupDatabase) FindMemberIncrVersion(ctx context.Context, groupID strin
|
||||
return g.groupMemberDB.FindMemberIncrVersion(ctx, groupID, version, limit)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) BatchFindMemberIncrVersion(ctx context.Context, groupIDs []string, versions []uint64, limits []int) (map[string]*model.VersionLog, error) {
|
||||
if len(groupIDs) == 0 {
|
||||
return nil, errs.Wrap(errs.New("groupIDs is nil."))
|
||||
}
|
||||
|
||||
// convert []uint64 to []uint
|
||||
var uintVersions []uint
|
||||
for _, version := range versions {
|
||||
uintVersions = append(uintVersions, uint(version))
|
||||
}
|
||||
|
||||
versionLogs, err := g.groupMemberDB.BatchFindMemberIncrVersion(ctx, groupIDs, uintVersions, limits)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
|
||||
groupMemberIncrVersionsMap := datautil.SliceToMap(versionLogs, func(e *model.VersionLog) string {
|
||||
return e.DID
|
||||
})
|
||||
|
||||
return groupMemberIncrVersionsMap, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindJoinIncrVersion(ctx context.Context, userID string, version uint, limit int) (*model.VersionLog, error) {
|
||||
return g.groupMemberDB.FindJoinIncrVersion(ctx, userID, version, limit)
|
||||
}
|
||||
@ -506,6 +533,20 @@ func (g *groupDatabase) FindMaxGroupMemberVersionCache(ctx context.Context, grou
|
||||
return g.cache.FindMaxGroupMemberVersion(ctx, groupID)
|
||||
}
|
||||
|
||||
func (g *groupDatabase) BatchFindMaxGroupMemberVersionCache(ctx context.Context, groupIDs []string) (map[string]*model.VersionLog, error) {
|
||||
if len(groupIDs) == 0 {
|
||||
return nil, errs.Wrap(errs.New("groupIDs is nil in Cache."))
|
||||
}
|
||||
versionLogs, err := g.cache.BatchFindMaxGroupMemberVersion(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
maxGroupMemberVersionsMap := datautil.SliceToMap(versionLogs, func(e *model.VersionLog) string {
|
||||
return e.DID
|
||||
})
|
||||
return maxGroupMemberVersionsMap, nil
|
||||
}
|
||||
|
||||
func (g *groupDatabase) FindMaxJoinGroupVersionCache(ctx context.Context, userID string) (*model.VersionLog, error) {
|
||||
return g.cache.FindMaxJoinGroupVersion(ctx, userID)
|
||||
}
|
||||
|
||||
@ -62,14 +62,6 @@ type UserDatabase interface {
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
|
||||
SortQuery(ctx context.Context, userIDName map[string]string, asc bool) ([]*model.User, error)
|
||||
// SubscribeUsersStatus Subscribe a user's presence status
|
||||
SubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
// UnsubscribeUsersStatus unsubscribe a user's presence status
|
||||
UnsubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
// GetAllSubscribeList Get a list of all subscriptions
|
||||
GetAllSubscribeList(ctx context.Context, userID string) ([]string, error)
|
||||
// GetSubscribedList Get all subscribed lists
|
||||
GetSubscribedList(ctx context.Context, userID string) ([]string, error)
|
||||
|
||||
// CRUD user command
|
||||
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
|
||||
@ -80,14 +72,13 @@ type UserDatabase interface {
|
||||
}
|
||||
|
||||
type userDatabase struct {
|
||||
tx tx.Tx
|
||||
userDB database.User
|
||||
cache cache.UserCache
|
||||
mongoDB database.SubscribeUser
|
||||
tx tx.Tx
|
||||
userDB database.User
|
||||
cache cache.UserCache
|
||||
}
|
||||
|
||||
func NewUserDatabase(userDB database.User, cache cache.UserCache, tx tx.Tx, mongoDB database.SubscribeUser) UserDatabase {
|
||||
return &userDatabase{userDB: userDB, cache: cache, tx: tx, mongoDB: mongoDB}
|
||||
func NewUserDatabase(userDB database.User, cache cache.UserCache, tx tx.Tx) UserDatabase {
|
||||
return &userDatabase{userDB: userDB, cache: cache, tx: tx}
|
||||
}
|
||||
|
||||
func (u *userDatabase) InitOnce(ctx context.Context, users []*model.User) error {
|
||||
@ -212,36 +203,6 @@ func (u *userDatabase) SortQuery(ctx context.Context, userIDName map[string]stri
|
||||
return u.userDB.SortQuery(ctx, userIDName, asc)
|
||||
}
|
||||
|
||||
// SubscribeUsersStatus Subscribe or unsubscribe a user's presence status.
|
||||
func (u *userDatabase) SubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error {
|
||||
err := u.mongoDB.AddSubscriptionList(ctx, userID, userIDs)
|
||||
return err
|
||||
}
|
||||
|
||||
// UnsubscribeUsersStatus unsubscribe a user's presence status.
|
||||
func (u *userDatabase) UnsubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error {
|
||||
err := u.mongoDB.UnsubscriptionList(ctx, userID, userIDs)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAllSubscribeList Get a list of all subscriptions.
|
||||
func (u *userDatabase) GetAllSubscribeList(ctx context.Context, userID string) ([]string, error) {
|
||||
list, err := u.mongoDB.GetAllSubscribeList(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// GetSubscribedList Get all subscribed lists.
|
||||
func (u *userDatabase) GetSubscribedList(ctx context.Context, userID string) ([]string, error) {
|
||||
list, err := u.mongoDB.GetSubscribedList(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
|
||||
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value, ex)
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/db/pagination"
|
||||
)
|
||||
@ -40,5 +41,6 @@ type GroupMember interface {
|
||||
JoinGroupIncrVersion(ctx context.Context, userID string, groupIDs []string, state int32) error
|
||||
MemberGroupIncrVersion(ctx context.Context, groupID string, userIDs []string, state int32) error
|
||||
FindMemberIncrVersion(ctx context.Context, groupID string, version uint, limit int) (*model.VersionLog, error)
|
||||
BatchFindMemberIncrVersion(ctx context.Context, groupIDs []string, versions []uint, limits []int) ([]*model.VersionLog, error)
|
||||
FindJoinIncrVersion(ctx context.Context, userID string, version uint, limit int) (*model.VersionLog, error)
|
||||
}
|
||||
|
||||
@ -109,7 +109,13 @@ func (f *FriendMgo) UpdateByMap(ctx context.Context, ownerUserID string, friendU
|
||||
return mongoutil.IncrVersion(func() error {
|
||||
return mongoutil.UpdateOne(ctx, f.coll, filter, bson.M{"$set": args}, true)
|
||||
}, func() error {
|
||||
return f.owner.IncrVersion(ctx, ownerUserID, []string{friendUserID}, model.VersionStateUpdate)
|
||||
var friendUserIDs []string
|
||||
if f.IsUpdateIsPinned(args) {
|
||||
friendUserIDs = []string{model.VersionSortChangeID, friendUserID}
|
||||
} else {
|
||||
friendUserIDs = []string{friendUserID}
|
||||
}
|
||||
return f.owner.IncrVersion(ctx, ownerUserID, friendUserIDs, model.VersionStateUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
@ -214,7 +220,7 @@ func (f *FriendMgo) FindFriendUserIDs(ctx context.Context, ownerUserID string) (
|
||||
|
||||
func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) error {
|
||||
// Ensure there are IDs to update
|
||||
if len(friendUserIDs) == 0 {
|
||||
if len(friendUserIDs) == 0 || len(val) == 0 {
|
||||
return nil // Or return an error if you expect there to always be IDs
|
||||
}
|
||||
|
||||
@ -230,7 +236,13 @@ func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, frien
|
||||
return mongoutil.IncrVersion(func() error {
|
||||
return mongoutil.Ignore(mongoutil.UpdateMany(ctx, f.coll, filter, update))
|
||||
}, func() error {
|
||||
return f.owner.IncrVersion(ctx, ownerUserID, friendUserIDs, model.VersionStateUpdate)
|
||||
var userIDs []string
|
||||
if f.IsUpdateIsPinned(val) {
|
||||
userIDs = append([]string{model.VersionSortChangeID}, friendUserIDs...)
|
||||
} else {
|
||||
userIDs = friendUserIDs
|
||||
}
|
||||
return f.owner.IncrVersion(ctx, ownerUserID, userIDs, model.VersionStateUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
@ -248,3 +260,11 @@ func (f *FriendMgo) FindFriendUserID(ctx context.Context, friendUserID string) (
|
||||
func (f *FriendMgo) IncrVersion(ctx context.Context, ownerUserID string, friendUserIDs []string, state int32) error {
|
||||
return f.owner.IncrVersion(ctx, ownerUserID, friendUserIDs, state)
|
||||
}
|
||||
|
||||
func (f *FriendMgo) IsUpdateIsPinned(data map[string]any) bool {
|
||||
if data == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := data["is_pinned"]
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ package mgo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/tools/log"
|
||||
@ -59,7 +60,7 @@ type GroupMemberMgo struct {
|
||||
}
|
||||
|
||||
func (g *GroupMemberMgo) memberSort() any {
|
||||
return bson.D{{"role_level", -1}, {"create_time", -1}}
|
||||
return bson.D{{"role_level", -1}, {"create_time", 1}}
|
||||
}
|
||||
|
||||
func (g *GroupMemberMgo) Create(ctx context.Context, groupMembers []*model.GroupMember) (err error) {
|
||||
@ -118,7 +119,7 @@ func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, us
|
||||
return mongoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID},
|
||||
bson.M{"$set": bson.M{"role_level": roleLevel}}, true)
|
||||
}, func() error {
|
||||
return g.member.IncrVersion(ctx, groupID, []string{userID}, model.VersionStateUpdate)
|
||||
return g.member.IncrVersion(ctx, groupID, []string{model.VersionSortChangeID, userID}, model.VersionStateUpdate)
|
||||
})
|
||||
}
|
||||
func (g *GroupMemberMgo) UpdateUserRoleLevels(ctx context.Context, groupID string, firstUserID string, firstUserRoleLevel int32, secondUserID string, secondUserRoleLevel int32) error {
|
||||
@ -131,10 +132,9 @@ func (g *GroupMemberMgo) UpdateUserRoleLevels(ctx context.Context, groupID strin
|
||||
bson.M{"$set": bson.M{"role_level": secondUserRoleLevel}}, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, func() error {
|
||||
return g.member.IncrVersion(ctx, groupID, []string{firstUserID, secondUserID}, model.VersionStateUpdate)
|
||||
return g.member.IncrVersion(ctx, groupID, []string{model.VersionSortChangeID, firstUserID, secondUserID}, model.VersionStateUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
@ -145,7 +145,13 @@ func (g *GroupMemberMgo) Update(ctx context.Context, groupID string, userID stri
|
||||
return mongoutil.IncrVersion(func() error {
|
||||
return mongoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID}, bson.M{"$set": data}, true)
|
||||
}, func() error {
|
||||
return g.member.IncrVersion(ctx, groupID, []string{userID}, model.VersionStateUpdate)
|
||||
var userIDs []string
|
||||
if g.IsUpdateRoleLevel(data) {
|
||||
userIDs = []string{model.VersionSortChangeID, userID}
|
||||
} else {
|
||||
userIDs = []string{userID}
|
||||
}
|
||||
return g.member.IncrVersion(ctx, groupID, userIDs, model.VersionStateUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
@ -225,6 +231,11 @@ func (g *GroupMemberMgo) FindMemberIncrVersion(ctx context.Context, groupID stri
|
||||
return g.member.FindChangeLog(ctx, groupID, version, limit)
|
||||
}
|
||||
|
||||
func (g *GroupMemberMgo) BatchFindMemberIncrVersion(ctx context.Context, groupIDs []string, versions []uint, limits []int) ([]*model.VersionLog, error) {
|
||||
log.ZDebug(ctx, "Batch find member incr version", "groupIDs", groupIDs, "versions", versions)
|
||||
return g.member.BatchFindChangeLog(ctx, groupIDs, versions, limits)
|
||||
}
|
||||
|
||||
func (g *GroupMemberMgo) FindJoinIncrVersion(ctx context.Context, userID string, version uint, limit int) (*model.VersionLog, error) {
|
||||
log.ZDebug(ctx, "find join incr version", "userID", userID, "version", version)
|
||||
return g.join.FindChangeLog(ctx, userID, version, limit)
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mgo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
|
||||
"github.com/openimsdk/tools/errs"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// prefixes and suffixes.
|
||||
const (
|
||||
SubscriptionPrefix = "subscription_prefix"
|
||||
SubscribedPrefix = "subscribed_prefix"
|
||||
)
|
||||
|
||||
// MaximumSubscription Maximum number of subscriptions.
|
||||
const (
|
||||
MaximumSubscription = 3000
|
||||
)
|
||||
|
||||
func NewUserMongoDriver(database *mongo.Database) database.SubscribeUser {
|
||||
return &UserMongoDriver{
|
||||
userCollection: database.Collection(model.SubscribeUserTableName),
|
||||
}
|
||||
}
|
||||
|
||||
type UserMongoDriver struct {
|
||||
userCollection *mongo.Collection
|
||||
}
|
||||
|
||||
// AddSubscriptionList Subscriber's handling of thresholds.
|
||||
func (u *UserMongoDriver) AddSubscriptionList(ctx context.Context, userID string, userIDList []string) error {
|
||||
// Check the number of lists in the key.
|
||||
pipeline := mongo.Pipeline{
|
||||
{{"$match", bson.D{{"user_id", SubscriptionPrefix + userID}}}},
|
||||
{{"$project", bson.D{{"count", bson.D{{"$size", "$user_id_list"}}}}}},
|
||||
}
|
||||
// perform aggregate operations
|
||||
cursor, err := u.userCollection.Aggregate(ctx, pipeline)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
var cnt struct {
|
||||
Count int `bson:"count"`
|
||||
}
|
||||
// iterate over aggregated results
|
||||
for cursor.Next(ctx) {
|
||||
err = cursor.Decode(&cnt)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
var newUserIDList []string
|
||||
// If the threshold is exceeded, pop out the previous MaximumSubscription - len(userIDList) and insert it.
|
||||
if cnt.Count+len(userIDList) > MaximumSubscription {
|
||||
newUserIDList, err = u.GetAllSubscribeList(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newUserIDList = newUserIDList[MaximumSubscription-len(userIDList):]
|
||||
_, err = u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscriptionPrefix + userID},
|
||||
bson.M{"$set": bson.M{"user_id_list": newUserIDList}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Another way to subscribe to N before pop,Delete after testing
|
||||
/*for i := 1; i <= MaximumSubscription-len(userIDList); i++ {
|
||||
_, err := u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscriptionPrefix + userID},
|
||||
bson.M{SubscriptionPrefix + userID: bson.M{"$pop": -1}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}*/
|
||||
}
|
||||
upsert := true
|
||||
opts := &options.UpdateOptions{
|
||||
Upsert: &upsert,
|
||||
}
|
||||
_, err = u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscriptionPrefix + userID},
|
||||
bson.M{"$addToSet": bson.M{"user_id_list": bson.M{"$each": userIDList}}},
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
for _, user := range userIDList {
|
||||
_, err = u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscribedPrefix + user},
|
||||
bson.M{"$addToSet": bson.M{"user_id_list": userID}},
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return errs.WrapMsg(err, "transaction failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscriptionList Handling of unsubscribe.
|
||||
func (u *UserMongoDriver) UnsubscriptionList(ctx context.Context, userID string, userIDList []string) error {
|
||||
_, err := u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscriptionPrefix + userID},
|
||||
bson.M{"$pull": bson.M{"user_id_list": bson.M{"$in": userIDList}}},
|
||||
)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
err = u.RemoveSubscribedListFromUser(ctx, userID, userIDList)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSubscribedListFromUser Among the unsubscribed users, delete the user from the subscribed list.
|
||||
func (u *UserMongoDriver) RemoveSubscribedListFromUser(ctx context.Context, userID string, userIDList []string) error {
|
||||
var err error
|
||||
for _, userIDTemp := range userIDList {
|
||||
_, err = u.userCollection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscribedPrefix + userIDTemp},
|
||||
bson.M{"$pull": bson.M{"user_id_list": userID}},
|
||||
)
|
||||
}
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// GetAllSubscribeList Get all users subscribed by this user.
|
||||
func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||
var user model.SubscribeUser
|
||||
cursor := u.userCollection.FindOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscriptionPrefix + userID})
|
||||
err = cursor.Decode(&user)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return []string{}, nil
|
||||
} else {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
return user.UserIDList, nil
|
||||
}
|
||||
|
||||
// GetSubscribedList Get the user subscribed by those users.
|
||||
func (u *UserMongoDriver) GetSubscribedList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||
var user model.SubscribeUser
|
||||
cursor := u.userCollection.FindOne(
|
||||
ctx,
|
||||
bson.M{"user_id": SubscribedPrefix + userID})
|
||||
err = cursor.Decode(&user)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return []string{}, nil
|
||||
} else {
|
||||
return nil, errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
return user.UserIDList, nil
|
||||
}
|
||||
@ -3,6 +3,8 @@ package mgo
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/versionctx"
|
||||
@ -13,13 +15,12 @@ import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewVersionLog(coll *mongo.Collection) (database.VersionLog, error) {
|
||||
lm := &VersionLogMgo{coll: coll}
|
||||
if lm.initIndex(context.Background()) != nil {
|
||||
return nil, errs.ErrInternalServer.WrapMsg("init index failed", "coll", coll.Name())
|
||||
if err := lm.initIndex(context.Background()); err != nil {
|
||||
return nil, errs.WrapMsg(err, "init version log index failed", "coll", coll.Name())
|
||||
}
|
||||
return lm, nil
|
||||
}
|
||||
@ -33,7 +34,9 @@ func (l *VersionLogMgo) initIndex(ctx context.Context) error {
|
||||
Keys: bson.M{
|
||||
"d_id": 1,
|
||||
},
|
||||
Options: options.Index().SetUnique(true),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -152,8 +155,24 @@ func (l *VersionLogMgo) writeLogBatch2(ctx context.Context, dId string, eIds []s
|
||||
"$unset": "delete_e_ids",
|
||||
},
|
||||
}
|
||||
opt := options.FindOneAndUpdate().SetUpsert(false).SetReturnDocument(options.After).SetProjection(bson.M{"logs": 0})
|
||||
return mongoutil.FindOneAndUpdate[*model.VersionLog](ctx, l.coll, filter, pipeline, opt)
|
||||
projection := bson.M{
|
||||
"logs": 0,
|
||||
}
|
||||
opt := options.FindOneAndUpdate().SetUpsert(false).SetReturnDocument(options.After).SetProjection(projection)
|
||||
res, err := mongoutil.FindOneAndUpdate[*model.VersionLog](ctx, l.coll, filter, pipeline, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Logs = make([]model.VersionLogElem, 0, len(eIds))
|
||||
for _, id := range eIds {
|
||||
res.Logs = append(res.Logs, model.VersionLogElem{
|
||||
EID: id,
|
||||
State: state,
|
||||
Version: res.Version,
|
||||
LastUpdate: res.LastUpdate,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *VersionLogMgo) findDoc(ctx context.Context, dId string) (*model.VersionLog, error) {
|
||||
@ -181,6 +200,26 @@ func (l *VersionLogMgo) FindChangeLog(ctx context.Context, dId string, version u
|
||||
}
|
||||
}
|
||||
|
||||
func (l *VersionLogMgo) BatchFindChangeLog(ctx context.Context, dIds []string, versions []uint, limits []int) (vLogs []*model.VersionLog, err error) {
|
||||
for i := 0; i < len(dIds); i++ {
|
||||
if vLog, err := l.findChangeLog(ctx, dIds[i], versions[i], limits[i]); err == nil {
|
||||
vLogs = append(vLogs, vLog)
|
||||
} else if !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
log.ZError(ctx, "findChangeLog error:", errs.Wrap(err))
|
||||
}
|
||||
log.ZDebug(ctx, "init doc", "dId", dIds[i])
|
||||
if res, err := l.initDoc(ctx, dIds[i], nil, 0, time.Now()); err == nil {
|
||||
log.ZDebug(ctx, "init doc success", "dId", dIds[i])
|
||||
vLogs = append(vLogs, res)
|
||||
} else if mongo.IsDuplicateKeyError(err) {
|
||||
l.findChangeLog(ctx, dIds[i], versions[i], limits[i])
|
||||
} else {
|
||||
log.ZError(ctx, "init doc error:", errs.Wrap(err))
|
||||
}
|
||||
}
|
||||
return vLogs, errs.Wrap(err)
|
||||
}
|
||||
|
||||
func (l *VersionLogMgo) findChangeLog(ctx context.Context, dId string, version uint, limit int) (*model.VersionLog, error) {
|
||||
if version == 0 && limit == 0 {
|
||||
return l.findDoc(ctx, dId)
|
||||
|
||||
@ -9,12 +9,12 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Result[V any](val V, err error) V {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
//func Result[V any](val V, err error) V {
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// return val
|
||||
//}
|
||||
|
||||
func Check(err error) {
|
||||
if err != nil {
|
||||
@ -30,7 +30,7 @@ func TestName(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
vl := tmp.(*VersionLogMgo)
|
||||
res, err := vl.writeLogBatch2(context.Background(), "100", []string{"1000", "1001", "1003"}, model.VersionStateInsert, time.Now())
|
||||
res, err := vl.incrVersionResult(context.Background(), "100", []string{"1000", "1001", "1003"}, model.VersionStateInsert)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package database
|
||||
|
||||
import "context"
|
||||
|
||||
// SubscribeUser Operation interface of user mongodb.
|
||||
type SubscribeUser interface {
|
||||
// AddSubscriptionList Subscriber's handling of thresholds.
|
||||
AddSubscriptionList(ctx context.Context, userID string, userIDList []string) error
|
||||
// UnsubscriptionList Handling of unsubscribe.
|
||||
UnsubscriptionList(ctx context.Context, userID string, userIDList []string) error
|
||||
// RemoveSubscribedListFromUser Among the unsubscribed users, delete the user from the subscribed list.
|
||||
RemoveSubscribedListFromUser(ctx context.Context, userID string, userIDList []string) error
|
||||
// GetAllSubscribeList Get all users subscribed by this user
|
||||
GetAllSubscribeList(ctx context.Context, id string) (userIDList []string, err error)
|
||||
// GetSubscribedList Get the user subscribed by those users
|
||||
GetSubscribedList(ctx context.Context, id string) (userIDList []string, err error)
|
||||
}
|
||||
@ -2,8 +2,9 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -14,6 +15,7 @@ const (
|
||||
type VersionLog interface {
|
||||
IncrVersion(ctx context.Context, dId string, eIds []string, state int32) error
|
||||
FindChangeLog(ctx context.Context, dId string, version uint, limit int) (*model.VersionLog, error)
|
||||
BatchFindChangeLog(ctx context.Context, dIds []string, versions []uint, limits []int) ([]*model.VersionLog, error)
|
||||
DeleteAfterUnchangedLog(ctx context.Context, deadline time.Time) error
|
||||
Delete(ctx context.Context, dId string) error
|
||||
}
|
||||
|
||||
@ -14,6 +14,11 @@ const (
|
||||
VersionStateUpdate
|
||||
)
|
||||
|
||||
const (
|
||||
VersionGroupChangeID = ""
|
||||
VersionSortChangeID = "____S_O_R_T_I_D____"
|
||||
)
|
||||
|
||||
type VersionLogElem struct {
|
||||
EID string `bson:"e_id"`
|
||||
State int32 `bson:"state"`
|
||||
|
||||
@ -116,7 +116,7 @@ func Main(conf string, del time.Duration) error {
|
||||
{
|
||||
Prefix: MaxSeq,
|
||||
GetSeq: cSeq.GetMaxSeq,
|
||||
SetSeq: cSeq.SetMinSeq,
|
||||
SetSeq: cSeq.SetMaxSeq,
|
||||
},
|
||||
{
|
||||
Prefix: MinSeq,
|
||||
|
||||
@ -12,7 +12,7 @@ func main() {
|
||||
config string
|
||||
second int
|
||||
)
|
||||
flag.StringVar(&config, "c", "/Users/chao/Desktop/project/open-im-server/config", "config directory")
|
||||
flag.StringVar(&config, "c", "", "config directory")
|
||||
flag.IntVar(&second, "sec", 3600*24, "delayed deletion of the original seq key after conversion")
|
||||
flag.Parse()
|
||||
if err := internal.Main(config, time.Duration(second)*time.Second); err != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user