From 4cf6ae68084bd521d4b492d3963b4db470182f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Villablanca=20V=C3=A1squez?= Date: Wed, 26 Jun 2019 05:52:43 -0400 Subject: [PATCH] Migrates Channel.UpdateLastViewedAt to sync by default (#11240) --- app/channel.go | 11 +- store/sqlstore/channel_store.go | 142 +++++++++++++------------- store/store.go | 2 +- store/storetest/channel_store.go | 15 +-- store/storetest/mocks/ChannelStore.go | 19 +++- 5 files changed, 97 insertions(+), 92 deletions(-) diff --git a/app/channel.go b/app/channel.go index 2c9bf1498e..790c613bd9 100644 --- a/app/channel.go +++ b/app/channel.go @@ -1708,8 +1708,8 @@ func (a *App) SetActiveChannel(userId string, channelId string) *model.AppError } func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *model.AppError { - if result := <-a.Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId); result.Err != nil { - return result.Err + if _, err := a.Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId); err != nil { + return err } if *a.Config().ServiceSettings.EnableChannelViewedMessages { @@ -1856,12 +1856,11 @@ func (a *App) MarkChannelsAsViewed(channelIds []string, userId string, currentSe } } } - result := <-a.Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId) - if result.Err != nil { - return nil, result.Err + times, err := a.Srv.Store.Channel().UpdateLastViewedAt(channelIds, userId) + if err != nil { + return nil, err } - times := result.Data.(map[string]int64) if *a.Config().ServiceSettings.EnableChannelViewedMessages { for _, channelId := range channelIds { message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil) diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index a675dbd972..0c070b3b8f 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -1725,93 +1725,89 @@ func (s SqlChannelStore) PermanentDeleteMembersByUser(userId string) store.Store }) } -func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string) store.StoreChannel { - return store.Do(func(result *store.StoreResult) { - props := make(map[string]interface{}) +func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string) (map[string]int64, *model.AppError) { + props := make(map[string]interface{}) - updateIdQuery := "" - for index, channelId := range channelIds { - if len(updateIdQuery) > 0 { - updateIdQuery += " OR " - } - - props["channelId"+strconv.Itoa(index)] = channelId - updateIdQuery += "ChannelId = :channelId" + strconv.Itoa(index) + updateIdQuery := "" + for index, channelId := range channelIds { + if len(updateIdQuery) > 0 { + updateIdQuery += " OR " } - selectIdQuery := strings.Replace(updateIdQuery, "ChannelId", "Id", -1) + props["channelId"+strconv.Itoa(index)] = channelId + updateIdQuery += "ChannelId = :channelId" + strconv.Itoa(index) + } - var lastPostAtTimes []struct { - Id string - LastPostAt int64 - TotalMsgCount int64 + selectIdQuery := strings.Replace(updateIdQuery, "ChannelId", "Id", -1) + + var lastPostAtTimes []struct { + Id string + LastPostAt int64 + TotalMsgCount int64 + } + + selectQuery := "SELECT Id, LastPostAt, TotalMsgCount FROM Channels WHERE (" + selectIdQuery + ")" + + if _, err := s.GetMaster().Select(&lastPostAtTimes, selectQuery, props); err != nil || len(lastPostAtTimes) <= 0 { + var extra string + status := http.StatusInternalServerError + if err == nil { + status = http.StatusBadRequest + extra = "No channels found" + } else { + extra = err.Error() } + return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+extra, status) + } - selectQuery := "SELECT Id, LastPostAt, TotalMsgCount FROM Channels WHERE (" + selectIdQuery + ")" + times := map[string]int64{} + msgCountQuery := "" + lastViewedQuery := "" + for index, t := range lastPostAtTimes { + times[t.Id] = t.LastPostAt - if _, err := s.GetMaster().Select(&lastPostAtTimes, selectQuery, props); err != nil || len(lastPostAtTimes) <= 0 { - var extra string - status := http.StatusInternalServerError - if err == nil { - status = http.StatusBadRequest - extra = "No channels found" - } else { - extra = err.Error() - } - result.Err = model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+extra, status) - return - } + props["msgCount"+strconv.Itoa(index)] = t.TotalMsgCount + msgCountQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(MsgCount, :msgCount%d) ", index, index) - times := map[string]int64{} - msgCountQuery := "" - lastViewedQuery := "" - for index, t := range lastPostAtTimes { - times[t.Id] = t.LastPostAt + props["lastViewed"+strconv.Itoa(index)] = t.LastPostAt + lastViewedQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(LastViewedAt, :lastViewed%d) ", index, index) - props["msgCount"+strconv.Itoa(index)] = t.TotalMsgCount - msgCountQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(MsgCount, :msgCount%d) ", index, index) + props["channelId"+strconv.Itoa(index)] = t.Id + } - props["lastViewed"+strconv.Itoa(index)] = t.LastPostAt - lastViewedQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(LastViewedAt, :lastViewed%d) ", index, index) + var updateQuery string - props["channelId"+strconv.Itoa(index)] = t.Id - } + if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { + updateQuery = `UPDATE + ChannelMembers + SET + MentionCount = 0, + MsgCount = CAST(CASE ChannelId ` + msgCountQuery + ` END AS BIGINT), + LastViewedAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT), + LastUpdateAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT) + WHERE + UserId = :UserId + AND (` + updateIdQuery + `)` + } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { + updateQuery = `UPDATE + ChannelMembers + SET + MentionCount = 0, + MsgCount = CASE ChannelId ` + msgCountQuery + ` END, + LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END, + LastUpdateAt = CASE ChannelId ` + lastViewedQuery + ` END + WHERE + UserId = :UserId + AND (` + updateIdQuery + `)` + } - var updateQuery string + props["UserId"] = userId - if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { - updateQuery = `UPDATE - ChannelMembers - SET - MentionCount = 0, - MsgCount = CAST(CASE ChannelId ` + msgCountQuery + ` END AS BIGINT), - LastViewedAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT), - LastUpdateAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT) - WHERE - UserId = :UserId - AND (` + updateIdQuery + `)` - } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { - updateQuery = `UPDATE - ChannelMembers - SET - MentionCount = 0, - MsgCount = CASE ChannelId ` + msgCountQuery + ` END, - LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END, - LastUpdateAt = CASE ChannelId ` + lastViewedQuery + ` END - WHERE - UserId = :UserId - AND (` + updateIdQuery + `)` - } + if _, err := s.GetMaster().Exec(updateQuery, props); err != nil { + return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) + } - props["UserId"] = userId - - if _, err := s.GetMaster().Exec(updateQuery, props); err != nil { - result.Err = model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) - return - } - - result.Data = times - }) + return times, nil } func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) *model.AppError { diff --git a/store/store.go b/store/store.go index 80c3f45144..7980b6d5c3 100644 --- a/store/store.go +++ b/store/store.go @@ -176,7 +176,7 @@ type ChannelStore interface { RemoveMember(channelId string, userId string) *model.AppError PermanentDeleteMembersByUser(userId string) StoreChannel PermanentDeleteMembersByChannel(channelId string) *model.AppError - UpdateLastViewedAt(channelIds []string, userId string) StoreChannel + UpdateLastViewedAt(channelIds []string, userId string) (map[string]int64, *model.AppError) IncrementMentionCount(channelId string, userId string) *model.AppError AnalyticsTypeCount(teamId string, channelType string) (int64, *model.AppError) GetMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index a6163f0eb2..c936880211 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -1686,15 +1686,16 @@ func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) { m2.NotifyProps = model.GetDefaultChannelNotifyProps() store.Must(ss.Channel().SaveMember(&m2)) - if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, m1.UserId); result.Err != nil { - t.Fatal("failed to update", result.Err) - } else if result.Data.(map[string]int64)[o1.Id] != o1.LastPostAt { + var times map[string]int64 + if times, err = ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, m1.UserId); err != nil { + t.Fatal("failed to update", err) + } else if times[o1.Id] != o1.LastPostAt { t.Fatal("last viewed at time incorrect") } - if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId, m2.ChannelId}, m1.UserId); result.Err != nil { - t.Fatal("failed to update", result.Err) - } else if result.Data.(map[string]int64)[o2.Id] != o2.LastPostAt { + if times, err = ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId, m2.ChannelId}, m1.UserId); err != nil { + t.Fatal("failed to update", err) + } else if times[o2.Id] != o2.LastPostAt { t.Fatal("last viewed at time incorrect") } @@ -1710,7 +1711,7 @@ func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) { assert.Equal(t, rm2.LastUpdateAt, o2.LastPostAt) assert.Equal(t, rm2.MsgCount, o2.TotalMsgCount) - if result := <-ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, "missing id"); result.Err != nil { + if _, err := ss.Channel().UpdateLastViewedAt([]string{m1.ChannelId}, "missing id"); err != nil { t.Fatal("failed to update") } } diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index ad1e8b299c..6692b0572e 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -1450,19 +1450,28 @@ func (_m *ChannelStore) Update(channel *model.Channel) (*model.Channel, *model.A } // UpdateLastViewedAt provides a mock function with given fields: channelIds, userId -func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userId string) store.StoreChannel { +func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userId string) (map[string]int64, *model.AppError) { ret := _m.Called(channelIds, userId) - var r0 store.StoreChannel - if rf, ok := ret.Get(0).(func([]string, string) store.StoreChannel); ok { + var r0 map[string]int64 + if rf, ok := ret.Get(0).(func([]string, string) map[string]int64); ok { r0 = rf(channelIds, userId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(store.StoreChannel) + r0 = ret.Get(0).(map[string]int64) } } - return r0 + var r1 *model.AppError + if rf, ok := ret.Get(1).(func([]string, string) *model.AppError); ok { + r1 = rf(channelIds, userId) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*model.AppError) + } + } + + return r0, r1 } // UpdateMember provides a mock function with given fields: member