Skip to content

Commit

Permalink
Merge pull request #1081 from Tarunmeena0901/BB-760
Browse files Browse the repository at this point in the history
Fix BB-760 : When elasticsearch configuration is missing, server crashes
  • Loading branch information
MonkeyDo authored May 27, 2024
2 parents 704985d + 8d2a884 commit 5a8a0f8
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 71 deletions.
8 changes: 0 additions & 8 deletions config/config.json.ctmpl
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,13 @@
{{if service "bookbrainz-elasticsearch-test"}}
{{with index (service "bookbrainz-elasticsearch-test") 0}}
"node": "http://{{.Address}}:{{.Port}}",
"auth": {
"username": "elastic",
"password": "changeme"
},
"requestTimeout": 30000
{{end}}
{{end}}
{{ else }}
{{if service "bookbrainz-elasticsearch"}}
{{with index (service "bookbrainz-elasticsearch") 0}}
"node": "http://{{.Address}}:{{.Port}}",
"auth": {
"username": "elastic",
"password": "changeme"
},
"requestTimeout": 30000
{{end}}
{{end}}
Expand Down
4 changes: 0 additions & 4 deletions config/config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@
},
"search": {
"node": "http://elasticsearch:9200",
"auth": {
"username": "elastic",
"password": "changeme"
},
"requestTimeout": 30000
},
"mailConfig" :{
Expand Down
4 changes: 0 additions & 4 deletions config/config.local.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@
},
"search": {
"node": "http://localhost:9200",
"auth": {
"username": "elastic",
"password": "changeme"
},
"requestTimeout": 60000
}
}
1 change: 0 additions & 1 deletion src/api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ mainRouter.use((req, res) => {
// https://github.com/elastic/elasticsearch-js/issues/33
search.init(app.locals.orm, Object.assign({}, config.search));


const DEFAULT_API_PORT = 9098;
app.set('port', process.env.PORT || DEFAULT_API_PORT);

Expand Down
2 changes: 1 addition & 1 deletion src/client/components/pages/entities/cbReviewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ class CBReviewModal extends React.Component<

/* executes getAccessToken() only in a browser to avoid unnecessary server-side calls during component mounting */
componentDidMount = async () => {
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
this.accessToken = await this.getAccessToken();
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/client/entity-editor/entity-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ const EntityEditor = (props: Props) => {
window.onbeforeunload = handleUrlChange;
}, [handleUrlChange]);

if(entity){
entityURL = getEntityUrl(entity);
if (entity) {
entityURL = getEntityUrl(entity);
}

return (
<form onSubmit={onSubmit}>
<Card>
Expand Down
10 changes: 5 additions & 5 deletions src/client/entity-editor/identifier-editor/identifier-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ import {
} from '../validators/common';
import type {Dispatch} from 'redux';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import IdentifierLink from '../../components/pages/entities/identifiers-links.js';
import Select from 'react-select';
import ValueField from './value-field';
import {collapseWhiteSpaces} from '../../../common/helpers/utils';
import {connect} from 'react-redux';
import {faTimes} from '@fortawesome/free-solid-svg-icons';
import IdentifierLink from "../../components/pages/entities/identifiers-links.js"


type OwnProps = {
Expand Down Expand Up @@ -131,11 +131,11 @@ function IdentifierRow({
</Row>
{typeValue && valueValue && (
<Row>
<Col>
<Col>
Preview Link:
<IdentifierLink typeId={typeValue} value={valueValue}/>
</Col>
</Row>
<IdentifierLink typeId={typeValue} value={valueValue}/>
</Col>
</Row>
)}
<hr/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/client/helpers/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export function stringToHTMLWithLinks(content: string) {
cleanUrl = url.substring(0, firstUnbalancedParanthesis);
suffix = url.substring(firstUnbalancedParanthesis);
}
let link = `<a href="${cleanUrl.startsWith('www.') ? `https://${cleanUrl}` : cleanUrl}" target="_blank">${cleanUrl}</a>`;
const link = `<a href="${cleanUrl.startsWith('www.') ? `https://${cleanUrl}` : cleanUrl}" target="_blank">${cleanUrl}</a>`;
return link + suffix;
}
);
Expand Down
107 changes: 64 additions & 43 deletions src/common/helpers/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@

import * as commonUtils from './utils';
import {camelCase, isString, snakeCase, upperFirst} from 'lodash';

import ElasticSearch from '@elastic/elasticsearch';
import ElasticSearch, {ApiResponse, type Client, type ClientOptions} from '@elastic/elasticsearch';
import type {EntityTypeString} from 'bookbrainz-data/lib/types/entity';
import {type ORM} from 'bookbrainz-data';
import httpStatus from 'http-status';
import log from 'log';

Expand All @@ -33,7 +33,7 @@ const _bulkIndexSize = 10000;
const _retryDelay = 10;
const _maxJitter = 75;

let _client = null;
let _client:Client = null;

function sanitizeEntityType(type) {
if (!type) {
Expand Down Expand Up @@ -189,39 +189,45 @@ export async function _bulkIndexEntities(entities) {

operationSucceeded = true;

// eslint-disable-next-line no-await-in-loop
const response = await _client.bulk({
body: bulkOperations
}).catch(error => { log.error('error bulk indexing entities for search:', error); });

/*
* In case of failed index operations, the promise won't be rejected;
* instead, we have to inspect the response and respond to any failures
* individually.
*/
if (response?.errors === true) {
entitiesToIndex = response.items.reduce((accumulator, item) => {
// We currently only handle queue overrun
if (item.index.status === httpStatus.TOO_MANY_REQUESTS) {
const failedEntity = entities.find(
(element) => (element.bbid ?? element.id) === item.index._id
);

accumulator.push(failedEntity);
}
try {
// eslint-disable-next-line no-await-in-loop
const {body: bulkResponse} = await _client.bulk({
body: bulkOperations
});

/*
* In case of failed index operations, the promise won't be rejected;
* instead, we have to inspect the response and respond to any failures
* individually.
*/
if (bulkResponse?.errors === true) {
entitiesToIndex = bulkResponse.items.reduce((accumulator, item) => {
// We currently only handle queue overrun
if (item.index.status === httpStatus.TOO_MANY_REQUESTS) {
const failedEntity = entities.find(
(element) => (element.bbid ?? element.id) === item.index._id
);

accumulator.push(failedEntity);
}

return accumulator;
}, []);
return accumulator;
}, []);


if (entitiesToIndex.length) {
operationSucceeded = false;
if (entitiesToIndex.length) {
operationSucceeded = false;

const jitter = Math.random() * _maxJitter;
// eslint-disable-next-line no-await-in-loop
await new Promise(resolve => setTimeout(resolve, _retryDelay + jitter));
const jitter = Math.random() * _maxJitter;
// eslint-disable-next-line no-await-in-loop
await new Promise(resolve => setTimeout(resolve, _retryDelay + jitter));
}
}
}
catch (error) {
log.error('error bulk indexing entities for search:', error);
operationSucceeded = false;
}
}
}

Expand Down Expand Up @@ -587,22 +593,37 @@ export function searchByName(orm, name, type, size, from) {
return _searchForEntities(orm, dslQuery);
}

export async function init(orm, options) {
if (!isString(options.host)) {
options.host = 'localhost:9200';
/**
* Search init
* @description Sets up the search server connection with defaults,
* and returns a connection status boolean
* @param {ORM} orm the BookBrainz ORM
* @param {ClientOptions} [options] Optional (but recommended) connection settings, will provide defaults if missing
* @returns {Promise<boolean>} A Promise which resolves to the connection status boolean
*/
export async function init(orm: ORM, options:ClientOptions) {
if (!isString(options.node)) {
const defaultOptions:ClientOptions = {
node: 'http://localhost:9200',
requestTimeout: 60000
};
log.warning('ElasticSearch configuration not provided. Using default settings.');
_client = new ElasticSearch.Client(defaultOptions);
}
else {
_client = new ElasticSearch.Client(options);
}

_client = new ElasticSearch.Client(options);

// Automatically index on app startup if we haven't already
try {
const mainIndexExists = await _client.indices.exists({index: _index});
if (mainIndexExists) {
return null;
}
return generateIndex(orm);
await _client.ping();
}
catch (error) {
return null;
log.warning('Could not connect to ElasticSearch:', error.toString());
return false;
}
const mainIndexExists = await _client.indices.exists({index: _index});
if (!mainIndexExists) {
// Automatically index on app startup if we haven't already, but don't block app setup
generateIndex(orm).catch(log.error);
}
return true;
}
13 changes: 12 additions & 1 deletion src/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ if (config.influx) {

// Authentication code depends on session, so init session first
const authInitiated = auth.init(app);
let searchInitiated;

// Clone search config to prevent error if starting webserver and api
// https://github.com/elastic/elasticsearch-js/issues/33
search.init(app.locals.orm, Object.assign({}, config.search));
(async function initializeSearch() {
searchInitiated = await search.init(app.locals.orm, Object.assign({}, config.search));
})();

// Set up constants that will remain valid for the life of the app
debug(`Git revision: ${siteRevision}`);
Expand All @@ -124,6 +127,14 @@ app.use((req, res, next) => {
});
}

if (!searchInitiated) {
const msg = 'We could not connect to our search server, all search functionality is unavailable.';
res.locals.alerts.push({
level: 'danger',
message: `${msg}`
});
}

if (!req.session || !authInitiated) {
res.locals.alerts.push({
level: 'danger',
Expand Down

0 comments on commit 5a8a0f8

Please sign in to comment.