All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.polyglotted.eswrapper.services.IndexerWrapper Maven / Gradle / Ivy

package io.polyglotted.eswrapper.services;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.polyglotted.eswrapper.indexing.IgnoreErrors;
import io.polyglotted.eswrapper.indexing.IndexKey;
import io.polyglotted.eswrapper.indexing.Indexable;
import io.polyglotted.eswrapper.query.response.SimpleDoc;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.engine.DocumentAlreadyExistsException;

import java.util.List;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.uniqueIndex;
import static io.polyglotted.eswrapper.indexing.IgnoreErrors.lenient;
import static io.polyglotted.eswrapper.indexing.IgnoreErrors.strict;
import static io.polyglotted.eswrapper.query.request.QueryBuilder.idRequest;
import static io.polyglotted.eswrapper.query.response.ResultBuilder.SimpleDocBuilder;
import static org.elasticsearch.client.Requests.refreshRequest;

@Slf4j
@RequiredArgsConstructor
public final class IndexerWrapper {
    private final Client client;

    public void index(BulkRequest bulkRequest) {
        index(bulkRequest, strict());
    }

    public void twoPhaseCommit(Indexable indexable) {
        lockTheIndexOrFail(indexable.index);
        List currentDocs = getCurrent(indexable.index, indexable.updateIds());
        try {
            index(indexable.updateRequest(uniqueIndex(currentDocs, SimpleDoc::key)));
            index(indexable.writeRequest());

        } catch (RuntimeException ex) {
            log.error("failed two phase commit", ex);
            deleteUpdatesInHistory(indexable.index, indexable.updateKeys());
            forceReindex(currentDocs);
            throw ex;

        } finally {
            unlockIndex(indexable.index);
            client.admin().indices().refresh(refreshRequest(indexable.index)).actionGet();
        }
    }

    @VisibleForTesting
    List getCurrent(String currentIndex, String... ids) {
        return SimpleDocBuilder.buildFrom(client.search(idRequest(ids, ImmutableList.of(), currentIndex)).actionGet());
    }

    @VisibleForTesting
    void forceReindex(List currentDocs) {
        index(new BulkRequest().refresh(false).add(transform(currentDocs, SimpleDoc::forcedRequest)), lenient());
    }

    @VisibleForTesting
    void deleteUpdatesInHistory(String historyIndex, Iterable items) {
        index(new BulkRequest().refresh(false).add(transform(items,
           indexKey -> new DeleteRequest(historyIndex, indexKey.type, indexKey.id))), lenient());
    }

    @VisibleForTesting
    void lockTheIndexOrFail(String index) {
        try {
            IndexResponse response = client.index(new IndexRequest(index, "$lock", "global")
               .create(true).source("_val", true)).actionGet();
            checkState(response.isCreated(), "unable to lock the index " + index);
        } catch (DocumentAlreadyExistsException dex) {
            throw new IllegalStateException("unable to lock the index " + index);
        }
    }

    @VisibleForTesting
    void unlockIndex(String index) {
        client.delete(new DeleteRequest(index, "$lock", "global")).actionGet();
    }

    public void index(BulkRequest bulkRequest, IgnoreErrors ignoreErrors) {
        if (bulkRequest.numberOfActions() <= 0) return;
        BulkResponse responses = client.bulk(bulkRequest).actionGet();
        checkResponse(responses, ignoreErrors);
    }

    private static void checkResponse(BulkResponse responses, IgnoreErrors ignore) {
        if (!responses.hasFailures()) return;

        ImmutableMap.Builder errorBuilder = ImmutableMap.builder();
        for (BulkItemResponse response : responses) {
            String failureMessage = response.getFailureMessage();
            if (!ignore.ignoreFailure(failureMessage)) {
                errorBuilder.put(IndexKey.from(response), failureMessage);
            }
        }
        ImmutableMap errors = errorBuilder.build();
        if (errors.size() > 0) throw new IndexerException(errors);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy