package org.fastcatsearch.job.search; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.fastcatsearch.cluster.Node; import org.fastcatsearch.cluster.NodeService; import org.fastcatsearch.common.Strings; import org.fastcatsearch.control.ResultFuture; import org.fastcatsearch.error.SearchError; import org.fastcatsearch.error.ServerErrorCode; import org.fastcatsearch.exception.FastcatSearchException; import org.fastcatsearch.ir.IRService; import org.fastcatsearch.ir.config.CollectionContext; import org.fastcatsearch.ir.group.GroupResults; import org.fastcatsearch.ir.group.GroupsData; import org.fastcatsearch.ir.query.Groups; import org.fastcatsearch.ir.query.Query; import org.fastcatsearch.ir.search.GroupResultAggregator; import org.fastcatsearch.job.Job; import org.fastcatsearch.job.internal.InternalGroupSearchJob; import org.fastcatsearch.query.QueryMap; import org.fastcatsearch.query.QueryParser; import org.fastcatsearch.service.ServiceManager; import org.fastcatsearch.transport.vo.StreamableGroupsData; public class ClusterGroupSearchJob extends Job { private static final long serialVersionUID = -1051204900646595240L; @Override public JobResult doRun() throws FastcatSearchException { long st = System.currentTimeMillis(); QueryMap queryMap = (QueryMap) getArgs(); GroupResults groupResults = null; boolean noCache = false; Query q = QueryParser.getInstance().parseQuery(queryMap); //no cache 옵션이 없으면 캐시를 확인한다. if(q.getMeta().isSearchOption(Query.SEARCH_OPT_NOCACHE)){ noCache = true; } IRService irService = ServiceManager.getInstance().getService(IRService.class); if(!noCache){ groupResults = irService.groupingCache().get(queryMap.queryString()); // logger.debug("CACHE_GET result>>{}, qr >>{}", groupResults, queryMap.queryString()); if(groupResults != null){ return new JobResult(groupResults); } } NodeService nodeService = ServiceManager.getInstance().getService(NodeService.class); String collectionId = q.getMeta().collectionId(); Groups groups = q.getGroups(); String[] collectionIdList = collectionId.split(","); if(collectionIdList.length > 1) { shuffleCollectionList(collectionIdList); } ResultFuture[] resultFutureList = new ResultFuture[collectionIdList.length]; for (int i = 0; i < collectionIdList.length; i++) { String id = collectionIdList[i]; Node dataNode = nodeService.getBalancedNode(id); logger.debug("shard [{}] search at {}", id, dataNode); QueryMap newQueryMap = queryMap.clone(); newQueryMap.setId(id); //보내는 곳마다 collectionId를 재 셋팅한다. (collection group명일수 있기때문에) InternalGroupSearchJob job = new InternalGroupSearchJob(newQueryMap); resultFutureList[i] = nodeService.sendRequest(dataNode, job); // 노드 접속불가일경우 resultFutureList[i]가 null로 리턴됨. if (resultFutureList[i] == null) { throw new SearchError(ServerErrorCode.DATA_NODE_CONNECTION_ERROR, dataNode.toString() ); } } List<GroupsData> resultList = new ArrayList<GroupsData>(collectionIdList.length); for (int i = 0; i < collectionIdList.length; i++) { Object obj = resultFutureList[i].take(); if(!resultFutureList[i].isSuccess()){ if (obj instanceof SearchError) { throw (SearchError) obj; } else if (obj instanceof Throwable) { throw new FastcatSearchException((Throwable) obj); } else { throw new FastcatSearchException("Error while searching.", obj); } } StreamableGroupsData obj2 = (StreamableGroupsData) obj; resultList.add(obj2.groupData()); } GroupResultAggregator aggregator = new GroupResultAggregator(groups); groupResults = aggregator.aggregate(resultList); if(groupResults != null && !noCache){ irService.groupingCache().put(queryMap.queryString(), groupResults); } logger.debug("ClusterGroupSearchJob 수행시간 : {}", Strings.getHumanReadableTimeInterval(System.currentTimeMillis() - st)); return new JobResult(groupResults); } // Fisher-Yates shuffle Random random = new Random(System.nanoTime()); private void shuffleCollectionList(String[] collectionId) { for (int i = collectionId.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); // Simple swap String t = collectionId[index]; collectionId[index] = collectionId[i]; collectionId[i] = t; } } }