Update elastic engine to preserve pre-existing indices if indexer receives 0 documents
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
+7
-4
@@ -252,6 +252,7 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
|
||||
async getIndexer(type: string) {
|
||||
const alias = this.constructSearchAlias(type);
|
||||
const indexerLogger = this.logger.child({ documentType: type });
|
||||
|
||||
const indexer = new ElasticSearchSearchEngineIndexer({
|
||||
type,
|
||||
@@ -259,13 +260,13 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
indexSeparator: this.indexSeparator,
|
||||
alias,
|
||||
elasticSearchClientWrapper: this.elasticSearchClientWrapper,
|
||||
logger: this.logger,
|
||||
logger: indexerLogger,
|
||||
batchSize: this.batchSize,
|
||||
});
|
||||
|
||||
// Attempt cleanup upon failure.
|
||||
indexer.on('error', async e => {
|
||||
this.logger.error(`Failed to index documents for type ${type}`, e);
|
||||
indexerLogger.error(`Failed to index documents for type ${type}`, e);
|
||||
let cleanupError: Error | undefined;
|
||||
|
||||
// In some cases, a failure may have occurred before the indexer was able
|
||||
@@ -296,11 +297,13 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
});
|
||||
|
||||
if (cleanupError) {
|
||||
this.logger.error(
|
||||
indexerLogger.error(
|
||||
`Unable to clean up elastic index ${indexer.indexName}: ${cleanupError}`,
|
||||
);
|
||||
} else {
|
||||
this.logger.info(`Removed partial, failed index ${indexer.indexName}`);
|
||||
indexerLogger.info(
|
||||
`Removed partial, failed index ${indexer.indexName}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+20
@@ -176,6 +176,26 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
expect(deleteSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles when no documents are received', async () => {
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments([]).execute();
|
||||
|
||||
// Older indices should have been queried for.
|
||||
expect(catSpy).toHaveBeenCalled();
|
||||
|
||||
// A new index should have been created.
|
||||
const createdIndex = createSpy.mock.calls[0][0].path.slice(1);
|
||||
expect(createdIndex).toContain('some-type-index__');
|
||||
|
||||
// No documents should have been sent
|
||||
expect(bulkSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Alias should not have been rotated.
|
||||
expect(aliasesSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Old index should not be cleaned up.
|
||||
expect(deleteSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles bulk and batching during indexing', async () => {
|
||||
const documents = range(550).map(i => ({
|
||||
title: `Hello World ${i}`,
|
||||
|
||||
+18
@@ -131,6 +131,24 @@ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer {
|
||||
// Wait for the bulk helper to finish processing.
|
||||
const result = await this.bulkResult;
|
||||
|
||||
// Warn that no documents were indexed, early return so that alias swapping
|
||||
// does not occur, and clean up the empty index we just created.
|
||||
if (this.processed === 0) {
|
||||
this.logger.warn(
|
||||
`Index for ${this.type} was not ${
|
||||
this.removableIndices.length ? 'replaced' : 'created'
|
||||
}: indexer received 0 documents`,
|
||||
);
|
||||
try {
|
||||
await this.elasticSearchClientWrapper.deleteIndex({
|
||||
index: this.indexName,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`Unable to clean up elastic index: ${error}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Rotate main alias upon completion. Apply permanent secondary alias so
|
||||
// stale indices can be referenced for deletion in case initial attempt
|
||||
// fails. Allow errors to bubble up so that we can clean up the created index.
|
||||
|
||||
Reference in New Issue
Block a user