Merging performance branch into master (#4268)
* improve performance on sendNotifications * Fix SQL queries * Remove get direct profiles, not needed anymore * Add raw data to error details if AppError fails to decode * men * Fix decode (#4052) * Fixing json decode * Adding unit test * Initial work for client scaling (#4051) * Begin adding paging to profiles API * Added more paging functionality * Finish hooking up admin console user lists * Add API for searching users and add searching to all user lists * Add lazy loading of profiles * Revert config.json * Fix unit tests and some style issues * Add GetProfilesFromList to Go driver and fix web unit test * Update etag for GetProfiles * Updating ui for filters and pagination (#4044) * Updating UI for pagination * Adjusting margins for filter row * Adjusting margin for specific modals * Adding relative padding to system console * Adjusting responsive view * Update client user tests * Minor fixes for direct messages modal (#4056) * Remove some unneeded initial load calls (#4057) * UX updates to user lists, added smart counts and bug fixes (#4059) * Improved getExplicitMentions and unit tests (#4064) * Refactor getting posts to lazy load profiles correctly (#4062) * Comment out SetActiveChannel test (#4066) * Profiler cpu, block, and memory profiler. (#4081) * Fix TestSetActiveChannel unit test (#4071) * Fixing build failure caused by dependancies updating (#4076) * Adding profiler * Fix admin_team_member_dropdown eslint errors * Bumping session cache size (#4077) * Bumping session cache size * Bumping status cache * Refactor how the client handles channel members to be large team friendly (#4106) * Refactor how the client handles channel members to be large team friendly * Change Id to ChannelId in ChannelStats model * Updated getChannelMember and getProfilesByIds routes to match proposal * Performance improvements (#4100) * Performance improvements * Fixing re-connect issue * Fixing error message * Some other minor perf tweaks * Some other minor perf tweaks * Fixing config file * Fixing buffer size * Fixing web socket send message * adding some error logging * fix getMe to be user required * Fix websocket event for new user * Fixing shutting down * Reverting web socket changes * Fixing logging lvl * Adding caching to GetMember * Adding some logging * Fixing caching * Fixing caching invalidate * Fixing direct message caching * Fixing caching * Fixing caching * Remove GetDirectProfiles from initial load * Adding logging and fixing websocket client * Adding back caching from bad merge. * Explicitly close go driver requests (#4162) * Refactored how the client handles team members to be more large team friendly (#4159) * Refactor getProfilesForDirectMessageList API into getAllProfiles API * Refactored how the client handles team members to be more large team friendly * Fix js error when receiving a notification * Fix JS error caused by current user being overwritten with sanitized version (#4165) * Adding error message to status failure (#4167) * Fix a few bugs caused by client scaling refactoring (#4170) * When there is no read replica, don't open a second set of connections to the master database (#4173) * Adding connection tacking to stats (#4174) * Reduce DB writes for statuses and other status related changes (#4175) * Fix bug preventing opening of DM channels from more modal (#4181) * Fixing socket timing error (#4183) * Fixing ping/pong handler * Fixing socket timing error * Commenting out status broadcasting * Removing user status changes * Removing user status changes * Removing user status changes * Removing user status changes * Adding DoPreComputeJson() * Performance improvements (#4194) * * Fix System Console Analytics queries * Add db.SetConnMaxLifetime to 15 minutes * Add "net/http/pprof" for profiling * Add FreeOSMemory() to manually release memory on reload config * Add flag to enable http profiler * Fix memory leak (#4197) * Fix memory leak * removed unneeded nil assignment * Fixing go routine leak (#4208) * Merge fixes * Merge fix * Refactored statuses to be queried by the client rather than broadcast by the server (#4212) * Refactored server code to reduce status broadcasts and to allow getting statuses by IDs * Refactor client code to periodically fetch statuses * Add store unit test for getting statuses by ids * Fix status unit test * Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket * Adding multiple threads to websocket hub (#4230) * Adding multiple threads to websocket hub * Fixing unit tests * Fixing so websocket connections from the same user end up in the same… (#4240) * Fixing so websocket connections from the same user end up in the same list * Removing old comment * Refactor user autocomplete to query the server (#4239) * Add API for autocompleting users * Converted at mention autocomplete to query server * Converted user search autocomplete to query server * Switch autocomplete API naming to use term instead of username * Split autocomplete API into two, one for channels and for teams * Fix copy/paste error * Some final client scaling fixes (#4246) * Add lazy loading of profiles to integration pages * Add lazy loading of profiles to emoji page * Fix JS error when receiving post in select team menu and also clean up channel store
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0512bd26ee
Коммит
365b8b465e
145
api/post.go
145
api/post.go
@@ -35,18 +35,18 @@ const (
|
||||
func InitPost() {
|
||||
l4g.Debug(utils.T("api.post.init.debug"))
|
||||
|
||||
BaseRoutes.NeedTeam.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("POST")
|
||||
BaseRoutes.NeedTeam.Handle("/posts/flagged/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getFlaggedPosts, false)).Methods("GET")
|
||||
BaseRoutes.NeedTeam.Handle("/posts/search", ApiUserRequiredActivity(searchPosts, true)).Methods("POST")
|
||||
BaseRoutes.NeedTeam.Handle("/posts/flagged/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequired(getFlaggedPosts)).Methods("GET")
|
||||
BaseRoutes.NeedTeam.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET")
|
||||
BaseRoutes.NeedTeam.Handle("/pltmp/{post_id}", ApiUserRequired(getPermalinkTmp)).Methods("GET")
|
||||
|
||||
BaseRoutes.Posts.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
|
||||
BaseRoutes.Posts.Handle("/update", ApiUserRequired(updatePost)).Methods("POST")
|
||||
BaseRoutes.Posts.Handle("/page/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequiredActivity(getPosts, false)).Methods("GET")
|
||||
BaseRoutes.Posts.Handle("/since/{time:[0-9]+}", ApiUserRequiredActivity(getPostsSince, false)).Methods("GET")
|
||||
BaseRoutes.Posts.Handle("/create", ApiUserRequiredActivity(createPost, true)).Methods("POST")
|
||||
BaseRoutes.Posts.Handle("/update", ApiUserRequiredActivity(updatePost, true)).Methods("POST")
|
||||
BaseRoutes.Posts.Handle("/page/{offset:[0-9]+}/{limit:[0-9]+}", ApiUserRequired(getPosts)).Methods("GET")
|
||||
BaseRoutes.Posts.Handle("/since/{time:[0-9]+}", ApiUserRequired(getPostsSince)).Methods("GET")
|
||||
|
||||
BaseRoutes.NeedPost.Handle("/get", ApiUserRequired(getPost)).Methods("GET")
|
||||
BaseRoutes.NeedPost.Handle("/delete", ApiUserRequired(deletePost)).Methods("POST")
|
||||
BaseRoutes.NeedPost.Handle("/delete", ApiUserRequiredActivity(deletePost, true)).Methods("POST")
|
||||
BaseRoutes.NeedPost.Handle("/before/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsBefore)).Methods("GET")
|
||||
BaseRoutes.NeedPost.Handle("/after/{offset:[0-9]+}/{num_posts:[0-9]+}", ApiUserRequired(getPostsAfter)).Methods("GET")
|
||||
BaseRoutes.NeedPost.Handle("/get_file_infos", ApiUserRequired(getFileInfosForPost)).Methods("GET")
|
||||
@@ -154,7 +154,7 @@ func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post
|
||||
}
|
||||
}
|
||||
|
||||
go handlePostEvents(c, rpost, triggerWebhooks)
|
||||
handlePostEvents(c, rpost, triggerWebhooks)
|
||||
|
||||
return rpost, nil
|
||||
}
|
||||
@@ -250,7 +250,7 @@ func handlePostEvents(c *Context, post *model.Post, triggerWebhooks bool) {
|
||||
channel = result.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
go sendNotifications(c, post, team, channel)
|
||||
sendNotifications(c, post, team, channel)
|
||||
|
||||
var user *model.User
|
||||
if result := <-uchan; result.Err != nil {
|
||||
@@ -441,40 +441,31 @@ func handleWebhookEvents(c *Context, post *model.Post, team *model.Team, channel
|
||||
}
|
||||
}
|
||||
|
||||
// Given a map of user IDs to profiles and a map of user IDs of channel members, returns a list of mention
|
||||
// keywords for all users on the team. Users that are members of the channel will have all their mention
|
||||
// keywords returned while users that aren't in the channel will only have their @mentions returned.
|
||||
func getMentionKeywords(profiles map[string]*model.User, members map[string]string) map[string][]string {
|
||||
// Given a map of user IDs to profiles, returns a list of mention
|
||||
// keywords for all users in the channel.
|
||||
func getMentionKeywordsInChannel(profiles map[string]*model.User) map[string][]string {
|
||||
keywords := make(map[string][]string)
|
||||
|
||||
for id, profile := range profiles {
|
||||
_, inChannel := members[id]
|
||||
|
||||
if inChannel {
|
||||
if len(profile.NotifyProps["mention_keys"]) > 0 {
|
||||
// Add all the user's mention keys
|
||||
splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
|
||||
for _, k := range splitKeys {
|
||||
// note that these are made lower case so that we can do a case insensitive check for them
|
||||
key := strings.ToLower(k)
|
||||
keywords[key] = append(keywords[key], id)
|
||||
}
|
||||
if len(profile.NotifyProps["mention_keys"]) > 0 {
|
||||
// Add all the user's mention keys
|
||||
splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
|
||||
for _, k := range splitKeys {
|
||||
// note that these are made lower case so that we can do a case insensitive check for them
|
||||
key := strings.ToLower(k)
|
||||
keywords[key] = append(keywords[key], id)
|
||||
}
|
||||
}
|
||||
|
||||
// If turned on, add the user's case sensitive first name
|
||||
if profile.NotifyProps["first_name"] == "true" {
|
||||
keywords[profile.FirstName] = append(keywords[profile.FirstName], profile.Id)
|
||||
}
|
||||
// If turned on, add the user's case sensitive first name
|
||||
if profile.NotifyProps["first_name"] == "true" {
|
||||
keywords[profile.FirstName] = append(keywords[profile.FirstName], profile.Id)
|
||||
}
|
||||
|
||||
// Add @channel and @all to keywords if user has them turned on
|
||||
if profile.NotifyProps["channel"] == "true" {
|
||||
keywords["@channel"] = append(keywords["@channel"], profile.Id)
|
||||
keywords["@all"] = append(keywords["@all"], profile.Id)
|
||||
}
|
||||
} else {
|
||||
// user isn't in channel, so just look for @mentions
|
||||
key := "@" + strings.ToLower(profile.Username)
|
||||
keywords[key] = append(keywords[key], id)
|
||||
// Add @channel and @all to keywords if user has them turned on
|
||||
if profile.NotifyProps["channel"] == "true" {
|
||||
keywords["@channel"] = append(keywords["@channel"], profile.Id)
|
||||
keywords["@all"] = append(keywords["@all"], profile.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,9 +473,11 @@ func getMentionKeywords(profiles map[string]*model.User, members map[string]stri
|
||||
}
|
||||
|
||||
// Given a message and a map mapping mention keywords to the users who use them, returns a map of mentioned
|
||||
// users and whether or not @here was mentioned.
|
||||
func getExplicitMentions(message string, keywords map[string][]string) (map[string]bool, bool) {
|
||||
// users and a slice of potencial mention users not in the channel and whether or not @here was mentioned.
|
||||
func getExplicitMentions(message string, keywords map[string][]string) (map[string]bool, []string, bool) {
|
||||
mentioned := make(map[string]bool)
|
||||
potentialOthersMentioned := make([]string, 0)
|
||||
systemMentions := map[string]bool{"@here": true, "@channel": true, "@all": true}
|
||||
hereMentioned := false
|
||||
|
||||
addMentionedUsers := func(ids []string) {
|
||||
@@ -510,6 +503,9 @@ func getExplicitMentions(message string, keywords map[string][]string) (map[stri
|
||||
if ids, match := keywords[word]; match {
|
||||
addMentionedUsers(ids)
|
||||
isMention = true
|
||||
} else if _, ok := systemMentions[word]; !ok && strings.HasPrefix(word, "@") {
|
||||
potentialOthersMentioned = append(potentialOthersMentioned, word[1:])
|
||||
continue
|
||||
}
|
||||
|
||||
if !isMention {
|
||||
@@ -532,19 +528,19 @@ func getExplicitMentions(message string, keywords map[string][]string) (map[stri
|
||||
// Case-sensitive check for first name
|
||||
if ids, match := keywords[splitWord]; match {
|
||||
addMentionedUsers(ids)
|
||||
} else if _, ok := systemMentions[word]; !ok && strings.HasPrefix(word, "@") {
|
||||
username := word[1:len(splitWord)]
|
||||
potentialOthersMentioned = append(potentialOthersMentioned, username)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mentioned, hereMentioned
|
||||
return mentioned, potentialOthersMentioned, hereMentioned
|
||||
}
|
||||
|
||||
func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel) {
|
||||
// get profiles for all users we could be mentioning
|
||||
pchan := Srv.Store.User().GetProfiles(c.TeamId)
|
||||
dpchan := Srv.Store.User().GetDirectProfiles(c.Session.UserId)
|
||||
mchan := Srv.Store.Channel().GetMembers(post.ChannelId)
|
||||
pchan := Srv.Store.User().GetProfilesInChannel(channel.Id, -1, -1, true)
|
||||
fchan := Srv.Store.FileInfo().GetForPost(post.Id)
|
||||
|
||||
var profileMap map[string]*model.User
|
||||
@@ -555,30 +551,11 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
profileMap = result.Data.(map[string]*model.User)
|
||||
}
|
||||
|
||||
if result := <-dpchan; result.Err != nil {
|
||||
l4g.Error(utils.T("api.post.handle_post_events_and_forget.profiles.error"), c.TeamId, result.Err)
|
||||
return
|
||||
} else {
|
||||
dps := result.Data.(map[string]*model.User)
|
||||
for k, v := range dps {
|
||||
profileMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// If the user who made the post is mention don't send a notification
|
||||
if _, ok := profileMap[post.UserId]; !ok {
|
||||
l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
|
||||
return
|
||||
}
|
||||
// using a map as a pseudo-set since we're checking for containment a lot
|
||||
members := make(map[string]string)
|
||||
if result := <-mchan; result.Err != nil {
|
||||
l4g.Error(utils.T("api.post.handle_post_events_and_forget.members.error"), post.ChannelId, result.Err)
|
||||
return
|
||||
} else {
|
||||
for _, member := range result.Data.([]model.ChannelMember) {
|
||||
members[member.UserId] = member.UserId
|
||||
}
|
||||
}
|
||||
|
||||
mentionedUserIds := make(map[string]bool)
|
||||
allActivityPushUserIds := []string{}
|
||||
@@ -595,11 +572,11 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
|
||||
mentionedUserIds[otherUserId] = true
|
||||
} else {
|
||||
keywords := getMentionKeywords(profileMap, members)
|
||||
keywords := getMentionKeywordsInChannel(profileMap)
|
||||
|
||||
// get users that are explicitly mentioned
|
||||
var mentioned map[string]bool
|
||||
mentioned, hereNotification = getExplicitMentions(post.Message, keywords)
|
||||
var potentialOtherMentions []string
|
||||
mentioned, potentialOtherMentions, hereNotification = getExplicitMentions(post.Message, keywords)
|
||||
|
||||
// get users that have comment thread mentions enabled
|
||||
if len(post.RootId) > 0 {
|
||||
@@ -623,25 +600,15 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
delete(mentioned, post.UserId)
|
||||
}
|
||||
|
||||
outOfChannelMentions := make(map[string]bool)
|
||||
for id := range mentioned {
|
||||
if _, inChannel := members[id]; inChannel {
|
||||
mentionedUserIds[id] = true
|
||||
} else {
|
||||
outOfChannelMentions[id] = true
|
||||
if len(potentialOtherMentions) > 0 {
|
||||
if result := <-Srv.Store.User().GetProfilesByUsernames(potentialOtherMentions, team.Id); result.Err == nil {
|
||||
outOfChannelMentions := result.Data.(map[string]*model.User)
|
||||
go sendOutOfChannelMentions(c, post, outOfChannelMentions)
|
||||
}
|
||||
}
|
||||
|
||||
go sendOutOfChannelMentions(c, post, profileMap, outOfChannelMentions)
|
||||
|
||||
// find which users in the channel are set up to always receive mobile notifications
|
||||
for id := range members {
|
||||
profile := profileMap[id]
|
||||
if profile == nil {
|
||||
l4g.Warn(utils.T("api.post.notification.member_profile.warn"), id)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, profile := range profileMap {
|
||||
if profile.NotifyProps["push"] == model.USER_NOTIFY_ALL &&
|
||||
(post.UserId != profile.Id || post.Props["from_webhook"] == "true") &&
|
||||
!post.IsSystemMessage() {
|
||||
@@ -699,10 +666,9 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
}
|
||||
|
||||
_, profileFound := profileMap[status.UserId]
|
||||
_, isChannelMember := members[status.UserId]
|
||||
_, alreadyMentioned := mentionedUserIds[status.UserId]
|
||||
|
||||
if status.Status == model.STATUS_ONLINE && profileFound && isChannelMember && !alreadyMentioned {
|
||||
if status.Status == model.STATUS_ONLINE && profileFound && !alreadyMentioned {
|
||||
mentionedUsersList = append(mentionedUsersList, status.UserId)
|
||||
updateMentionChans = append(updateMentionChans, Srv.Store.Channel().IncrementMentionCount(post.ChannelId, status.UserId))
|
||||
}
|
||||
@@ -787,7 +753,8 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
message.Add("mentions", model.ArrayToJson(mentionedUsersList))
|
||||
}
|
||||
|
||||
go Publish(message)
|
||||
Publish(message)
|
||||
return
|
||||
}
|
||||
|
||||
func sendNotificationEmail(c *Context, post *model.Post, user *model.User, channel *model.Channel, team *model.Team, senderName string, sender *model.User) {
|
||||
@@ -1045,14 +1012,14 @@ func getMobileAppSession(userId string) *model.Session {
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendOutOfChannelMentions(c *Context, post *model.Post, profiles map[string]*model.User, outOfChannelMentions map[string]bool) {
|
||||
if len(outOfChannelMentions) == 0 {
|
||||
func sendOutOfChannelMentions(c *Context, post *model.Post, profiles map[string]*model.User) {
|
||||
if len(profiles) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var usernames []string
|
||||
for id := range outOfChannelMentions {
|
||||
usernames = append(usernames, profiles[id].Username)
|
||||
for _, user := range profiles {
|
||||
usernames = append(usernames, user.Username)
|
||||
}
|
||||
sort.Strings(usernames)
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user