diff --git a/.github/workflows/channels-ci.yml b/.github/workflows/channels-ci.yml
index 7d71862ba7..d6a1a6258c 100644
--- a/.github/workflows/channels-ci.yml
+++ b/.github/workflows/channels-ci.yml
@@ -83,16 +83,6 @@ jobs:
npm run mmjstool -- i18n clean-empty --webapp-dir ./src --mobile-dir /tmp/fake-mobile-dir --check
npm run mmjstool -- i18n check-empty-src --webapp-dir ./src --mobile-dir /tmp/fake-mobile-dir
rm -rf tmp
- - name: ci/lint-boards
- working-directory: webapp/boards
- run: |
- npm run i18n-extract
- git --no-pager diff --exit-code i18n/en.json || (echo "Please run \"cd webapp/boards && npm run i18n-extract\" and commit the changes in webapp/boards/i18n/en.json." && exit 1)
- - name: ci/lint-playbooks
- working-directory: webapp/playbooks
- run: |
- npm run i18n-extract
- git --no-pager diff --exit-code i18n/en.json || (echo "Please run \"cd webapp/playbooks && npm run i18n-extract\" and commit the changes in webapp/playbooks/i18n/en.json." && exit 1)
check-types:
runs-on: ubuntu-22.04
defaults:
diff --git a/CODEOWNERS b/CODEOWNERS
index 4ef5c97c43..b0bd218122 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -5,6 +5,3 @@
/webapp/package-lock.json @mattermost/web-platform
/webapp/platform/*/package.json @mattermost/web-platform
/webapp/scripts @mattermost/web-platform
-/server/channels/db/migrations @mattermost/server-platform
-/server/boards/services/store/sqlstore/migrations @mattermost/server-platform
-/server/playbooks/server/sqlstore/migrations @mattermost/server-platform
diff --git a/e2e-tests/playwright/support/server/default_config.ts b/e2e-tests/playwright/support/server/default_config.ts
index 3e8e5ed881..2d8cb12c58 100644
--- a/e2e-tests/playwright/support/server/default_config.ts
+++ b/e2e-tests/playwright/support/server/default_config.ts
@@ -665,6 +665,7 @@ const defaultServerConfig: AdminConfig = {
BoardsFeatureFlags: '',
BoardsDataRetention: false,
NormalizeLdapDNs: false,
+ UseCaseOnboarding: true,
GraphQL: false,
InsightsEnabled: true,
CommandPalette: false,
diff --git a/server/boards/services/store/sqlstore/migrate.go b/server/boards/services/store/sqlstore/migrate.go
index 63b4665490..1c876a5168 100644
--- a/server/boards/services/store/sqlstore/migrate.go
+++ b/server/boards/services/store/sqlstore/migrate.go
@@ -70,10 +70,7 @@ func (s *SQLStore) getMigrationConnection() (*sql.DB, error) {
}
*settings.DriverName = s.dbType
- db, err := sqlstore.SetupConnection("master", connectionString, &settings, sqlstore.DBPingAttempts)
- if err != nil {
- return nil, err
- }
+ db := sqlstore.SetupConnection("master", connectionString, &settings)
return db, nil
}
diff --git a/server/channels/api4/system_test.go b/server/channels/api4/system_test.go
index 25574e4400..5921e32802 100644
--- a/server/channels/api4/system_test.go
+++ b/server/channels/api4/system_test.go
@@ -892,7 +892,6 @@ func TestCompleteOnboarding(t *testing.T) {
req := &model.CompleteOnboardingRequest{
InstallPlugins: []string{"testplugin2"},
- Organization: "my-org",
}
t.Run("as a regular user", func(t *testing.T) {
diff --git a/server/channels/api4/user.go b/server/channels/api4/user.go
index 2269efe88c..c259922e82 100644
--- a/server/channels/api4/user.go
+++ b/server/channels/api4/user.go
@@ -3106,10 +3106,6 @@ func getThreadForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
- if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannel) {
- c.SetPermissionError(model.PermissionReadChannel)
- return
- }
extendedStr := r.URL.Query().Get("extended")
extended, _ := strconv.ParseBool(extendedStr)
@@ -3140,10 +3136,6 @@ func getThreadsForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
- if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionViewTeam) {
- c.SetPermissionError(model.PermissionViewTeam)
- return
- }
options := model.GetUserThreadsOpts{
Since: 0,
@@ -3221,10 +3213,6 @@ func updateReadStateThreadByUser(c *Context, w http.ResponseWriter, r *http.Requ
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
- if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannel) {
- c.SetPermissionError(model.PermissionReadChannel)
- return
- }
thread, err := c.App.UpdateThreadReadForUser(c.AppContext, c.AppContext.Session().Id, c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, c.Params.Timestamp)
if err != nil {
@@ -3291,10 +3279,6 @@ func unfollowThreadByUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
- if !c.App.SessionHasPermissionToChannelByPost(*c.AppContext.Session(), c.Params.ThreadId, model.PermissionReadChannel) {
- c.SetPermissionError(model.PermissionReadChannel)
- return
- }
err := c.App.UpdateThreadFollowForUser(c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, false)
if err != nil {
@@ -3354,10 +3338,6 @@ func updateReadStateAllThreadsByUser(c *Context, w http.ResponseWriter, r *http.
c.SetPermissionError(model.PermissionEditOtherUsers)
return
}
- if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), c.Params.TeamId, model.PermissionViewTeam) {
- c.SetPermissionError(model.PermissionViewTeam)
- return
- }
err := c.App.UpdateThreadsReadForUser(c.Params.UserId, c.Params.TeamId)
if err != nil {
diff --git a/server/channels/api4/user_test.go b/server/channels/api4/user_test.go
index 0f9ce87d33..8d9673ed9e 100644
--- a/server/channels/api4/user_test.go
+++ b/server/channels/api4/user_test.go
@@ -6360,15 +6360,6 @@ func TestGetThreadsForUser(t *testing.T) {
require.NoError(t, err)
require.Equal(t, uss.TotalUnreadThreads, int64(2))
})
-
- t.Run("should error when not a team member", func(t *testing.T) {
- th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
- defer th.LinkUserToTeam(th.BasicUser, th.BasicTeam)
-
- _, resp, err := th.Client.GetUserThreads(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
- require.Error(t, err)
- CheckForbiddenStatus(t, resp)
- })
}
func TestThreadSocketEvents(t *testing.T) {
@@ -6864,64 +6855,52 @@ func TestSingleThreadGet(t *testing.T) {
})
client := th.Client
+ defer th.App.Srv().Store().Post().PermanentDeleteByUser(th.BasicUser.Id)
+ defer th.App.Srv().Store().Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
- t.Run("get single thread", func(t *testing.T) {
- defer th.App.Srv().Store().Post().PermanentDeleteByUser(th.BasicUser.Id)
- defer th.App.Srv().Store().Post().PermanentDeleteByUser(th.SystemAdminUser.Id)
+ // create a post by regular user
+ rpost, _ := postAndCheck(t, client, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
+ // reply with another
+ postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
- // create a post by regular user
- rpost, _ := postAndCheck(t, client, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testMsg"})
- // reply with another
- postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel.Id, Message: "testReply", RootId: rpost.Id})
-
- // create another thread to check that we are not returning it by mistake
- rpost2, _ := postAndCheck(t, client, &model.Post{
- ChannelId: th.BasicChannel2.Id,
- Message: "testMsg2",
- Metadata: &model.PostMetadata{
- Priority: &model.PostPriority{
- Priority: model.NewString(model.PostPriorityUrgent),
- },
+ // create another thread to check that we are not returning it by mistake
+ rpost2, _ := postAndCheck(t, client, &model.Post{
+ ChannelId: th.BasicChannel2.Id,
+ Message: "testMsg2",
+ Metadata: &model.PostMetadata{
+ Priority: &model.PostPriority{
+ Priority: model.NewString(model.PostPriorityUrgent),
},
- })
- postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel2.Id, Message: "testReply", RootId: rpost2.Id})
+ },
+ })
+ postAndCheck(t, th.SystemAdminClient, &model.Post{ChannelId: th.BasicChannel2.Id, Message: "testReply", RootId: rpost2.Id})
- // regular user should have two threads with 3 replies total
- threads, _ := checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 2, 2, nil)
+ // regular user should have two threads with 3 replies total
+ threads, _ := checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 2, 2, nil)
- tr, _, err := th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, false)
- require.NoError(t, err)
- require.NotNil(t, tr)
- require.Equal(t, threads.Threads[0].PostId, tr.PostId)
- require.Empty(t, tr.Participants[0].Username)
+ tr, _, err := th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, false)
+ require.NoError(t, err)
+ require.NotNil(t, tr)
+ require.Equal(t, threads.Threads[0].PostId, tr.PostId)
+ require.Empty(t, tr.Participants[0].Username)
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.ServiceSettings.PostPriority = false
- })
-
- tr, _, err = th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, true)
- require.NoError(t, err)
- require.NotEmpty(t, tr.Participants[0].Username)
- require.Equal(t, false, tr.IsUrgent)
-
- th.App.UpdateConfig(func(cfg *model.Config) {
- *cfg.ServiceSettings.PostPriority = true
- cfg.FeatureFlags.PostPriority = true
- })
-
- tr, _, err = th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, true)
- require.NoError(t, err)
- require.Equal(t, true, tr.IsUrgent)
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostPriority = false
})
- t.Run("should error when not a team member", func(t *testing.T) {
- th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
- defer th.LinkUserToTeam(th.BasicUser, th.BasicTeam)
+ tr, _, err = th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, true)
+ require.NoError(t, err)
+ require.NotEmpty(t, tr.Participants[0].Username)
+ require.Equal(t, false, tr.IsUrgent)
- _, resp, err := th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, model.NewId(), false)
- require.Error(t, err)
- CheckForbiddenStatus(t, resp)
+ th.App.UpdateConfig(func(cfg *model.Config) {
+ *cfg.ServiceSettings.PostPriority = true
+ cfg.FeatureFlags.PostPriority = true
})
+
+ tr, _, err = th.Client.GetUserThread(th.BasicUser.Id, th.BasicTeam.Id, threads.Threads[0].PostId, true)
+ require.NoError(t, err)
+ require.Equal(t, true, tr.IsUrgent)
}
func TestMaintainUnreadMentionsInThread(t *testing.T) {
@@ -7093,23 +7072,6 @@ func TestReadThreads(t *testing.T) {
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 1, 1, nil)
})
-
- t.Run("should error when not a team member", func(t *testing.T) {
- th.UnlinkUserFromTeam(th.BasicUser, th.BasicTeam)
- defer th.LinkUserToTeam(th.BasicUser, th.BasicTeam)
-
- _, resp, err := th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, model.NewId(), model.GetMillis())
- require.Error(t, err)
- CheckForbiddenStatus(t, resp)
-
- _, resp, err = th.Client.SetThreadUnreadByPostId(th.BasicUser.Id, th.BasicTeam.Id, model.NewId(), model.NewId())
- require.Error(t, err)
- CheckForbiddenStatus(t, resp)
-
- resp, err = th.Client.UpdateThreadsReadForUser(th.BasicUser.Id, th.BasicTeam.Id)
- require.Error(t, err)
- CheckForbiddenStatus(t, resp)
- })
}
func TestMarkThreadUnreadMentionCount(t *testing.T) {
diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go
index 162254b84b..09fb2ce1cc 100644
--- a/server/channels/app/channel.go
+++ b/server/channels/app/channel.go
@@ -2518,9 +2518,6 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
if err := a.Srv().Store().ChannelMemberHistory().LogLeaveEvent(userIDToRemove, channel.Id, model.GetMillis()); err != nil {
return model.NewAppError("removeUserFromChannel", "app.channel_member_history.log_leave_event.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
- if err := a.Srv().Store().Thread().DeleteMembershipsForChannel(userIDToRemove, channel.Id); err != nil {
- return model.NewAppError("removeUserFromChannel", model.NoTranslation, nil, "failed to delete threadmemberships upon leaving channel", http.StatusInternalServerError).Wrap(err)
- }
if isGuest {
currentMembers, err := a.GetChannelMembersForUser(c, channel.TeamId, userIDToRemove)
diff --git a/server/channels/app/channel_test.go b/server/channels/app/channel_test.go
index 2b427ba806..eaf1171a6c 100644
--- a/server/channels/app/channel_test.go
+++ b/server/channels/app/channel_test.go
@@ -609,85 +609,6 @@ func TestLeaveDefaultChannel(t *testing.T) {
_, err = th.App.GetChannelMember(th.Context, townSquare.Id, guest.Id)
assert.NotNil(t, err)
})
-
- t.Run("Trying to leave the default channel should not delete thread memberships", func(t *testing.T) {
- post := &model.Post{
- ChannelId: townSquare.Id,
- Message: "root post",
- UserId: th.BasicUser.Id,
- }
- rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
- require.Nil(t, err)
-
- reply := &model.Post{
- ChannelId: townSquare.Id,
- Message: "reply post",
- UserId: th.BasicUser.Id,
- RootId: rpost.Id,
- }
- _, err = th.App.CreatePost(th.Context, reply, th.BasicChannel, false, true)
- require.Nil(t, err)
-
- threads, err := th.App.GetThreadsForUser(th.BasicUser.Id, townSquare.TeamId, model.GetUserThreadsOpts{})
- require.Nil(t, err)
- require.Len(t, threads.Threads, 1)
-
- err = th.App.LeaveChannel(th.Context, townSquare.Id, th.BasicUser.Id)
- assert.NotNil(t, err, "It should fail to remove a regular user from the default channel")
- assert.Equal(t, err.Id, "api.channel.remove.default.app_error")
-
- threads, err = th.App.GetThreadsForUser(th.BasicUser.Id, townSquare.TeamId, model.GetUserThreadsOpts{})
- require.Nil(t, err)
- require.Len(t, threads.Threads, 1)
- })
-}
-
-func TestLeaveChannel(t *testing.T) {
- th := Setup(t).InitBasic()
- defer th.TearDown()
-
- createThread := func(channel *model.Channel) (rpost *model.Post) {
- t.Helper()
- post := &model.Post{
- ChannelId: channel.Id,
- Message: "root post",
- UserId: th.BasicUser.Id,
- }
-
- rpost, err := th.App.CreatePost(th.Context, post, th.BasicChannel, false, true)
- require.Nil(t, err)
-
- reply := &model.Post{
- ChannelId: channel.Id,
- Message: "reply post",
- UserId: th.BasicUser.Id,
- RootId: rpost.Id,
- }
- _, err = th.App.CreatePost(th.Context, reply, th.BasicChannel, false, true)
- require.Nil(t, err)
-
- return rpost
- }
-
- t.Run("thread memberships are deleted", func(t *testing.T) {
- createThread(th.BasicChannel)
- channel2 := th.createChannel(th.Context, th.BasicTeam, model.ChannelTypeOpen)
- createThread(channel2)
-
- threads, err := th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicChannel.TeamId, model.GetUserThreadsOpts{})
- require.Nil(t, err)
- require.Len(t, threads.Threads, 2)
-
- err = th.App.LeaveChannel(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
- require.Nil(t, err)
-
- _, err = th.App.GetChannelMember(th.Context, th.BasicChannel.Id, th.BasicUser.Id)
- require.NotNil(t, err, "It should remove channel membership")
-
- threads, err = th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicChannel.TeamId, model.GetUserThreadsOpts{})
- require.Nil(t, err)
- require.Len(t, threads.Threads, 1)
- })
}
func TestLeaveLastChannel(t *testing.T) {
diff --git a/server/channels/app/license_test.go b/server/channels/app/license_test.go
index ea6bbcf7eb..7b32ee52e6 100644
--- a/server/channels/app/license_test.go
+++ b/server/channels/app/license_test.go
@@ -71,6 +71,8 @@ func TestGetSanitizedClientLicense(t *testing.T) {
assert.False(t, ok)
_, ok = m["SkuName"]
assert.False(t, ok)
+ _, ok = m["SkuShortName"]
+ assert.False(t, ok)
}
func TestGenerateRenewalToken(t *testing.T) {
diff --git a/server/channels/app/onboarding.go b/server/channels/app/onboarding.go
index 3b76aefe53..2dd85749d9 100644
--- a/server/channels/app/onboarding.go
+++ b/server/channels/app/onboarding.go
@@ -28,24 +28,6 @@ func (a *App) markAdminOnboardingComplete(c *request.Context) *model.AppError {
}
func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError {
- isCloud := a.Srv().License() != nil && *a.Srv().License().Features.Cloud
-
- if !isCloud && request.Organization == "" {
- mlog.Error("No organization name provided for self hosted onboarding")
- return model.NewAppError("CompleteOnboarding", "api.error_no_organization_name_provided_for_self_hosted_onboarding", nil, "", http.StatusBadRequest)
- }
-
- if request.Organization != "" {
- err := a.Srv().Store().System().SaveOrUpdate(&model.System{
- Name: model.SystemOrganizationName,
- Value: request.Organization,
- })
- if err != nil {
- // don't block onboarding because of that.
- a.Log().Error("failed to save organization name", mlog.Err(err))
- }
- }
-
pluginsEnvironment := a.Channels().GetPluginsEnvironment()
if pluginsEnvironment == nil {
return a.markAdminOnboardingComplete(c)
diff --git a/server/channels/app/onboarding_test.go b/server/channels/app/onboarding_test.go
deleted file mode 100644
index cf8462cf28..0000000000
--- a/server/channels/app/onboarding_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package app
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost-server/server/v8/channels/app/request"
- mm_model "github.com/mattermost/mattermost-server/server/v8/model"
-)
-
-func TestOnboardingSavesOrganizationName(t *testing.T) {
- th := Setup(t)
- defer th.TearDown()
-
- err := th.App.CompleteOnboarding(&request.Context{}, &mm_model.CompleteOnboardingRequest{
- Organization: "Mattermost In Tests",
- })
- require.Nil(t, err)
- defer func() {
- th.App.Srv().Store().System().PermanentDeleteByName(mm_model.SystemOrganizationName)
- }()
-
- sys, storeErr := th.App.Srv().Store().System().GetByName(mm_model.SystemOrganizationName)
- require.NoError(t, storeErr)
- require.Equal(t, "Mattermost In Tests", sys.Value)
-}
diff --git a/server/channels/app/platform/license_test.go b/server/channels/app/platform/license_test.go
index 258c2fbe38..6682348130 100644
--- a/server/channels/app/platform/license_test.go
+++ b/server/channels/app/platform/license_test.go
@@ -71,6 +71,8 @@ func TestGetSanitizedClientLicense(t *testing.T) {
assert.False(t, ok)
_, ok = m["SkuName"]
assert.False(t, ok)
+ _, ok = m["SkuShortName"]
+ assert.False(t, ok)
}
func TestGenerateRenewalToken(t *testing.T) {
diff --git a/server/channels/db/migrations/migrations.list b/server/channels/db/migrations/migrations.list
index 47f5bf333b..6a7d33d5c6 100644
--- a/server/channels/db/migrations/migrations.list
+++ b/server/channels/db/migrations/migrations.list
@@ -212,8 +212,6 @@ channels/db/migrations/mysql/000105_remove_tokens.down.sql
channels/db/migrations/mysql/000105_remove_tokens.up.sql
channels/db/migrations/mysql/000106_fileinfo_channelid.down.sql
channels/db/migrations/mysql/000106_fileinfo_channelid.up.sql
-channels/db/migrations/mysql/000107_threadmemberships_cleanup.down.sql
-channels/db/migrations/mysql/000107_threadmemberships_cleanup.up.sql
channels/db/migrations/postgres/000001_create_teams.down.sql
channels/db/migrations/postgres/000001_create_teams.up.sql
channels/db/migrations/postgres/000002_create_team_members.down.sql
@@ -426,5 +424,3 @@ channels/db/migrations/postgres/000105_remove_tokens.down.sql
channels/db/migrations/postgres/000105_remove_tokens.up.sql
channels/db/migrations/postgres/000106_fileinfo_channelid.down.sql
channels/db/migrations/postgres/000106_fileinfo_channelid.up.sql
-channels/db/migrations/postgres/000107_threadmemberships_cleanup.down.sql
-channels/db/migrations/postgres/000107_threadmemberships_cleanup.up.sql
diff --git a/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.down.sql b/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.down.sql
deleted file mode 100644
index 4743bd6462..0000000000
--- a/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.down.sql
+++ /dev/null
@@ -1 +0,0 @@
--- Skipping it because the forward migrations are destructive
diff --git a/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.up.sql b/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.up.sql
deleted file mode 100644
index 90644be3f3..0000000000
--- a/server/channels/db/migrations/mysql/000107_threadmemberships_cleanup.up.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-DELETE FROM
- tm USING ThreadMemberships AS tm
- JOIN Threads ON Threads.PostId = tm.PostId
-WHERE
- (tm.UserId, Threads.ChannelId) NOT IN (SELECT UserId, ChannelId FROM ChannelMembers);
diff --git a/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.down.sql b/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.down.sql
deleted file mode 100644
index 4743bd6462..0000000000
--- a/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.down.sql
+++ /dev/null
@@ -1 +0,0 @@
--- Skipping it because the forward migrations are destructive
diff --git a/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.up.sql b/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.up.sql
deleted file mode 100644
index 0ec82905bc..0000000000
--- a/server/channels/db/migrations/postgres/000107_threadmemberships_cleanup.up.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-DELETE FROM threadmemberships WHERE (postid, userid) IN (
- SELECT
- threadmemberships.postid,
- threadmemberships.userid
- FROM
- threadmemberships
- JOIN threads ON threads.postid = threadmemberships.postid
- LEFT JOIN channelmembers ON channelmembers.userid = threadmemberships.userid
- AND threads.channelid = channelmembers.channelid
- WHERE
- channelmembers.channelid IS NULL
-);
diff --git a/server/channels/einterfaces/metrics.go b/server/channels/einterfaces/metrics.go
index c44af2a3c5..06f44f7b66 100644
--- a/server/channels/einterfaces/metrics.go
+++ b/server/channels/einterfaces/metrics.go
@@ -13,7 +13,6 @@ import (
type MetricsInterface interface {
Register()
RegisterDBCollector(db *sql.DB, name string)
- UnregisterDBCollector(db *sql.DB, name string)
IncrementPostCreate()
IncrementWebhookPost()
diff --git a/server/channels/einterfaces/mocks/MetricsInterface.go b/server/channels/einterfaces/mocks/MetricsInterface.go
index 06f568546a..0d6f799ee5 100644
--- a/server/channels/einterfaces/mocks/MetricsInterface.go
+++ b/server/channels/einterfaces/mocks/MetricsInterface.go
@@ -319,11 +319,6 @@ func (_m *MetricsInterface) SetReplicaLagTime(node string, value float64) {
_m.Called(node, value)
}
-// UnregisterDBCollector provides a mock function with given fields: db, name
-func (_m *MetricsInterface) UnregisterDBCollector(db *sql.DB, name string) {
- _m.Called(db, name)
-}
-
type mockConstructorTestingTNewMetricsInterface interface {
mock.TestingT
Cleanup(func())
diff --git a/server/channels/store/opentracinglayer/opentracinglayer.go b/server/channels/store/opentracinglayer/opentracinglayer.go
index 66a5051260..941704a2f4 100644
--- a/server/channels/store/opentracinglayer/opentracinglayer.go
+++ b/server/channels/store/opentracinglayer/opentracinglayer.go
@@ -10123,24 +10123,6 @@ func (s *OpenTracingLayerThreadStore) DeleteMembershipForUser(userId string, pos
return err
}
-func (s *OpenTracingLayerThreadStore) DeleteMembershipsForChannel(userID string, channelID string) error {
- origCtx := s.Root.Store.Context()
- span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.DeleteMembershipsForChannel")
- s.Root.Store.SetContext(newCtx)
- defer func() {
- s.Root.Store.SetContext(origCtx)
- }()
-
- defer span.Finish()
- err := s.ThreadStore.DeleteMembershipsForChannel(userID, channelID)
- if err != nil {
- span.LogFields(spanlog.Error(err))
- ext.Error.Set(span, true)
- }
-
- return err
-}
-
func (s *OpenTracingLayerThreadStore) DeleteOrphanedRows(limit int) (int64, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.DeleteOrphanedRows")
diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go
index b39c79ab9b..91a3209c44 100644
--- a/server/channels/store/retrylayer/retrylayer.go
+++ b/server/channels/store/retrylayer/retrylayer.go
@@ -11563,27 +11563,6 @@ func (s *RetryLayerThreadStore) DeleteMembershipForUser(userId string, postID st
}
-func (s *RetryLayerThreadStore) DeleteMembershipsForChannel(userID string, channelID string) error {
-
- tries := 0
- for {
- err := s.ThreadStore.DeleteMembershipsForChannel(userID, channelID)
- if err == nil {
- return nil
- }
- if !isRepeatableError(err) {
- return err
- }
- tries++
- if tries >= 3 {
- err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
- return err
- }
- timepkg.Sleep(100 * timepkg.Millisecond)
- }
-
-}
-
func (s *RetryLayerThreadStore) DeleteOrphanedRows(limit int) (int64, error) {
tries := 0
diff --git a/server/channels/store/sqlstore/channel_store_categories.go b/server/channels/store/sqlstore/channel_store_categories.go
index 4eca0d5de0..3f2f726a0f 100644
--- a/server/channels/store/sqlstore/channel_store_categories.go
+++ b/server/channels/store/sqlstore/channel_store_categories.go
@@ -335,7 +335,7 @@ func (s SqlChannelStore) CreateSidebarCategory(userId, teamId string, newCategor
Id: newCategoryId,
UserId: userId,
TeamId: teamId,
- Sorting: newCategory.Sorting,
+ Sorting: model.SidebarCategorySortDefault,
SortOrder: int64(model.MinimalSidebarSortDistance * len(newOrder)), // first we place it at the end of the list
Type: model.SidebarCategoryCustom,
Muted: newCategory.Muted,
diff --git a/server/channels/store/sqlstore/sqlx_wrapper.go b/server/channels/store/sqlstore/sqlx_wrapper.go
index e8d771cada..0dab579512 100644
--- a/server/channels/store/sqlstore/sqlx_wrapper.go
+++ b/server/channels/store/sqlstore/sqlx_wrapper.go
@@ -6,12 +6,9 @@ package sqlstore
import (
"context"
"database/sql"
- "errors"
- "net"
"regexp"
"strconv"
"strings"
- "sync/atomic"
"time"
"unicode"
@@ -69,18 +66,14 @@ type sqlxDBWrapper struct {
*sqlx.DB
queryTimeout time.Duration
trace bool
- isOnline *atomic.Bool
}
func newSqlxDBWrapper(db *sqlx.DB, timeout time.Duration, trace bool) *sqlxDBWrapper {
- w := &sqlxDBWrapper{
+ return &sqlxDBWrapper{
DB: db,
queryTimeout: timeout,
trace: trace,
- isOnline: &atomic.Bool{},
}
- w.isOnline.Store(true)
- return w
}
func (w *sqlxDBWrapper) Stats() sql.DBStats {
@@ -90,19 +83,19 @@ func (w *sqlxDBWrapper) Stats() sql.DBStats {
func (w *sqlxDBWrapper) Beginx() (*sqlxTxWrapper, error) {
tx, err := w.DB.Beginx()
if err != nil {
- return nil, w.checkErr(err)
+ return nil, err
}
- return newSqlxTxWrapper(tx, w.queryTimeout, w.trace, w), nil
+ return newSqlxTxWrapper(tx, w.queryTimeout, w.trace), nil
}
func (w *sqlxDBWrapper) BeginXWithIsolation(opts *sql.TxOptions) (*sqlxTxWrapper, error) {
tx, err := w.DB.BeginTxx(context.Background(), opts)
if err != nil {
- return nil, w.checkErr(err)
+ return nil, err
}
- return newSqlxTxWrapper(tx, w.queryTimeout, w.trace, w), nil
+ return newSqlxTxWrapper(tx, w.queryTimeout, w.trace), nil
}
func (w *sqlxDBWrapper) Get(dest any, query string, args ...any) error {
@@ -116,7 +109,7 @@ func (w *sqlxDBWrapper) Get(dest any, query string, args ...any) error {
}(time.Now())
}
- return w.checkErr(w.DB.GetContext(ctx, dest, query, args...))
+ return w.DB.GetContext(ctx, dest, query, args...)
}
func (w *sqlxDBWrapper) GetBuilder(dest any, builder Builder) error {
@@ -141,7 +134,7 @@ func (w *sqlxDBWrapper) NamedExec(query string, arg any) (sql.Result, error) {
}(time.Now())
}
- return w.checkErrWithResult(w.DB.NamedExecContext(ctx, query, arg))
+ return w.DB.NamedExecContext(ctx, query, arg)
}
func (w *sqlxDBWrapper) Exec(query string, args ...any) (sql.Result, error) {
@@ -168,7 +161,7 @@ func (w *sqlxDBWrapper) ExecNoTimeout(query string, args ...any) (sql.Result, er
}(time.Now())
}
- return w.checkErrWithResult(w.DB.ExecContext(context.Background(), query, args...))
+ return w.DB.ExecContext(context.Background(), query, args...)
}
// ExecRaw is like Exec but without any rebinding of params. You need to pass
@@ -183,7 +176,7 @@ func (w *sqlxDBWrapper) ExecRaw(query string, args ...any) (sql.Result, error) {
}(time.Now())
}
- return w.checkErrWithResult(w.DB.ExecContext(ctx, query, args...))
+ return w.DB.ExecContext(ctx, query, args...)
}
func (w *sqlxDBWrapper) NamedQuery(query string, arg any) (*sqlx.Rows, error) {
@@ -199,7 +192,7 @@ func (w *sqlxDBWrapper) NamedQuery(query string, arg any) (*sqlx.Rows, error) {
}(time.Now())
}
- return w.checkErrWithRows(w.DB.NamedQueryContext(ctx, query, arg))
+ return w.DB.NamedQueryContext(ctx, query, arg)
}
func (w *sqlxDBWrapper) QueryRowX(query string, args ...any) *sqlx.Row {
@@ -227,7 +220,7 @@ func (w *sqlxDBWrapper) QueryX(query string, args ...any) (*sqlx.Rows, error) {
}(time.Now())
}
- return w.checkErrWithRows(w.DB.QueryxContext(ctx, query, args))
+ return w.DB.QueryxContext(ctx, query, args)
}
func (w *sqlxDBWrapper) Select(dest any, query string, args ...any) error {
@@ -245,7 +238,7 @@ func (w *sqlxDBWrapper) SelectCtx(ctx context.Context, dest any, query string, a
}(time.Now())
}
- return w.checkErr(w.DB.SelectContext(ctx, dest, query, args...))
+ return w.DB.SelectContext(ctx, dest, query, args...)
}
func (w *sqlxDBWrapper) SelectBuilder(dest any, builder Builder) error {
@@ -261,15 +254,13 @@ type sqlxTxWrapper struct {
*sqlx.Tx
queryTimeout time.Duration
trace bool
- dbw *sqlxDBWrapper
}
-func newSqlxTxWrapper(tx *sqlx.Tx, timeout time.Duration, trace bool, dbw *sqlxDBWrapper) *sqlxTxWrapper {
+func newSqlxTxWrapper(tx *sqlx.Tx, timeout time.Duration, trace bool) *sqlxTxWrapper {
return &sqlxTxWrapper{
Tx: tx,
queryTimeout: timeout,
trace: trace,
- dbw: dbw,
}
}
@@ -284,7 +275,7 @@ func (w *sqlxTxWrapper) Get(dest any, query string, args ...any) error {
}(time.Now())
}
- return w.dbw.checkErr(w.Tx.GetContext(ctx, dest, query, args...))
+ return w.Tx.GetContext(ctx, dest, query, args...)
}
func (w *sqlxTxWrapper) GetBuilder(dest any, builder Builder) error {
@@ -293,13 +284,13 @@ func (w *sqlxTxWrapper) GetBuilder(dest any, builder Builder) error {
return err
}
- return w.dbw.checkErr(w.Get(dest, query, args...))
+ return w.Get(dest, query, args...)
}
func (w *sqlxTxWrapper) Exec(query string, args ...any) (sql.Result, error) {
query = w.Tx.Rebind(query)
- return w.dbw.checkErrWithResult(w.ExecRaw(query, args...))
+ return w.ExecRaw(query, args...)
}
func (w *sqlxTxWrapper) ExecNoTimeout(query string, args ...any) (sql.Result, error) {
@@ -311,7 +302,7 @@ func (w *sqlxTxWrapper) ExecNoTimeout(query string, args ...any) (sql.Result, er
}(time.Now())
}
- return w.dbw.checkErrWithResult(w.Tx.ExecContext(context.Background(), query, args...))
+ return w.Tx.ExecContext(context.Background(), query, args...)
}
func (w *sqlxTxWrapper) ExecBuilder(builder Builder) (sql.Result, error) {
@@ -335,7 +326,7 @@ func (w *sqlxTxWrapper) ExecRaw(query string, args ...any) (sql.Result, error) {
}(time.Now())
}
- return w.dbw.checkErrWithResult(w.Tx.ExecContext(ctx, query, args...))
+ return w.Tx.ExecContext(ctx, query, args...)
}
func (w *sqlxTxWrapper) NamedExec(query string, arg any) (sql.Result, error) {
@@ -351,7 +342,7 @@ func (w *sqlxTxWrapper) NamedExec(query string, arg any) (sql.Result, error) {
}(time.Now())
}
- return w.dbw.checkErrWithResult(w.Tx.NamedExecContext(ctx, query, arg))
+ return w.Tx.NamedExecContext(ctx, query, arg)
}
func (w *sqlxTxWrapper) NamedQuery(query string, arg any) (*sqlx.Rows, error) {
@@ -395,7 +386,7 @@ func (w *sqlxTxWrapper) NamedQuery(query string, arg any) (*sqlx.Rows, error) {
}
}
- return res.rows, w.dbw.checkErr(res.err)
+ return res.rows, res.err
}
func (w *sqlxTxWrapper) QueryRowX(query string, args ...any) *sqlx.Row {
@@ -423,7 +414,7 @@ func (w *sqlxTxWrapper) QueryX(query string, args ...any) (*sqlx.Rows, error) {
}(time.Now())
}
- return w.dbw.checkErrWithRows(w.Tx.QueryxContext(ctx, query, args))
+ return w.Tx.QueryxContext(ctx, query, args)
}
func (w *sqlxTxWrapper) Select(dest any, query string, args ...any) error {
@@ -437,7 +428,7 @@ func (w *sqlxTxWrapper) Select(dest any, query string, args ...any) error {
}(time.Now())
}
- return w.dbw.checkErr(w.Tx.SelectContext(ctx, dest, query, args...))
+ return w.Tx.SelectContext(ctx, dest, query, args...)
}
func (w *sqlxTxWrapper) SelectBuilder(dest any, builder Builder) error {
@@ -468,23 +459,3 @@ func printArgs(query string, dur time.Duration, args ...any) {
}
mlog.Debug(query, fields...)
}
-
-func (w *sqlxDBWrapper) checkErrWithResult(res sql.Result, err error) (sql.Result, error) {
- return res, w.checkErr(err)
-}
-
-func (w *sqlxDBWrapper) checkErrWithRows(res *sqlx.Rows, err error) (*sqlx.Rows, error) {
- return res, w.checkErr(err)
-}
-
-func (w *sqlxDBWrapper) checkErr(err error) error {
- var netError *net.OpError
- if errors.As(err, &netError) && (!netError.Temporary() && !netError.Timeout()) {
- w.isOnline.Store(false)
- }
- return err
-}
-
-func (w *sqlxDBWrapper) Online() bool {
- return w.isOnline.Load()
-}
diff --git a/server/channels/store/sqlstore/sqlx_wrapper_test.go b/server/channels/store/sqlstore/sqlx_wrapper_test.go
index c03d228935..07c6391767 100644
--- a/server/channels/store/sqlstore/sqlx_wrapper_test.go
+++ b/server/channels/store/sqlstore/sqlx_wrapper_test.go
@@ -6,7 +6,6 @@ package sqlstore
import (
"context"
"strings"
- "sync"
"testing"
"github.com/stretchr/testify/assert"
@@ -29,14 +28,12 @@ func TestSqlX(t *testing.T) {
}
*settings.QueryTimeout = 1
store := &SqlStore{
- rrCounter: 0,
- srCounter: 0,
- settings: settings,
- quitMonitor: make(chan struct{}),
- wgMonitor: &sync.WaitGroup{},
+ rrCounter: 0,
+ srCounter: 0,
+ settings: settings,
}
- require.NoError(t, store.initConnection())
+ store.initConnection()
defer store.Close()
diff --git a/server/channels/store/sqlstore/store.go b/server/channels/store/sqlstore/store.go
index acd02b0853..d39f92661c 100644
--- a/server/channels/store/sqlstore/store.go
+++ b/server/channels/store/sqlstore/store.go
@@ -49,7 +49,7 @@ const (
MySQLForeignKeyViolationErrorCode = 1452
PGDuplicateObjectErrorCode = "42710"
MySQLDuplicateObjectErrorCode = 1022
- DBPingAttempts = 5
+ DBPingAttempts = 18
DBPingTimeoutSecs = 10
// This is a numerical version string by postgres. The format is
// 2 characters for major, minor, and patch version prior to 10.
@@ -123,9 +123,9 @@ type SqlStore struct {
masterX *sqlxDBWrapper
- ReplicaXs []*atomic.Pointer[sqlxDBWrapper]
+ ReplicaXs []*sqlxDBWrapper
- searchReplicaXs []*atomic.Pointer[sqlxDBWrapper]
+ searchReplicaXs []*sqlxDBWrapper
replicaLagHandles []*dbsql.DB
stores SqlStoreStores
@@ -138,28 +138,17 @@ type SqlStore struct {
isBinaryParam bool
pgDefaultTextSearchConfig string
-
- quitMonitor chan struct{}
- wgMonitor *sync.WaitGroup
}
func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlStore {
store := &SqlStore{
- rrCounter: 0,
- srCounter: 0,
- settings: &settings,
- metrics: metrics,
- quitMonitor: make(chan struct{}),
- wgMonitor: &sync.WaitGroup{},
+ rrCounter: 0,
+ srCounter: 0,
+ settings: &settings,
+ metrics: metrics,
}
- err := store.initConnection()
- if err != nil {
- mlog.Fatal("Error setting up connections", mlog.Err(err))
- }
-
- store.wgMonitor.Add(1)
- go store.monitorReplicas()
+ store.initConnection()
ver, err := store.GetDbVersion(true)
if err != nil {
@@ -241,28 +230,29 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
// SetupConnection sets up the connection to the database and pings it to make sure it's alive.
// It also applies any database configuration settings that are required.
-func SetupConnection(connType string, dataSource string, settings *model.SqlSettings, attempts int) (*dbsql.DB, error) {
+func SetupConnection(connType string, dataSource string, settings *model.SqlSettings) *dbsql.DB {
db, err := dbsql.Open(*settings.DriverName, dataSource)
if err != nil {
- return nil, errors.Wrap(err, "failed to open SQL connection")
+ mlog.Fatal("Failed to open SQL connection to err.", mlog.Err(err))
}
- for i := 0; i < attempts; i++ {
+ for i := 0; i < DBPingAttempts; i++ {
// At this point, we have passed sql.Open, so we deliberately ignore any errors.
sanitized, _ := SanitizeDataSource(*settings.DriverName, dataSource)
mlog.Info("Pinging SQL", mlog.String("database", connType), mlog.String("dataSource", sanitized))
ctx, cancel := context.WithTimeout(context.Background(), DBPingTimeoutSecs*time.Second)
defer cancel()
err = db.PingContext(ctx)
- if err != nil {
- if i == attempts-1 {
- return nil, err
+ if err == nil {
+ break
+ } else {
+ if i == DBPingAttempts-1 {
+ mlog.Fatal("Failed to ping DB, server will exit.", mlog.Err(err))
+ } else {
+ mlog.Error("Failed to ping DB", mlog.Err(err), mlog.Int("retrying in seconds", DBPingTimeoutSecs))
+ time.Sleep(DBPingTimeoutSecs * time.Second)
}
- mlog.Error("Failed to ping DB", mlog.Err(err), mlog.Int("retrying in seconds", DBPingTimeoutSecs))
- time.Sleep(DBPingTimeoutSecs * time.Second)
- continue
}
- break
}
if strings.HasPrefix(connType, replicaLagPrefix) {
@@ -282,7 +272,7 @@ func SetupConnection(connType string, dataSource string, settings *model.SqlSett
db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
db.SetConnMaxIdleTime(time.Duration(*settings.ConnMaxIdleTimeMilliseconds) * time.Millisecond)
- return db, nil
+ return db
}
func (ss *SqlStore) SetContext(context context.Context) {
@@ -295,7 +285,7 @@ func (ss *SqlStore) Context() context.Context {
func noOpMapper(s string) string { return s }
-func (ss *SqlStore) initConnection() error {
+func (ss *SqlStore) initConnection() {
dataSource := *ss.settings.DataSource
if ss.DriverName() == model.DatabaseDriverMysql {
// TODO: We ignore the readTimeout datasource parameter for MySQL since QueryTimeout
@@ -304,14 +294,11 @@ func (ss *SqlStore) initConnection() error {
var err error
dataSource, err = ResetReadTimeout(dataSource)
if err != nil {
- return errors.Wrap(err, "failed to reset read timeout from datasource")
+ mlog.Fatal("Failed to reset read timeout from datasource.", mlog.Err(err), mlog.String("src", dataSource))
}
}
- handle, err := SetupConnection("master", dataSource, ss.settings, DBPingAttempts)
- if err != nil {
- return err
- }
+ handle := SetupConnection("master", dataSource, ss.settings)
ss.masterX = newSqlxDBWrapper(sqlx.NewDb(handle, ss.DriverName()),
time.Duration(*ss.settings.QueryTimeout)*time.Second,
*ss.settings.Trace)
@@ -323,32 +310,34 @@ func (ss *SqlStore) initConnection() error {
}
if len(ss.settings.DataSourceReplicas) > 0 {
- ss.ReplicaXs = make([]*atomic.Pointer[sqlxDBWrapper], len(ss.settings.DataSourceReplicas))
+ ss.ReplicaXs = make([]*sqlxDBWrapper, len(ss.settings.DataSourceReplicas))
for i, replica := range ss.settings.DataSourceReplicas {
- ss.ReplicaXs[i] = &atomic.Pointer[sqlxDBWrapper]{}
- handle, err = SetupConnection(fmt.Sprintf("replica-%v", i), replica, ss.settings, DBPingAttempts)
- if err != nil {
- // Initializing to be offline
- ss.ReplicaXs[i].Store(&sqlxDBWrapper{isOnline: &atomic.Bool{}})
- mlog.Warn("Failed to setup connection. Skipping..", mlog.String("db", fmt.Sprintf("replica-%v", i)), mlog.Err(err))
- continue
+ handle := SetupConnection(fmt.Sprintf("replica-%v", i), replica, ss.settings)
+ ss.ReplicaXs[i] = newSqlxDBWrapper(sqlx.NewDb(handle, ss.DriverName()),
+ time.Duration(*ss.settings.QueryTimeout)*time.Second,
+ *ss.settings.Trace)
+ if ss.DriverName() == model.DatabaseDriverMysql {
+ ss.ReplicaXs[i].MapperFunc(noOpMapper)
+ }
+ if ss.metrics != nil {
+ ss.metrics.RegisterDBCollector(ss.ReplicaXs[i].DB.DB, "replica-"+strconv.Itoa(i))
}
- ss.setDB(ss.ReplicaXs[i], handle, "replica-"+strconv.Itoa(i))
}
}
if len(ss.settings.DataSourceSearchReplicas) > 0 {
- ss.searchReplicaXs = make([]*atomic.Pointer[sqlxDBWrapper], len(ss.settings.DataSourceSearchReplicas))
+ ss.searchReplicaXs = make([]*sqlxDBWrapper, len(ss.settings.DataSourceSearchReplicas))
for i, replica := range ss.settings.DataSourceSearchReplicas {
- ss.searchReplicaXs[i] = &atomic.Pointer[sqlxDBWrapper]{}
- handle, err = SetupConnection(fmt.Sprintf("search-replica-%v", i), replica, ss.settings, DBPingAttempts)
- if err != nil {
- // Initializing to be offline
- ss.searchReplicaXs[i].Store(&sqlxDBWrapper{isOnline: &atomic.Bool{}})
- mlog.Warn("Failed to setup connection. Skipping..", mlog.String("db", fmt.Sprintf("search-replica-%v", i)), mlog.Err(err))
- continue
+ handle := SetupConnection(fmt.Sprintf("search-replica-%v", i), replica, ss.settings)
+ ss.searchReplicaXs[i] = newSqlxDBWrapper(sqlx.NewDb(handle, ss.DriverName()),
+ time.Duration(*ss.settings.QueryTimeout)*time.Second,
+ *ss.settings.Trace)
+ if ss.DriverName() == model.DatabaseDriverMysql {
+ ss.searchReplicaXs[i].MapperFunc(noOpMapper)
+ }
+ if ss.metrics != nil {
+ ss.metrics.RegisterDBCollector(ss.searchReplicaXs[i].DB.DB, "searchreplica-"+strconv.Itoa(i))
}
- ss.setDB(ss.searchReplicaXs[i], handle, "searchreplica-"+strconv.Itoa(i))
}
}
@@ -358,14 +347,9 @@ func (ss *SqlStore) initConnection() error {
if src.DataSource == nil {
continue
}
- ss.replicaLagHandles[i], err = SetupConnection(fmt.Sprintf(replicaLagPrefix+"-%d", i), *src.DataSource, ss.settings, DBPingAttempts)
- if err != nil {
- mlog.Warn("Failed to setup replica lag handle. Skipping..", mlog.String("db", fmt.Sprintf(replicaLagPrefix+"-%d", i)), mlog.Err(err))
- continue
- }
+ ss.replicaLagHandles[i] = SetupConnection(fmt.Sprintf(replicaLagPrefix+"-%d", i), *src.DataSource, ss.settings)
}
}
- return nil
}
func (ss *SqlStore) DriverName() string {
@@ -471,15 +455,8 @@ func (ss *SqlStore) GetSearchReplicaX() *sqlxDBWrapper {
return ss.GetReplicaX()
}
- for i := 0; i < len(ss.searchReplicaXs); i++ {
- rrNum := atomic.AddInt64(&ss.srCounter, 1) % int64(len(ss.searchReplicaXs))
- if ss.searchReplicaXs[rrNum].Load().Online() {
- return ss.searchReplicaXs[rrNum].Load()
- }
- }
-
- // If all search replicas are down, then go with replica.
- return ss.GetReplicaX()
+ rrNum := atomic.AddInt64(&ss.srCounter, 1) % int64(len(ss.searchReplicaXs))
+ return ss.searchReplicaXs[rrNum]
}
func (ss *SqlStore) GetReplicaX() *sqlxDBWrapper {
@@ -487,64 +464,23 @@ func (ss *SqlStore) GetReplicaX() *sqlxDBWrapper {
return ss.GetMasterX()
}
- for i := 0; i < len(ss.ReplicaXs); i++ {
- rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.ReplicaXs))
- if ss.ReplicaXs[rrNum].Load().Online() {
- return ss.ReplicaXs[rrNum].Load()
+ rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.ReplicaXs))
+ return ss.ReplicaXs[rrNum]
+}
+
+func (ss *SqlStore) GetInternalReplicaDBs() []*sql.DB {
+ if len(ss.settings.DataSourceReplicas) == 0 || ss.lockedToMaster || !ss.hasLicense() {
+ return []*sql.DB{
+ ss.GetMasterX().DB.DB,
}
}
- // If all replicas are down, then go with master.
- return ss.GetMasterX()
-}
-
-func (ss *SqlStore) monitorReplicas() {
- t := time.NewTicker(time.Duration(*ss.settings.ReplicaMonitorIntervalSeconds) * time.Second)
- defer func() {
- t.Stop()
- ss.wgMonitor.Done()
- }()
- for {
- select {
- case <-ss.quitMonitor:
- return
- case <-t.C:
- setupReplica := func(r *atomic.Pointer[sqlxDBWrapper], dsn, name string) {
- if r.Load().Online() {
- return
- }
-
- handle, err := SetupConnection(name, dsn, ss.settings, 1)
- if err != nil {
- mlog.Warn("Failed to setup connection. Skipping..", mlog.String("db", name), mlog.Err(err))
- return
- }
- if ss.metrics != nil && r.Load() != nil && r.Load().DB != nil {
- ss.metrics.UnregisterDBCollector(r.Load().DB.DB, name)
- }
- ss.setDB(r, handle, name)
- }
- for i, replica := range ss.ReplicaXs {
- setupReplica(replica, ss.settings.DataSourceReplicas[i], "replica-"+strconv.Itoa(i))
- }
-
- for i, replica := range ss.searchReplicaXs {
- setupReplica(replica, ss.settings.DataSourceSearchReplicas[i], "search-replica-"+strconv.Itoa(i))
- }
- }
+ dbs := make([]*sql.DB, len(ss.ReplicaXs))
+ for i, rx := range ss.ReplicaXs {
+ dbs[i] = rx.DB.DB
}
-}
-func (ss *SqlStore) setDB(replica *atomic.Pointer[sqlxDBWrapper], handle *dbsql.DB, name string) {
- replica.Store(newSqlxDBWrapper(sqlx.NewDb(handle, ss.DriverName()),
- time.Duration(*ss.settings.QueryTimeout)*time.Second,
- *ss.settings.Trace))
- if ss.DriverName() == model.DatabaseDriverMysql {
- replica.Load().MapperFunc(noOpMapper)
- }
- if ss.metrics != nil {
- ss.metrics.RegisterDBCollector(replica.Load().DB.DB, name)
- }
+ return dbs
}
func (ss *SqlStore) GetInternalReplicaDB() *sql.DB {
@@ -553,7 +489,7 @@ func (ss *SqlStore) GetInternalReplicaDB() *sql.DB {
}
rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.ReplicaXs))
- return ss.ReplicaXs[rrNum].Load().DB.DB
+ return ss.ReplicaXs[rrNum].DB.DB
}
func (ss *SqlStore) TotalMasterDbConnections() int {
@@ -605,10 +541,7 @@ func (ss *SqlStore) TotalReadDbConnections() int {
count := 0
for _, db := range ss.ReplicaXs {
- if !db.Load().Online() {
- continue
- }
- count = count + db.Load().Stats().OpenConnections
+ count = count + db.Stats().OpenConnections
}
return count
@@ -621,10 +554,7 @@ func (ss *SqlStore) TotalSearchDbConnections() int {
count := 0
for _, db := range ss.searchReplicaXs {
- if !db.Load().Online() {
- continue
- }
- count = count + db.Load().Stats().OpenConnections
+ count = count + db.Stats().OpenConnections
}
return count
@@ -852,14 +782,9 @@ func IsUniqueConstraintError(err error, indexName []string) bool {
}
func (ss *SqlStore) GetAllConns() []*sqlxDBWrapper {
- all := make([]*sqlxDBWrapper, 0, len(ss.ReplicaXs)+1)
- for i := range ss.ReplicaXs {
- if !ss.ReplicaXs[i].Load().Online() {
- continue
- }
- all = append(all, ss.ReplicaXs[i].Load())
- }
- all = append(all, ss.masterX)
+ all := make([]*sqlxDBWrapper, len(ss.ReplicaXs)+1)
+ copy(all, ss.ReplicaXs)
+ all[len(ss.ReplicaXs)] = ss.masterX
return all
}
@@ -882,24 +807,11 @@ func (ss *SqlStore) RecycleDBConnections(d time.Duration) {
func (ss *SqlStore) Close() {
ss.masterX.Close()
- // Closing monitor and waiting for it to be done.
- // This needs to be done before closing the replica handles.
- close(ss.quitMonitor)
- ss.wgMonitor.Wait()
-
for _, replica := range ss.ReplicaXs {
- if replica.Load().Online() {
- replica.Load().Close()
- }
+ replica.Close()
}
for _, replica := range ss.searchReplicaXs {
- if replica.Load().Online() {
- replica.Load().Close()
- }
- }
-
- for _, replica := range ss.replicaLagHandles {
replica.Close()
}
}
@@ -1220,10 +1132,7 @@ func (ss *SqlStore) migrate(direction migrationDirection) error {
if err != nil {
return err
}
- db, err2 := SetupConnection("master", dataSource, ss.settings, DBPingAttempts)
- if err2 != nil {
- return err2
- }
+ db := SetupConnection("master", dataSource, ss.settings)
driver, err = ms.WithInstance(db)
defer db.Close()
case model.DatabaseDriverPostgres:
diff --git a/server/channels/store/sqlstore/store_test.go b/server/channels/store/sqlstore/store_test.go
index 699ee53e98..c218fa205d 100644
--- a/server/channels/store/sqlstore/store_test.go
+++ b/server/channels/store/sqlstore/store_test.go
@@ -761,15 +761,13 @@ func TestReplicaLagQuery(t *testing.T) {
mockMetrics.On("RegisterDBCollector", mock.AnythingOfType("*sql.DB"), "master")
store := &SqlStore{
- rrCounter: 0,
- srCounter: 0,
- settings: settings,
- metrics: mockMetrics,
- quitMonitor: make(chan struct{}),
- wgMonitor: &sync.WaitGroup{},
+ rrCounter: 0,
+ srCounter: 0,
+ settings: settings,
+ metrics: mockMetrics,
}
- require.NoError(t, store.initConnection())
+ store.initConnection()
store.stores.post = newSqlPostStore(store, mockMetrics)
err = store.migrate(migrationsDirectionUp)
require.NoError(t, err)
@@ -841,11 +839,9 @@ func TestMySQLReadTimeout(t *testing.T) {
settings.DataSource = &dataSource
store := &SqlStore{
- settings: settings,
- quitMonitor: make(chan struct{}),
- wgMonitor: &sync.WaitGroup{},
+ settings: settings,
}
- require.NoError(t, store.initConnection())
+ store.initConnection()
defer store.Close()
_, err = store.GetMasterX().ExecNoTimeout(`SELECT SLEEP(3)`)
diff --git a/server/channels/store/sqlstore/thread_store.go b/server/channels/store/sqlstore/thread_store.go
index 66ce1f42a1..b731b0b71c 100644
--- a/server/channels/store/sqlstore/thread_store.go
+++ b/server/channels/store/sqlstore/thread_store.go
@@ -688,28 +688,6 @@ func (s *SqlThreadStore) UpdateMembership(membership *model.ThreadMembership) (*
return s.updateMembership(s.GetMasterX(), membership)
}
-func (s *SqlThreadStore) DeleteMembershipsForChannel(userID, channelID string) error {
- subQuery := s.getSubQueryBuilder().
- Select("1").
- From("Threads").
- Where(sq.And{
- sq.Expr("Threads.PostId = ThreadMemberships.PostId"),
- sq.Eq{"Threads.ChannelId": channelID},
- })
-
- query := s.getQueryBuilder().
- Delete("ThreadMemberships").
- Where(sq.Eq{"UserId": userID}).
- Where(sq.Expr("EXISTS (?)", subQuery))
-
- _, err := s.GetMasterX().ExecBuilder(query)
- if err != nil {
- return errors.Wrapf(err, "failed to remove thread memberships with userid=%s channelid=%s", userID, channelID)
- }
-
- return nil
-}
-
func (s *SqlThreadStore) updateMembership(ex sqlxExecutor, membership *model.ThreadMembership) (*model.ThreadMembership, error) {
query := s.getQueryBuilder().
Update("ThreadMemberships").
@@ -734,14 +712,7 @@ func (s *SqlThreadStore) GetMembershipsForUser(userId, teamId string) ([]*model.
memberships := []*model.ThreadMembership{}
query := s.getQueryBuilder().
- Select(
- "ThreadMemberships.PostId",
- "ThreadMemberships.UserId",
- "ThreadMemberships.Following",
- "ThreadMemberships.LastUpdated",
- "ThreadMemberships.LastViewed",
- "ThreadMemberships.UnreadMentions",
- ).
+ Select("ThreadMemberships.*").
Join("Threads ON Threads.PostId = ThreadMemberships.PostId").
From("ThreadMemberships").
Where(sq.Or{sq.Eq{"Threads.ThreadTeamId": teamId}, sq.Eq{"Threads.ThreadTeamId": ""}}).
@@ -761,14 +732,7 @@ func (s *SqlThreadStore) GetMembershipForUser(userId, postId string) (*model.Thr
func (s *SqlThreadStore) getMembershipForUser(ex sqlxExecutor, userId, postId string) (*model.ThreadMembership, error) {
var membership model.ThreadMembership
query := s.getQueryBuilder().
- Select(
- "PostId",
- "UserId",
- "Following",
- "LastUpdated",
- "LastViewed",
- "UnreadMentions",
- ).
+ Select("*").
From("ThreadMemberships").
Where(sq.And{
sq.Eq{"PostId": postId},
diff --git a/server/channels/store/store.go b/server/channels/store/store.go
index cd813239d4..7da24fd24c 100644
--- a/server/channels/store/store.go
+++ b/server/channels/store/store.go
@@ -72,7 +72,10 @@ type Store interface {
// GetInternalMasterDB allows access to the raw master DB
// handle for the multi-product architecture.
GetInternalMasterDB() *sql.DB
+ // GetInternalReplicaDBs allows access to the raw replica DB
+ // handles for the multi-product architecture.
GetInternalReplicaDB() *sql.DB
+ GetInternalReplicaDBs() []*sql.DB
TotalMasterDbConnections() int
TotalReadDbConnections() int
TotalSearchDbConnections() int
@@ -344,7 +347,6 @@ type ThreadStore interface {
PermanentDeleteBatchThreadMembershipsForRetentionPolicies(now, globalPolicyEndTime, limit int64, cursor model.RetentionPolicyCursor) (int64, model.RetentionPolicyCursor, error)
DeleteOrphanedRows(limit int) (deleted int64, err error)
GetThreadUnreadReplyCount(threadMembership *model.ThreadMembership) (int64, error)
- DeleteMembershipsForChannel(userID, channelID string) error
// Insights - threads
GetTopThreadsForTeamSince(teamID string, userID string, since int64, offset int, limit int) (*model.TopThreadList, error)
diff --git a/server/channels/store/storetest/channel_store_categories.go b/server/channels/store/storetest/channel_store_categories.go
index 6ba934f45b..ecd49ef8c2 100644
--- a/server/channels/store/storetest/channel_store_categories.go
+++ b/server/channels/store/storetest/channel_store_categories.go
@@ -672,38 +672,6 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) {
require.NoError(t, err)
assert.Equal(t, []string{}, res2.Channels)
})
-
- t.Run("should store the correct sorting value", func(t *testing.T) {
- userId := model.NewId()
-
- team := setupTeam(t, ss, userId)
-
- opts := &store.SidebarCategorySearchOpts{
- TeamID: team.Id,
- ExcludeTeam: false,
- }
- res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts)
- require.NoError(t, nErr)
- require.NotEmpty(t, res)
- // Create the category
- created, err := ss.Channel().CreateSidebarCategory(userId, team.Id, &model.SidebarCategoryWithChannels{
- SidebarCategory: model.SidebarCategory{
- DisplayName: model.NewId(),
- Sorting: model.SidebarCategorySortManual,
- },
- })
- require.NoError(t, err)
-
- // Confirm that sorting value is correct
- res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, team.Id)
- require.NoError(t, err)
- require.Len(t, res.Categories, 4)
- // first category will be favorites and second will be newly created
- assert.Equal(t, model.SidebarCategoryCustom, res.Categories[1].Type)
- assert.Equal(t, created.Id, res.Categories[1].Id)
- assert.Equal(t, model.SidebarCategorySortManual, res.Categories[1].Sorting)
- assert.Equal(t, model.SidebarCategorySortManual, created.Sorting)
- })
}
func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) {
diff --git a/server/channels/store/storetest/mocks/Store.go b/server/channels/store/storetest/mocks/Store.go
index bca15c95e0..bb06fb9005 100644
--- a/server/channels/store/storetest/mocks/Store.go
+++ b/server/channels/store/storetest/mocks/Store.go
@@ -346,6 +346,22 @@ func (_m *Store) GetInternalReplicaDB() *sql.DB {
return r0
}
+// GetInternalReplicaDBs provides a mock function with given fields:
+func (_m *Store) GetInternalReplicaDBs() []*sql.DB {
+ ret := _m.Called()
+
+ var r0 []*sql.DB
+ if rf, ok := ret.Get(0).(func() []*sql.DB); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]*sql.DB)
+ }
+ }
+
+ return r0
+}
+
// Group provides a mock function with given fields:
func (_m *Store) Group() store.GroupStore {
ret := _m.Called()
diff --git a/server/channels/store/storetest/mocks/ThreadStore.go b/server/channels/store/storetest/mocks/ThreadStore.go
index 661194a935..60b9211db2 100644
--- a/server/channels/store/storetest/mocks/ThreadStore.go
+++ b/server/channels/store/storetest/mocks/ThreadStore.go
@@ -29,20 +29,6 @@ func (_m *ThreadStore) DeleteMembershipForUser(userId string, postID string) err
return r0
}
-// DeleteMembershipsForChannel provides a mock function with given fields: userID, channelID
-func (_m *ThreadStore) DeleteMembershipsForChannel(userID string, channelID string) error {
- ret := _m.Called(userID, channelID)
-
- var r0 error
- if rf, ok := ret.Get(0).(func(string, string) error); ok {
- r0 = rf(userID, channelID)
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
// DeleteOrphanedRows provides a mock function with given fields: limit
func (_m *ThreadStore) DeleteOrphanedRows(limit int) (int64, error) {
ret := _m.Called(limit)
diff --git a/server/channels/store/storetest/settings.go b/server/channels/store/storetest/settings.go
index 0104b950bb..a1253f28bb 100644
--- a/server/channels/store/storetest/settings.go
+++ b/server/channels/store/storetest/settings.go
@@ -261,7 +261,6 @@ func MakeSqlSettings(driver string, withReplica bool) *model.SqlSettings {
}
log("Created temporary " + driver + " database " + dbName)
- settings.ReplicaMonitorIntervalSeconds = model.NewInt(5)
return settings
}
diff --git a/server/channels/store/storetest/thread_store.go b/server/channels/store/storetest/thread_store.go
index efbc74d3ac..4cd64c8f1e 100644
--- a/server/channels/store/storetest/thread_store.go
+++ b/server/channels/store/storetest/thread_store.go
@@ -29,7 +29,6 @@ func TestThreadStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("MarkAllAsReadByChannels", func(t *testing.T) { testMarkAllAsReadByChannels(t, ss) })
t.Run("GetTopThreads", func(t *testing.T) { testGetTopThreads(t, ss) })
t.Run("MarkAllAsReadByTeam", func(t *testing.T) { testMarkAllAsReadByTeam(t, ss) })
- t.Run("DeleteMembershipsForChannel", func(t *testing.T) { testDeleteMembershipsForChannel(t, ss) })
}
func testThreadStorePopulation(t *testing.T, ss store.Store) {
@@ -1915,121 +1914,3 @@ func testMarkAllAsReadByTeam(t *testing.T, ss store.Store) {
assertThreadReplyCount(t, userBID, team2.Id, 1, "expected 1 unread message in team2 for userB")
})
}
-
-func testDeleteMembershipsForChannel(t *testing.T, ss store.Store) {
- createThreadMembership := func(userID, postID string) (*model.ThreadMembership, func()) {
- t.Helper()
- opts := store.ThreadMembershipOpts{
- Following: true,
- IncrementMentions: false,
- UpdateFollowing: true,
- UpdateViewedTimestamp: false,
- UpdateParticipants: false,
- }
- mem, err := ss.Thread().MaintainMembership(userID, postID, opts)
- require.NoError(t, err)
-
- return mem, func() {
- err := ss.Thread().DeleteMembershipForUser(userID, postID)
- require.NoError(t, err)
- }
- }
-
- postingUserID := model.NewId()
- userAID := model.NewId()
- userBID := model.NewId()
-
- team, err := ss.Team().Save(&model.Team{
- DisplayName: "DisplayName",
- Name: "team" + model.NewId(),
- Email: MakeEmail(),
- Type: model.TeamOpen,
- })
- require.NoError(t, err)
-
- channel1, err := ss.Channel().Save(&model.Channel{
- TeamId: team.Id,
- DisplayName: "DisplayName",
- Name: "channel1" + model.NewId(),
- Type: model.ChannelTypeOpen,
- }, -1)
- require.NoError(t, err)
- channel2, err := ss.Channel().Save(&model.Channel{
- TeamId: team.Id,
- DisplayName: "DisplayName2",
- Name: "channel2" + model.NewId(),
- Type: model.ChannelTypeOpen,
- }, -1)
- require.NoError(t, err)
-
- rootPost1, err := ss.Post().Save(&model.Post{
- ChannelId: channel1.Id,
- UserId: postingUserID,
- Message: model.NewRandomString(10),
- })
- require.NoError(t, err)
-
- _, err = ss.Post().Save(&model.Post{
- ChannelId: channel1.Id,
- UserId: postingUserID,
- Message: model.NewRandomString(10),
- RootId: rootPost1.Id,
- })
- require.NoError(t, err)
-
- rootPost2, err := ss.Post().Save(&model.Post{
- ChannelId: channel2.Id,
- UserId: postingUserID,
- Message: model.NewRandomString(10),
- })
- require.NoError(t, err)
- _, err = ss.Post().Save(&model.Post{
- ChannelId: channel2.Id,
- UserId: postingUserID,
- Message: model.NewRandomString(10),
- RootId: rootPost2.Id,
- })
- require.NoError(t, err)
-
- t.Run("should return memberships for user", func(t *testing.T) {
- memA1, cleanupA1 := createThreadMembership(userAID, rootPost1.Id)
- defer cleanupA1()
- memA2, cleanupA2 := createThreadMembership(userAID, rootPost2.Id)
- defer cleanupA2()
-
- membershipsA, err := ss.Thread().GetMembershipsForUser(userAID, team.Id)
- require.NoError(t, err)
-
- require.Len(t, membershipsA, 2)
- require.ElementsMatch(t, []*model.ThreadMembership{memA1, memA2}, membershipsA)
- })
-
- t.Run("should delete memberships for user for channel", func(t *testing.T) {
- _, cleanupA1 := createThreadMembership(userAID, rootPost1.Id)
- defer cleanupA1()
- memA2, cleanupA2 := createThreadMembership(userAID, rootPost2.Id)
- defer cleanupA2()
-
- ss.Thread().DeleteMembershipsForChannel(userAID, channel1.Id)
- membershipsA, err := ss.Thread().GetMembershipsForUser(userAID, team.Id)
- require.NoError(t, err)
-
- require.Len(t, membershipsA, 1)
- require.ElementsMatch(t, []*model.ThreadMembership{memA2}, membershipsA)
- })
-
- t.Run("deleting memberships for channel for userA should not affect userB", func(t *testing.T) {
- _, cleanupA1 := createThreadMembership(userAID, rootPost1.Id)
- defer cleanupA1()
- _, cleanupA2 := createThreadMembership(userAID, rootPost2.Id)
- defer cleanupA2()
- memB1, cleanupB2 := createThreadMembership(userBID, rootPost1.Id)
- defer cleanupB2()
-
- membershipsB, err := ss.Thread().GetMembershipsForUser(userBID, team.Id)
- require.NoError(t, err)
-
- require.Len(t, membershipsB, 1)
- require.ElementsMatch(t, []*model.ThreadMembership{memB1}, membershipsB)
- })
-}
diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go
index 3dc9a94c19..b52293e013 100644
--- a/server/channels/store/timerlayer/timerlayer.go
+++ b/server/channels/store/timerlayer/timerlayer.go
@@ -9112,22 +9112,6 @@ func (s *TimerLayerThreadStore) DeleteMembershipForUser(userId string, postID st
return err
}
-func (s *TimerLayerThreadStore) DeleteMembershipsForChannel(userID string, channelID string) error {
- start := time.Now()
-
- err := s.ThreadStore.DeleteMembershipsForChannel(userID, channelID)
-
- elapsed := float64(time.Since(start)) / float64(time.Second)
- if s.Root.Metrics != nil {
- success := "false"
- if err == nil {
- success = "true"
- }
- s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.DeleteMembershipsForChannel", success, elapsed)
- }
- return err
-}
-
func (s *TimerLayerThreadStore) DeleteOrphanedRows(limit int) (int64, error) {
start := time.Now()
diff --git a/server/channels/testlib/helper.go b/server/channels/testlib/helper.go
index f6a1b22531..f74a562568 100644
--- a/server/channels/testlib/helper.go
+++ b/server/channels/testlib/helper.go
@@ -331,7 +331,7 @@ func (h *MainHelper) SetReplicationLagForTesting(seconds int) error {
func (h *MainHelper) execOnEachReplica(query string, args ...any) error {
for _, replica := range h.SQLStore.ReplicaXs {
- _, err := replica.Load().Exec(query, args...)
+ _, err := replica.Exec(query, args...)
if err != nil {
return err
}
diff --git a/server/channels/utils/license.go b/server/channels/utils/license.go
index 43f1f8a0ba..b937662f35 100644
--- a/server/channels/utils/license.go
+++ b/server/channels/utils/license.go
@@ -210,6 +210,7 @@ func GetSanitizedClientLicense(l map[string]string) map[string]string {
delete(sanitizedLicense, "StartsAt")
delete(sanitizedLicense, "ExpiresAt")
delete(sanitizedLicense, "SkuName")
+ delete(sanitizedLicense, "SkuShortName")
return sanitizedLicense
}
diff --git a/server/i18n/en.json b/server/i18n/en.json
index 40f626291f..598462a448 100644
--- a/server/i18n/en.json
+++ b/server/i18n/en.json
@@ -1777,10 +1777,6 @@
"id": "api.error_get_first_admin_visit_marketplace_status",
"translation": "Error trying to retrieve the first admin visit marketplace status from the store."
},
- {
- "id": "api.error_no_organization_name_provided_for_self_hosted_onboarding",
- "translation": "Error no organization name provided for self hosted onboarding."
- },
{
"id": "api.error_set_first_admin_complete_setup",
"translation": "Error trying to save first admin complete setup in the store."
diff --git a/server/model/config.go b/server/model/config.go
index af4341bdfa..f278c97cdf 100644
--- a/server/model/config.go
+++ b/server/model/config.go
@@ -1173,7 +1173,6 @@ type SqlSettings struct {
DisableDatabaseSearch *bool `access:"environment_database,write_restrictable,cloud_restrictable"`
MigrationsStatementTimeoutSeconds *int `access:"environment_database,write_restrictable,cloud_restrictable"`
ReplicaLagSettings []*ReplicaLagSettings `access:"environment_database,write_restrictable,cloud_restrictable"` // telemetry: none
- ReplicaMonitorIntervalSeconds *int `access:"environment_database,write_restrictable,cloud_restrictable"`
}
func (s *SqlSettings) SetDefaults(isUpdate bool) {
@@ -1238,10 +1237,6 @@ func (s *SqlSettings) SetDefaults(isUpdate bool) {
if s.ReplicaLagSettings == nil {
s.ReplicaLagSettings = []*ReplicaLagSettings{}
}
-
- if s.ReplicaMonitorIntervalSeconds == nil {
- s.ReplicaMonitorIntervalSeconds = NewInt(5)
- }
}
type LogSettings struct {
diff --git a/server/model/onboarding.go b/server/model/onboarding.go
index 0fe5e91ffa..797bea7c1d 100644
--- a/server/model/onboarding.go
+++ b/server/model/onboarding.go
@@ -10,7 +10,6 @@ import (
// CompleteOnboardingRequest describes parameters of the requested plugin.
type CompleteOnboardingRequest struct {
- Organization string `json:"organization"` // Organization is the name of the organization
InstallPlugins []string `json:"install_plugins"` // InstallPlugins is a list of plugins to be installed
}
diff --git a/server/model/system.go b/server/model/system.go
index 24b4fce9c9..fbc2aaa684 100644
--- a/server/model/system.go
+++ b/server/model/system.go
@@ -16,7 +16,6 @@ const (
SystemAsymmetricSigningKeyKey = "AsymmetricSigningKey"
SystemPostActionCookieSecretKey = "PostActionCookieSecret"
SystemInstallationDateKey = "InstallationDate"
- SystemOrganizationName = "OrganizationName"
SystemFirstServerRunTimestampKey = "FirstServerRunTimestamp"
SystemClusterEncryptionKey = "ClusterEncryptionKey"
SystemUpgradedFromTeId = "UpgradedFromTE"
diff --git a/server/platform/services/telemetry/telemetry.go b/server/platform/services/telemetry/telemetry.go
index 4fdbdf51ec..d4da4770bc 100644
--- a/server/platform/services/telemetry/telemetry.go
+++ b/server/platform/services/telemetry/telemetry.go
@@ -522,7 +522,6 @@ func (ts *TelemetryService) trackConfig() {
"query_timeout": *cfg.SqlSettings.QueryTimeout,
"disable_database_search": *cfg.SqlSettings.DisableDatabaseSearch,
"migrations_statement_timeout_seconds": *cfg.SqlSettings.MigrationsStatementTimeoutSeconds,
- "replica_monitor_interval_seconds": *cfg.SqlSettings.ReplicaMonitorIntervalSeconds,
})
ts.SendTelemetry(TrackConfigLog, map[string]any{
diff --git a/webapp/channels/src/actions/global_actions.tsx b/webapp/channels/src/actions/global_actions.tsx
index 6ac09280ab..cb22542fa4 100644
--- a/webapp/channels/src/actions/global_actions.tsx
+++ b/webapp/channels/src/actions/global_actions.tsx
@@ -14,7 +14,7 @@ import {Preferences} from 'mattermost-redux/constants';
import {getConfig, isPerformanceDebuggingEnabled} from 'mattermost-redux/selectors/entities/general';
import {getCurrentTeamId, getMyTeams, getTeam, getMyTeamMember, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import {getBool, isCollapsedThreadsEnabled, isGraphQLEnabled} from 'mattermost-redux/selectors/entities/preferences';
-import {getCurrentUser, getCurrentUserId, isFirstAdmin} from 'mattermost-redux/selectors/entities/users';
+import {getCurrentUser, getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getCurrentChannelStats, getCurrentChannelId, getMyChannelMember, getRedirectChannelNameForTeam, getChannelsNameMapInTeam, getAllDirectChannels, getChannelMessageCount} from 'mattermost-redux/selectors/entities/channels';
import {appsEnabled} from 'mattermost-redux/selectors/entities/apps';
import {ChannelTypes} from 'mattermost-redux/action_types';
@@ -367,19 +367,11 @@ export async function redirectUserToDefaultTeam() {
return;
}
- // if the user is the first admin
- const isUserFirstAdmin = isFirstAdmin(state);
-
const locale = getCurrentLocale(state);
const teamId = LocalStorageStore.getPreviousTeamId(user.id);
let myTeams = getMyTeams(state);
if (myTeams.length === 0) {
- if (isUserFirstAdmin) {
- getHistory().push('/preparing-workspace');
- return;
- }
-
getHistory().push('/select_team');
return;
}
diff --git a/webapp/channels/src/components/do_verify_email/do_verify_email.tsx b/webapp/channels/src/components/do_verify_email/do_verify_email.tsx
index 0e81ead2c7..7b03d9e7d5 100644
--- a/webapp/channels/src/components/do_verify_email/do_verify_email.tsx
+++ b/webapp/channels/src/components/do_verify_email/do_verify_email.tsx
@@ -6,6 +6,7 @@ import {useIntl} from 'react-intl';
import {useSelector, useDispatch} from 'react-redux';
import {useLocation, useHistory} from 'react-router-dom';
+import {redirectUserToDefaultTeam} from 'actions/global_actions';
import {trackEvent} from 'actions/telemetry_actions.jsx';
import LaptopAlertSVG from 'components/common/svg_images_components/laptop_alert_svg';
@@ -14,6 +15,7 @@ import LoadingScreen from 'components/loading_screen';
import {clearErrors, logError} from 'mattermost-redux/actions/errors';
import {verifyUserEmail, getMe} from 'mattermost-redux/actions/users';
+import {getUseCaseOnboarding} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {DispatchFunc} from 'mattermost-redux/types/actions';
@@ -38,6 +40,7 @@ const DoVerifyEmail = () => {
const token = params.get('token') ?? '';
const loggedIn = Boolean(useSelector(getCurrentUserId));
+ const useCaseOnboarding = useSelector(getUseCaseOnboarding);
const [verifyStatus, setVerifyStatus] = useState(VerifyStatus.PENDING);
const [serverError, setServerError] = useState('');
@@ -49,11 +52,16 @@ const DoVerifyEmail = () => {
const handleRedirect = () => {
if (loggedIn) {
- // need info about whether admin or not,
- // and whether admin has already completed
- // first time onboarding. Instead of fetching and orchestrating that here,
- // let the default root component handle it.
- history.push('/');
+ if (useCaseOnboarding) {
+ // need info about whether admin or not,
+ // and whether admin has already completed
+ // first time onboarding. Instead of fetching and orchestrating that here,
+ // let the default root component handle it.
+ history.push('/');
+ return;
+ }
+
+ redirectUserToDefaultTeam();
return;
}
diff --git a/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/index.ts b/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/index.ts
index 5a2ac01c35..a59ff532cc 100644
--- a/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/index.ts
+++ b/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/index.ts
@@ -8,6 +8,7 @@ import {withRouter} from 'react-router-dom';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {GenericAction} from 'mattermost-redux/types/actions';
import {getCurrentRelativeTeamUrl} from 'mattermost-redux/selectors/entities/teams';
+import {getUseCaseOnboarding} from 'mattermost-redux/selectors/entities/preferences';
import {isFirstAdmin} from 'mattermost-redux/selectors/entities/users';
import {getUserGuideDropdownPluginMenuItems} from 'selectors/plugins';
@@ -31,6 +32,7 @@ function mapStateToProps(state: GlobalState) {
teamUrl: getCurrentRelativeTeamUrl(state),
pluginMenuItems: getUserGuideDropdownPluginMenuItems(state),
isFirstAdmin: isFirstAdmin(state),
+ useCaseOnboarding: getUseCaseOnboarding(state),
};
}
diff --git a/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/user_guide_dropdown.test.tsx b/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/user_guide_dropdown.test.tsx
index aa1ac2e833..effe92c1ad 100644
--- a/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/user_guide_dropdown.test.tsx
+++ b/webapp/channels/src/components/global_header/center_controls/user_guide_dropdown/user_guide_dropdown.test.tsx
@@ -34,6 +34,7 @@ describe('components/channel_header/components/UserGuideDropdown', () => {
},
pluginMenuItems: [],
isFirstAdmin: false,
+ useCaseOnboarding: false,
};
test('should match snapshot', () => {
diff --git a/webapp/channels/src/components/login/login.test.tsx b/webapp/channels/src/components/login/login.test.tsx
index 512c98856f..b1116f4d2c 100644
--- a/webapp/channels/src/components/login/login.test.tsx
+++ b/webapp/channels/src/components/login/login.test.tsx
@@ -288,18 +288,4 @@ describe('components/login/Login', () => {
expect(externalLoginButton.props().label).toEqual('OpenID 2');
expect(externalLoginButton.props().style).toEqual({color: '#00ff00', borderColor: '#00ff00'});
});
-
- it('should redirect on login', () => {
- mockState.entities.users.currentUserId = 'user1';
- LocalStorageStore.setWasLoggedIn(true);
- mockConfig.EnableSignInWithEmail = 'true';
- const redirectPath = '/boards/team/teamID/boardID';
- mockLocation.search = '?redirect_to=' + redirectPath;
- mount(
-
- - Collaboration is tough by yourself. Invite a few team members using the invitation link below. - -
-