MM-61229: Place Redis behind enterprise (#28917)

And re-order the server initialization (AGAIN).

For the nth time, we found several bugs in the initialization
process.

1. filestore.NewExportFileBackend and filestore.NewFileBackend
depended on license, but the license wasn't even loaded until
later!
2. The `ps.sqlStore.UpdateLicense` call also didn't work
because the license wouldn't get loaded. It only accidentally
worked because of `ps.AddLicenseListener` which would update
the license later on. We remove that.

Ideally, we would have loaded the license first and then
checked for redis client, but it's very difficult to do that.
Reasons are explained in the code comment.

So we just wait until the license is loaded, and simply throw an
error later.

https://mattermost.atlassian.net/browse/MM-61229
```release-note
NONE
```
---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Agniva De Sarker
2024-11-15 13:27:23 +05:30
коммит произвёл GitHub
родитель 8933756aea
Коммит 8d1c42bc91
4 изменённых файлов: 40 добавлений и 14 удалений

Просмотреть файл

@@ -112,6 +112,7 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
*memoryConfig.CacheSettings.RedisAddress = redisHost + ":6379"
*memoryConfig.CacheSettings.DisableClientCache = true
*memoryConfig.CacheSettings.RedisDB = 0
options = append(options, app.ForceEnableRedis())
}
if updateConfig != nil {
updateConfig(memoryConfig)

Просмотреть файл

@@ -59,6 +59,13 @@ func SetFileStore(filestore filestore.FileBackend) Option {
}
}
func ForceEnableRedis() Option {
return func(s *Server) error {
s.platformOptions = append(s.platformOptions, platform.ForceEnableRedis())
return nil
}
}
func RunEssentialJobs(s *Server) error {
s.runEssentialJobs = true

Просмотреть файл

@@ -119,3 +119,10 @@ func SetCluster(cluster einterfaces.ClusterInterface) Option {
return nil
}
}
func ForceEnableRedis() Option {
return func(ps *PlatformService) error {
ps.forceEnableRedis = true
return nil
}
}

Просмотреть файл

@@ -103,6 +103,10 @@ type PlatformService struct {
sharedChannelService SharedChannelServiceIFace
pluginEnv HookRunner
// This is a test mode setting used to enable Redis
// without a license.
forceEnableRedis bool
}
type HookRunner interface {
@@ -257,8 +261,26 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
}
}
ps.Store, err = ps.newStore()
if err != nil {
return nil, fmt.Errorf("cannot create store: %w", err)
}
// Step 7: Init License
if model.BuildEnterpriseReady == "true" {
ps.LoadLicense()
}
license := ps.License()
// Step 3: Initialize filestore
// This is a hack because ideally we wouldn't even have started the Redis client
// if the license didn't have clustering. But there's an intricate deadlock
// where license cannot be loaded before store, and store cannot be loaded before
// cache. So loading license before loading cache is an uphill battle.
if (license == nil || !*license.Features.Cluster) && *cacheConfig.CacheType == model.CacheTypeRedis && !ps.forceEnableRedis {
return nil, fmt.Errorf("Redis cannot be used in an instance without a license or a license without clustering")
}
// Step 8: Initialize filestore
if ps.filestore == nil {
insecure := ps.Config().ServiceSettings.EnableInsecureOutgoingConnections
backend, err2 := filestore.NewFileBackend(filestore.NewFileBackendSettingsFromConfig(&ps.Config().FileSettings, license != nil && *license.Features.Compliance, insecure != nil && *insecure))
@@ -282,17 +304,11 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
}
}
ps.Store, err = ps.newStore()
if err != nil {
return nil, fmt.Errorf("cannot create store: %w", err)
}
// Note: we hardcode the session and status cache to LRU because they lead
// to a lot of SCAN calls in case of Redis. We could potentially have a
// reverse mapping to avoid the scan, but this needs more complicated code.
// Leaving this for now.
// Needed before loading license
ps.statusCache, err = cache.NewProvider().NewCache(&cache.CacheOptions{
Name: "Status",
Size: model.StatusCacheSize,
@@ -314,12 +330,7 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
return nil, fmt.Errorf("could not create session cache: %w", err)
}
// Step 7: Init License
if model.BuildEnterpriseReady == "true" {
ps.LoadLicense()
}
// Step 8: Init Metrics Server depends on step 6 (store) and 7 (license)
// Step 9: Init Metrics Server depends on step 6 (store) and 7 (license)
if ps.startMetrics {
if mErr := ps.resetMetrics(); mErr != nil {
return nil, mErr
@@ -334,7 +345,7 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
})
}
// Step 9: Init AsymmetricSigningKey depends on step 6 (store)
// Step 10: Init AsymmetricSigningKey depends on step 6 (store)
if err = ps.EnsureAsymmetricSigningKey(); err != nil {
return nil, fmt.Errorf("unable to ensure asymmetric signing key: %w", err)
}