From 56bf1b695af91debe1b0b6901dcef67fd2a96f2d Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Fri, 14 Apr 2023 14:28:50 +0530 Subject: [PATCH] MM-49984: Displaying active search backend in system console (#22721) This will be shown in the system console to let users know which search backend is active. Implemented via adding an extra param in the ping response. https://mattermost.atlassian.net/browse/MM-49984 ```release-note The database section in the system console now has an additional read-only section which shows the active search backend in use. This can be helpful to confirm which is the currently active search engine when there are multiple of them configured. ``` --------- Co-authored-by: Mattermost Build --- server/channels/api4/system.go | 2 + server/channels/app/app_iface.go | 1 + .../app/opentracing/opentracing_layer.go | 17 +++++++ server/channels/app/searchengine.go | 4 ++ server/i18n/en.json | 8 ++-- .../services/searchengine/searchengine.go | 13 +++++- .../searchengine/searchengine_test.go | 42 +++++++++++++++++ .../database_settings.test.jsx.snap | 30 ++++++++++++ .../admin_console/database_settings.jsx | 46 ++++++++++++++++++- .../admin_console/database_settings.test.jsx | 6 +++ webapp/channels/src/i18n/en.json | 2 + webapp/platform/client/src/client4.ts | 1 + 12 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 server/platform/services/searchengine/searchengine_test.go diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index 216533b67f3..00143beeb85 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -190,6 +190,8 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) { s["CanReceiveNotifications"] = c.App.SendTestPushNotification(deviceID) } + s["ActiveSearchBackend"] = c.App.ActiveSearchBackend() + if s[model.STATUS] != model.StatusOk { w.WriteHeader(http.StatusInternalServerError) } diff --git a/server/channels/app/app_iface.go b/server/channels/app/app_iface.go index 640b508cf19..b402409be24 100644 --- a/server/channels/app/app_iface.go +++ b/server/channels/app/app_iface.go @@ -400,6 +400,7 @@ type AppIface interface { VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError AccountMigration() einterfaces.AccountMigrationInterface ActivateMfa(userID, token string) *model.AppError + ActiveSearchBackend() string AddChannelsToRetentionPolicy(policyID string, channelIDs []string) *model.AppError AddConfigListener(listener func(*model.Config, *model.Config)) string AddDirectChannels(c request.CTX, teamID string, user *model.User) *model.AppError diff --git a/server/channels/app/opentracing/opentracing_layer.go b/server/channels/app/opentracing/opentracing_layer.go index 73193c5bd97..48ff40a0ca3 100644 --- a/server/channels/app/opentracing/opentracing_layer.go +++ b/server/channels/app/opentracing/opentracing_layer.go @@ -89,6 +89,23 @@ func (a *OpenTracingAppLayer) ActivateMfa(userID string, token string) *model.Ap return resultVar0 } +func (a *OpenTracingAppLayer) ActiveSearchBackend() string { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ActiveSearchBackend") + + a.ctx = newCtx + a.app.Srv().Store().SetContext(newCtx) + defer func() { + a.app.Srv().Store().SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0 := a.app.ActiveSearchBackend() + + return resultVar0 +} + func (a *OpenTracingAppLayer) AddChannelMember(c request.CTX, userID string, channel *model.Channel, opts app.ChannelMemberOpts) (*model.ChannelMember, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AddChannelMember") diff --git a/server/channels/app/searchengine.go b/server/channels/app/searchengine.go index d441662a5fe..345edefb05a 100644 --- a/server/channels/app/searchengine.go +++ b/server/channels/app/searchengine.go @@ -60,3 +60,7 @@ func (a *App) PurgeBleveIndexes() *model.AppError { } return nil } + +func (a *App) ActiveSearchBackend() string { + return a.ch.srv.platform.SearchEngine.ActiveEngine() +} diff --git a/server/i18n/en.json b/server/i18n/en.json index 540a62bff50..5f8740a5484 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -7795,6 +7795,10 @@ "id": "ent.elasticsearch.indexer.index_batch.nothing_left_to_index.error", "translation": "Trying to index a new batch when all the entities are completed" }, + { + "id": "ent.elasticsearch.max_version.app_error", + "translation": "Elasticsearch version {{.Version}} is higher than max supported version of {{.MaxVersion}}" + }, { "id": "ent.elasticsearch.not_started.error", "translation": "Elasticsearch is not started" @@ -7863,10 +7867,6 @@ "id": "ent.elasticsearch.search_users.unmarshall_user_failed", "translation": "Failed to decode search results" }, - { - "id": "ent.elasticsearch.start.already_started.app_error", - "translation": "Elasticsearch is already started." - }, { "id": "ent.elasticsearch.start.create_bulk_processor_failed.app_error", "translation": "Failed to create Elasticsearch bulk processor." diff --git a/server/platform/services/searchengine/searchengine.go b/server/platform/services/searchengine/searchengine.go index 55948e98e88..6530b937236 100644 --- a/server/platform/services/searchengine/searchengine.go +++ b/server/platform/services/searchengine/searchengine.go @@ -45,8 +45,19 @@ func (seb *Broker) GetActiveEngines() []SearchEngineInterface { if seb.ElasticsearchEngine != nil && seb.ElasticsearchEngine.IsActive() { engines = append(engines, seb.ElasticsearchEngine) } - if seb.BleveEngine != nil && seb.BleveEngine.IsActive() { + if seb.BleveEngine != nil && seb.BleveEngine.IsActive() && seb.BleveEngine.IsIndexingEnabled() { engines = append(engines, seb.BleveEngine) } return engines } + +func (seb *Broker) ActiveEngine() string { + activeEngines := seb.GetActiveEngines() + if len(activeEngines) > 0 { + return activeEngines[0].GetName() + } + if *seb.cfg.SqlSettings.DisableDatabaseSearch { + return "none" + } + return "database" +} diff --git a/server/platform/services/searchengine/searchengine_test.go b/server/platform/services/searchengine/searchengine_test.go new file mode 100644 index 00000000000..00f806f3df9 --- /dev/null +++ b/server/platform/services/searchengine/searchengine_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package searchengine + +import ( + "testing" + + "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/server/platform/services/searchengine/mocks" + "github.com/stretchr/testify/assert" +) + +func TestActiveEngine(t *testing.T) { + cfg := &model.Config{} + cfg.SetDefaults() + + b := NewBroker(cfg) + + esMock := &mocks.SearchEngineInterface{} + esMock.On("IsActive").Return(true) + esMock.On("GetName").Return("elasticsearch") + + bleveMock := &mocks.SearchEngineInterface{} + bleveMock.On("IsActive").Return(true) + bleveMock.On("IsIndexingEnabled").Return(true) + bleveMock.On("GetName").Return("bleve") + + assert.Equal(t, "database", b.ActiveEngine()) + + b.ElasticsearchEngine = esMock + assert.Equal(t, "elasticsearch", b.ActiveEngine()) + + b.ElasticsearchEngine = nil + b.BleveEngine = bleveMock + assert.Equal(t, "bleve", b.ActiveEngine()) + + b.BleveEngine = nil + *b.cfg.SqlSettings.DisableDatabaseSearch = true + + assert.Equal(t, "none", b.ActiveEngine()) +} diff --git a/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.jsx.snap index 5685b6f279f..ded545ccf40 100644 --- a/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.jsx.snap +++ b/webapp/channels/src/components/admin_console/__snapshots__/database_settings.test.jsx.snap @@ -353,6 +353,36 @@ exports[`components/DatabaseSettings should match snapshot 1`] = ` +
+ +
+ +
+ +
+
+
{ // driverName and dataSource are read-only from the UI @@ -34,6 +43,17 @@ export default class DatabaseSettings extends AdminSettings { return config; }; + componentDidMount() { + this.getSearchBackend().then((searchBackend) => { + this.setState({searchBackend}); + }); + } + + async getSearchBackend() { + const res = await ping()(); + return res.ActiveSearchBackend; + } + getStateFromConfig(config) { return { driverName: config.SqlSettings.DriverName, @@ -368,6 +388,30 @@ export default class DatabaseSettings extends AdminSettings {
+
+ +
+ +
+ +
+
+
); }; diff --git a/webapp/channels/src/components/admin_console/database_settings.test.jsx b/webapp/channels/src/components/admin_console/database_settings.test.jsx index b6e98f8a6c3..936ee41d132 100644 --- a/webapp/channels/src/components/admin_console/database_settings.test.jsx +++ b/webapp/channels/src/components/admin_console/database_settings.test.jsx @@ -7,8 +7,14 @@ import {shallow} from 'enzyme'; import DatabaseSettings from 'components/admin_console/database_settings.jsx'; jest.mock('actions/admin_actions.jsx', () => { + const pingFn = () => { + return jest.fn(() => { + return {ActiveSearchBackend: 'none'}; + }); + }; return { recycleDatabaseConnection: jest.fn(), + ping: pingFn, }; }); diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index 9a248005fc3..a94109ae806 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -741,6 +741,8 @@ "admin.database.migrations_table.name": "Name", "admin.database.migrations_table.title": "Applied Schema Migrations", "admin.database.migrations_table.version": "Version", + "admin.database.search_backend.help_text": "Shows the currently active backend used for search. Values can be none, database, elasticsearch, bleve etc.", + "admin.database.search_backend.title": "Active Search Backend", "admin.database.title": "Database", "admin.developer.title": "Developer Settings", "admin.elasticsearch.bulkIndexingTitle": "Bulk Indexing:", diff --git a/webapp/platform/client/src/client4.ts b/webapp/platform/client/src/client4.ts index a58b1655bae..1f8638ab535 100644 --- a/webapp/platform/client/src/client4.ts +++ b/webapp/platform/client/src/client4.ts @@ -2439,6 +2439,7 @@ export default class Client4 { ping = () => { return this.doFetch<{ status: string; + ActiveSearchBackend: string; }>( `${this.getBaseRoute()}/system/ping?time=${Date.now()}`, {method: 'get'},