Просмотр исходного кода

Merge pull request #2130 from weseek/fix/handling-fulltextsearch-trouble

Fix/handling fulltextsearch trouble
Yuki Takei 6 лет назад
Родитель
Сommit
64a4f2c88c

+ 1 - 0
resource/locales/en-US/translation.json

@@ -626,6 +626,7 @@
     "connection_status_label_unconfigured": "UNCONFIGURED",
     "connection_status_label_connected": "CONNECTED",
     "connection_status_label_disconnected": "DISCONNECTED",
+    "connection_status_label_erroroccured": "ERROR OCCURED ON SEARCH SERVICE",
     "indices_status": "Indices Status",
     "indices_status_label_normalized": "NORMALIZED",
     "indices_status_label_unnormalized": "REBUILDING or BROKEN",

+ 1 - 0
resource/locales/ja/translation.json

@@ -615,6 +615,7 @@
     "connection_status_label_unconfigured": "設定されていません",
     "connection_status_label_connected": "接続されています",
     "connection_status_label_disconnected": "切断されています",
+    "connection_status_label_erroroccured": "SearchService でエラーが発生しています",
     "indices_status": "インデックスの状態",
     "indices_status_label_normalized": "正規化されています",
     "indices_status_label_unnormalized": "リビルド中 または 破損しています",

+ 25 - 10
src/client/js/components/Admin/ElasticsearchManagement/ElasticsearchManagement.jsx

