/*
* Copyright (C) 2014 Jörg Prante
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xbib.elasticsearch.action.knapsack.pull;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollAction;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.node.service.NodeService;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.joda.time.DateTime;
import org.xbib.elasticsearch.helper.client.BulkNodeClient;
import org.xbib.elasticsearch.helper.client.BulkTransportClient;
import org.xbib.elasticsearch.helper.client.ClientBuilder;
import org.xbib.elasticsearch.knapsack.KnapsackService;
import org.xbib.elasticsearch.knapsack.KnapsackState;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.elasticsearch.client.Requests.createIndexRequest;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.clientSettings;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.getAliases;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.getMapping;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.getSettings;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.mapIndex;
import static org.xbib.elasticsearch.knapsack.KnapsackHelper.mapType;
public class TransportKnapsackPullAction extends TransportAction<KnapsackPullRequest, KnapsackPullResponse> {
private final static ESLogger logger = ESLoggerFactory.getLogger(KnapsackPullAction.class.getSimpleName());
private final Client client;
private final NodeService nodeService;
private final KnapsackService knapsack;
@Inject
public TransportKnapsackPullAction(Settings settings, ThreadPool threadPool,
Client client, NodeService nodeService, ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver,
TransportService transportService,
KnapsackService knapsack) {
super(settings, KnapsackPullAction.NAME, threadPool, actionFilters, indexNameExpressionResolver, transportService.getTaskManager());
this.client = client;
this.nodeService = nodeService;
this.knapsack = knapsack;
}
@Override
protected void doExecute(final KnapsackPullRequest request, ActionListener<KnapsackPullResponse> listener) {
final KnapsackState state = new KnapsackState()
.setMode("pull")
.setNodeName(nodeService.nodeName());
final KnapsackPullResponse response = new KnapsackPullResponse()
.setState(state);
try {
final BulkTransportClient transportClient = ClientBuilder.builder()
.put(ClientBuilder.MAX_ACTIONS_PER_REQUEST, request.getMaxActionsPerBulkRequest())
.put(ClientBuilder.MAX_CONCURRENT_REQUESTS, request.getMaxBulkConcurrency())
.put(ClientBuilder.FLUSH_INTERVAL, TimeValue.timeValueSeconds(5))
.put(clientSettings(client, request))
.toBulkTransportClient();
final BulkNodeClient nodeClient = ClientBuilder.builder()
.put(ClientBuilder.MAX_ACTIONS_PER_REQUEST, request.getMaxActionsPerBulkRequest())
.put(ClientBuilder.MAX_CONCURRENT_REQUESTS, request.getMaxBulkConcurrency())
.put(ClientBuilder.FLUSH_INTERVAL, TimeValue.timeValueSeconds(5))
.toBulkNodeClient(client);
state.setTimestamp(new DateTime());
response.setRunning(true);
knapsack.submit(new Thread() {
public void run() {
performPull(request, state, transportClient, nodeClient);
}
});
listener.onResponse(response);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
listener.onFailure(e);
}
}
/**
* Pull action thread
*
* @param request request
* @param state state
* @param transportClient bulk client for remote cluster access
* @param nodeClient bulk client for local cluster access
*/
final void performPull(final KnapsackPullRequest request,
final KnapsackState state,
final BulkTransportClient transportClient,
final BulkNodeClient nodeClient) {
try {
logger.info("start of pull: {}", state);
long count = 0L;
Map<String, Set<String>> indices = new HashMap<>();
for (String s : Strings.commaDelimitedListToSet(request.getIndex())) {
indices.put(s, Strings.commaDelimitedListToSet(request.getType()));
}
if (request.withMetadata()) {
// renaming indices/types
if (request.getIndexTypeNames() != null) {
for (Object spec : request.getIndexTypeNames().keySet()) {
if (spec == null) {
continue;
}
String[] s = spec.toString().split("/");
String index = s[0];
String type = s.length > 1 ? s[1] : null;
if (!"_all".equals(index)) {
Set<String> types = indices.get(index);
if (types == null) {
types = new HashSet<>();
}
if (type != null) {
types.add(type);
}
indices.put(index, types);
}
}
}
// get settings for all indices
logger.info("getting settings for indices {}", indices.keySet());
Set<String> settingsIndices = new HashSet<>(indices.keySet());
settingsIndices.remove("_all");
Map<String, String> settings = getSettings(transportClient.client(),
settingsIndices.toArray(new String[settingsIndices.size()]));
logger.info("found indices: {}", settings.keySet());
// we resolved the specs in indices to the real indices in the settings
// get mapping and alias per index and create index if copy mode is enabled
for (String index : settings.keySet()) {
CreateIndexRequest createIndexRequest = createIndexRequest(mapIndex(request, index));
Set<String> types = indices.get(index);
createIndexRequest.settings(settings.get(index));
logger.info("getting mappings for index {} and types {}", index, types);
Map<String, String> mappings = getMapping(transportClient.client(),
index, types != null ? new HashSet<>(types) : null);
logger.info("found mappings: {}", mappings.keySet());
for (String type : mappings.keySet()) {
logger.info("adding mapping: {}", mapType(request, index, type));
createIndexRequest.mapping(mapType(request, index, type), mappings.get(type));
}
// create index
logger.info("creating index: {}", mapIndex(request, index));
nodeClient.client().execute(CreateIndexAction.INSTANCE, createIndexRequest).actionGet();
logger.info("index created: {}", mapIndex(request, index));
logger.info("getting aliases for index {}", index);
Map<String, String> aliases = getAliases(client, index);
logger.info("found {} aliases", aliases.size());
if (!aliases.isEmpty()) {
IndicesAliasesRequestBuilder requestBuilder = new IndicesAliasesRequestBuilder(nodeClient.client(), IndicesAliasesAction.INSTANCE);
for (String alias : aliases.keySet()) {
if (aliases.get(alias).isEmpty()) {
requestBuilder.addAlias(index, alias);
} else {
requestBuilder.addAlias(index, alias, aliases.get(alias)); // with filter
}
}
requestBuilder.execute().actionGet();
logger.info("aliases created", aliases.size());
}
}
}
SearchRequest searchRequest = request.getSearchRequest();
if (searchRequest == null) {
searchRequest = new SearchRequestBuilder(transportClient.client(), SearchAction.INSTANCE)
.setQuery(QueryBuilders.matchAllQuery()).addSort(SortBuilders.fieldSort("_doc")).request();
}
long total = 0L;
for (String index : indices.keySet()) {
if (!"_all".equals(index)) {
searchRequest.indices(index);
}
Set<String> types = indices.get(index);
if (types != null) {
searchRequest.types(types.toArray(new String[types.size()]));
}
searchRequest.scroll(request.getTimeout());
SearchResponse searchResponse = transportClient.client()
.execute(SearchAction.INSTANCE, searchRequest).actionGet();
do {
total += searchResponse.getHits().getHits().length;
logger.debug("total={} hits={} took={}", total,
searchResponse.getHits().getHits().length,
searchResponse.getTookInMillis());
for (SearchHit hit : searchResponse.getHits()) {
indexSearchHit(nodeClient, request, hit);
count++;
}
searchResponse = transportClient.client().execute(SearchScrollAction.INSTANCE,
new SearchScrollRequest(searchResponse.getScrollId()).scroll(request.getTimeout())).actionGet();
} while (searchResponse.getHits().getHits().length > 0 && !Thread.interrupted());
}
nodeClient.flushIngest();
nodeClient.waitForResponses(TimeValue.timeValueSeconds(60));
for (String index : indices.keySet()) {
nodeClient.refreshIndex(index);
}
nodeClient.shutdown();
transportClient.shutdown();
logger.info("end of pull: {}, docs = {}, count = {}", state, total, count);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
try {
knapsack.removeImport(state);
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
private void indexSearchHit(BulkNodeClient bulkNodeClient, KnapsackPullRequest request, SearchHit hit)
throws IOException {
IndexRequest indexRequest = new IndexRequest(mapIndex(request, hit.getIndex()),
mapType(request, hit.getIndex(), hit.getType()), hit.getId());
for (String f : hit.getFields().keySet()) {
switch (f) {
case "_parent":
indexRequest.parent(hit.getFields().get(f).getValue().toString());
break;
case "_routing":
indexRequest.routing(hit.getFields().get(f).getValue().toString());
break;
case "_timestamp":
indexRequest.timestamp(hit.getFields().get(f).getValue().toString());
break;
case "_version":
indexRequest.versionType(VersionType.EXTERNAL)
.version(Long.parseLong(hit.getFields().get(f).getValue().toString()));
break;
case "_source":
indexRequest.source(hit.getSourceAsString());
break;
default:
indexRequest.source(f, hit.getFields().get(f).getValue().toString());
break;
}
}
if (!hit.getFields().keySet().contains("_source")) {
indexRequest.source(hit.getSourceAsString());
}
bulkNodeClient.bulkIndex(indexRequest);
}
}