diff --git a/server/channels/app/platform/config.go b/server/channels/app/platform/config.go index 09eb3ab57a..8f6a532032 100644 --- a/server/channels/app/platform/config.go +++ b/server/channels/app/platform/config.go @@ -326,12 +326,20 @@ func (ps *PlatformService) LimitedClientConfig() map[string]string { } func (ps *PlatformService) IsFirstUserAccount() bool { - count, err := ps.Store.User().Count(model.UserCountOptions{IncludeDeleted: true}) - if err != nil { - return false + if ps.fetchUserCountForFirstUserAccountCheck.Load() { + count, err := ps.Store.User().Count(model.UserCountOptions{IncludeDeleted: true}) + if err != nil { + return false + } + // Avoid calling the user count query in future if we get a count > 0 + if count > 0 { + ps.fetchUserCountForFirstUserAccountCheck.Store(false) + return false + } + return true } - return count <= 0 + return false } func (ps *PlatformService) MaxPostSize() int { diff --git a/server/channels/app/platform/config_test.go b/server/channels/app/platform/config_test.go index cc874e0d94..f982ef794d 100644 --- a/server/channels/app/platform/config_test.go +++ b/server/channels/app/platform/config_test.go @@ -109,37 +109,33 @@ func TestIsFirstUserAccount(t *testing.T) { storeMock.On("User").Return(userStoreMock) type test struct { - name string - count int64 - err error - result bool + name string + count int64 + err error + result bool + shouldCallStore bool } tests := []test{ - {"success no users", 0, nil, true}, - {"success one user", 1, nil, false}, - {"success multiple users", 42, nil, false}, - {"success negative users", -100, nil, true}, - {"failed request", 0, errors.New("error"), false}, - } - - for _, te := range tests { - t.Run(te.name, func(t *testing.T) { - *userStoreMock = smocks.UserStore{} - - userStoreMock.On("Count", model.UserCountOptions{IncludeDeleted: true}).Return(te.count, te.err) - require.Equal(t, te.result, th.Service.IsFirstUserAccount()) - }) + {"failed request", 0, errors.New("error"), false, true}, + {"success negative users", -100, nil, true, true}, + {"success no users", 0, nil, true, true}, + {"success one user", 1, nil, false, true}, + {"success multiple users - no store call", 42, nil, false, false}, } // create a session, this should not affect IsFirstUserAccount th.Service.sessionCache.Set("mock_session", 1) for _, te := range tests { - t.Run(te.name+" with session", func(t *testing.T) { + t.Run(te.name, func(t *testing.T) { *userStoreMock = smocks.UserStore{} - userStoreMock.On("Count", model.UserCountOptions{IncludeDeleted: true}).Return(te.count, te.err) + userStoreMock.On("Count", model.UserCountOptions{IncludeDeleted: true}).Return(te.count, te.err).RunFn = func(args mock.Arguments) { + if !te.shouldCallStore { + assert.Fail(t, "should not have called the store") + } + } require.Equal(t, te.result, th.Service.IsFirstUserAccount()) }) } diff --git a/server/channels/app/platform/service.go b/server/channels/app/platform/service.go index d235531a34..bfd6027a91 100644 --- a/server/channels/app/platform/service.go +++ b/server/channels/app/platform/service.go @@ -49,10 +49,11 @@ type PlatformService struct { sessionCache cache.Cache sessionPool sync.Pool - asymmetricSigningKey atomic.Value - clientConfig atomic.Value - clientConfigHash atomic.Value - limitedClientConfig atomic.Value + asymmetricSigningKey atomic.Value + clientConfig atomic.Value + clientConfigHash atomic.Value + limitedClientConfig atomic.Value + fetchUserCountForFirstUserAccountCheck atomic.Bool logger *mlog.Logger notificationsLogger *mlog.Logger @@ -126,6 +127,9 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) { additionalClusterHandlers: map[model.ClusterEvent]einterfaces.ClusterMessageHandler{}, } + // Assume the first user account has not been created yet. A call to the DB will later check if this is really the case. + ps.fetchUserCountForFirstUserAccountCheck.Store(true) + // Step 1: Cache provider. // At the moment we only have this implementation // in the future the cache provider will be built based on the loaded config