@@ -18,8 +18,11 @@ class ElasticsearchManagement extends React.Component {
     super(props);
 
     this.state = {
-      isConfigured: null,
-      isConnected: null,
+      isInitialized: false,
+
+      isConnected: false,
+      isConfigured: false,
+      isReconnectingProcessing: false,
       isRebuildingProcessing: false,
       isRebuildingCompleted: false,
 
@@ -69,8 +72,8 @@ class ElasticsearchManagement extends React.Component {
       const { info } = await appContainer.apiv3Get('/search/indices');
 
       this.setState({
-        isConfigured: true,
         isConnected: true,
+        isConfigured: true,
 
         indicesData: info.indices,
         aliasesData: info.aliases,
@@ -89,21 +92,26 @@ class ElasticsearchManagement extends React.Component {
 
       toastError(errors);
     }
+    finally {
+      this.setState({ isInitialized: true });
+    }
   }
 
   async reconnect() {
     const { appContainer } = this.props;
 
+    this.setState({ isReconnectingProcessing: true });
+
     try {
       await appContainer.apiv3Post('/search/connection');
-      toastSuccess('Reconnecting to Elasticsearch has succeeded');
     }
     catch (e) {
       toastError(e);
       return;
     }
 
-    await this.retrieveIndicesStatus();
+    // reload
+    window.location.reload();
   }
 
   async normalizeIndices() {
@@ -138,19 +146,26 @@ class ElasticsearchManagement extends React.Component {
   }
 
   render() {
-    const { t } = this.props;
+    const { t, appContainer } = this.props;
     const {
-      isConfigured, isConnected, isRebuildingProcessing, isRebuildingCompleted,
+      isInitialized,
+      isConnected, isConfigured, isReconnectingProcessing, isRebuildingProcessing, isRebuildingCompleted,
       isNormalized, indicesData, aliasesData,
     } = this.state;
 
+    const isErrorOccuredOnSearchService = !appContainer.config.isSearchServiceReachable;
+
+    const isReconnectBtnEnabled = !isReconnectingProcessing && (!isInitialized || !isConnected || isErrorOccuredOnSearchService);
+
     return (
       <>
         <div className="row">
           <div className="col-xs-12">
             <StatusTable
-              isConfigured={isConfigured}
+              isInitialized={isInitialized}
+              isErrorOccuredOnSearchService={isErrorOccuredOnSearchService}
               isConnected={isConnected}
+              isConfigured={isConfigured}
               isNormalized={isNormalized}
               indicesData={indicesData}
               aliasesData={aliasesData}
@@ -165,8 +180,8 @@ class ElasticsearchManagement extends React.Component {
           <label className="col-xs-3 control-label">{ t('full_text_search_management.reconnect') }</label>
           <div className="col-xs-6">
             <ReconnectControls
-              isConfigured={isConfigured}
-              isConnected={isConnected}
+              isEnabled={isReconnectBtnEnabled}
+              isProcessing={isReconnectingProcessing}
               onReconnectingRequested={this.reconnect}
             />
           </div>

+ 4 - 5
src/client/js/components/Admin/ElasticsearchManagement/ReconnectControls.jsx

@@ -7,9 +7,7 @@ import { createSubscribedElement } from '../../UnstatedUtils';
 class ReconnectControls extends React.PureComponent {
 
   render() {
-    const { t, isConfigured, isConnected } = this.props;
-
-    const isEnabled = (isConfigured == null || isConfigured === true) && isConnected === false;
+    const { t, isEnabled, isProcessing } = this.props;
 
     return (
       <>
@@ -19,6 +17,7 @@ class ReconnectControls extends React.PureComponent {
           onClick={() => { this.props.onReconnectingRequested() }}
           disabled={!isEnabled}
         >
+          { isProcessing && <i className="fa fa-spinner fa-pulse mr-2"></i> }
           { t('full_text_search_management.reconnect_button') }
         </button>
 
@@ -41,8 +40,8 @@ const ReconnectControlsWrapper = (props) => {
 ReconnectControls.propTypes = {
   t: PropTypes.func.isRequired, // i18next
 
-  isConfigured: PropTypes.bool,
-  isConnected: PropTypes.bool,
+  isEnabled: PropTypes.bool,
+  isProcessing: PropTypes.bool,
   onReconnectingRequested: PropTypes.func.isRequired,
 };
 

+ 54 - 23
src/client/js/components/Admin/ElasticsearchManagement/StatusTable.jsx

@@ -6,6 +6,46 @@ import { createSubscribedElement } from '../../UnstatedUtils';
 
 class StatusTable extends React.PureComponent {
 
+  renderPreInitializedLabel() {
+    return <span className="label label-default">――</span>;
+  }
+
+  renderConnectionStatusLabels() {
+    const { t } = this.props;
+    const {
+      isErrorOccuredOnSearchService,
+      isConnected, isConfigured,
+    } = this.props;
+
+    const errorOccuredLabel = isErrorOccuredOnSearchService
+      ? <span className="label label-danger ml-2">{ t('full_text_search_management.connection_status_label_erroroccured') }</span>
+      : null;
+
+    let connectionStatusLabel = null;
+    if (!isConfigured) {
+      connectionStatusLabel = <span className="label label-default">{ t('full_text_search_management.connection_status_label_unconfigured') }</span>;
+    }
+    else {
+      connectionStatusLabel = isConnected
+        ? <span className="label label-success">{ t('full_text_search_management.connection_status_label_connected') }</span>
+        : <span className="label label-danger">{ t('full_text_search_management.connection_status_label_disconnected') }</span>;
+    }
+
+    return (
+      <>
+        {connectionStatusLabel}{errorOccuredLabel}
+      </>
+    );
+  }
+
+  renderIndicesStatusLabel() {
+    const { t, isNormalized } = this.props;
+
+    return isNormalized
+      ? <span className="label label-info">{ t('full_text_search_management.indices_status_label_normalized') }</span>
+      : <span className="label label-warning">{ t('full_text_search_management.indices_status_label_unnormalized') }</span>;
+  }
+
   renderIndexInfoPanel(indexName, body = {}, aliases = []) {
     const collapseId = `collapse-${indexName}`;
 
@@ -95,41 +135,29 @@ class StatusTable extends React.PureComponent {
 
   render() {
     const { t } = this.props;
-    const { isConfigured, isConnected, isNormalized } = this.props;
-
-
-    let connectionStatusLabel = <span className="label label-default">――</span>;
-    if (isConfigured != null && !isConfigured) {
-      connectionStatusLabel = <span className="label label-default">{ t('full_text_search_management.connection_status_label_unconfigured') }</span>;
-    }
-    else if (isConnected != null) {
-      connectionStatusLabel = isConnected
-        ? <span className="label label-success">{ t('full_text_search_management.connection_status_label_connected') }</span>
-        : <span className="label label-danger">{ t('full_text_search_management.connection_status_label_disconnected') }</span>;
-    }
-
-    let indicesStatusLabel = <span className="label label-default">――</span>;
-    if (isNormalized != null) {
-      indicesStatusLabel = isNormalized
-        ? <span className="label label-info">{ t('full_text_search_management.indices_status_label_normalized') }</span>
-        : <span className="label label-warning">{ t('full_text_search_management.indices_status_label_unnormalized') }</span>;
-    }
+    const {
+      isInitialized,
+    } = this.props;
 
     return (
       <table className="table table-bordered">
         <tbody>
           <tr>
             <th>{ t('full_text_search_management.connection_status') }</th>
-            <td>{connectionStatusLabel}</td>
+            <td>
+              { isInitialized ? this.renderConnectionStatusLabels() : this.renderPreInitializedLabel() }
+            </td>
           </tr>
           <tr>
             <th>{ t('full_text_search_management.indices_status') }</th>
-            <td>{indicesStatusLabel}</td>
+            <td>
+              { isInitialized ? this.renderIndicesStatusLabel() : this.renderPreInitializedLabel() }
+            </td>
           </tr>
           <tr>
             <th className="col-sm-4">{ t('full_text_search_management.indices_summary') }</th>
             <td className="p-4">
-              { this.renderIndexInfoPanels() }
+              { isInitialized && this.renderIndexInfoPanels() }
             </td>
           </tr>
         </tbody>
@@ -149,8 +177,11 @@ const StatusTableWrapper = (props) => {
 StatusTable.propTypes = {
   t: PropTypes.func.isRequired, // i18next
 
-  isConfigured: PropTypes.bool,
+  isInitialized: PropTypes.bool,
+  isErrorOccuredOnSearchService: PropTypes.bool,
+
   isConnected: PropTypes.bool,
+  isConfigured: PropTypes.bool,
   isNormalized: PropTypes.bool,
   indicesData: PropTypes.object,
   aliasesData: PropTypes.object,

+ 16 - 6
src/server/service/search.js

@@ -7,7 +7,8 @@ class SearchService {
     this.crowi = crowi;
     this.configManager = crowi.configManager;
 
-    this.isErrorOccured = null;
+    this.isErrorOccuredOnGettingInfo = null;
+    this.isErrorOccuredOnSearching = null;
 
     try {
       this.delegator = this.initDelegator();
@@ -27,7 +28,7 @@ class SearchService {
   }
 
   get isReachable() {
-    return this.isConfigured && !this.isErrorOccured;
+    return this.isConfigured && !this.isErrorOccuredOnGettingInfo && !this.isErrorOccuredOnSearching;
   }
 
   get isSearchboxEnabled() {
@@ -72,18 +73,25 @@ class SearchService {
 
   async initClient() {
     // reset error flag
-    this.isErrorOccured = false;
+    this.isErrorOccuredOnGettingInfo = false;
+    this.isErrorOccuredOnSearching = false;
 
     return this.delegator.initClient();
   }
 
   async getInfo() {
+
     try {
-      return await this.delegator.getInfo();
+      const result = await this.delegator.getInfo();
+
+      this.isErrorOccuredOnGettingInfo = false;
+      return result;
     }
     catch (err) {
+      logger.error(err);
+
       // switch error flag, `isReachable` to be `false`
-      this.isErrorOccured = true;
+      this.isErrorOccuredOnGettingInfo = true;
       throw err;
     }
   }
@@ -105,8 +113,10 @@ class SearchService {
       return await this.delegator.searchKeyword(keyword, user, userGroups, searchOpts);
     }
     catch (err) {
+      logger.error(err);
+
       // switch error flag, `isReachable` to be `false`
-      this.isErrorOccured = true;
+      this.isErrorOccuredOnSearching = true;
       throw err;
     }
   }