/* * Copyright (c) 2013 Websquared, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * swsong - initial API and implementation */ package org.fastcatsearch.job.indexing; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.fastcatsearch.cluster.ClusterUtils; import org.fastcatsearch.cluster.Node; import org.fastcatsearch.cluster.NodeJobResult; import org.fastcatsearch.cluster.NodeService; import org.fastcatsearch.common.io.Streamable; import org.fastcatsearch.control.JobService; import org.fastcatsearch.control.ResultFuture; import org.fastcatsearch.db.mapper.IndexingResultMapper.ResultStatus; import org.fastcatsearch.exception.FastcatSearchException; import org.fastcatsearch.ir.CollectionFullIndexer; import org.fastcatsearch.ir.CollectionIndexerable; import org.fastcatsearch.ir.IRService; import org.fastcatsearch.ir.MultiThreadCollectionFullIndexer; import org.fastcatsearch.ir.analysis.AnalyzerPoolManager; import org.fastcatsearch.ir.common.IndexingType; import org.fastcatsearch.ir.config.CollectionContext; import org.fastcatsearch.ir.config.CollectionIndexStatus.IndexStatus; import org.fastcatsearch.ir.config.DataInfo.SegmentInfo; import org.fastcatsearch.ir.io.DataInput; import org.fastcatsearch.ir.io.DataOutput; import org.fastcatsearch.ir.search.CollectionHandler; import org.fastcatsearch.ir.util.Counter; import org.fastcatsearch.job.CacheServiceRestartJob; import org.fastcatsearch.job.cluster.NodeCollectionReloadJob; import org.fastcatsearch.job.cluster.NodeDirectoryCleanJob; import org.fastcatsearch.job.result.IndexingJobResult; import org.fastcatsearch.job.state.IndexingTaskState; import org.fastcatsearch.service.ServiceManager; import org.fastcatsearch.transport.vo.StreamableCollectionContext; import org.fastcatsearch.transport.vo.StreamableThrowable; import org.fastcatsearch.util.CollectionContextUtil; import org.fastcatsearch.util.FilePaths; /** * 특정 collection의 index node에서 수행되는 job. * index node가 아닌 노드에 전달되면 색인을 수행하지 않는다. * * */ public class CollectionFullIndexingJob extends IndexingJob { private static final long serialVersionUID = 7898036370433248984L; private CollectionContext collectionContext; public CollectionFullIndexingJob(){ } public CollectionFullIndexingJob(CollectionContext collectionContext){ this.collectionContext = collectionContext; } @Override public JobResult doRun() throws FastcatSearchException { prepare(IndexingType.FULL, "ALL"); Throwable throwable = null; ResultStatus resultStatus = ResultStatus.RUNNING; Object result = null; long startTime = System.currentTimeMillis(); try { IRService irService = ServiceManager.getInstance().getService(IRService.class); AnalyzerPoolManager analyzerPoolManager = irService.createAnalyzerPoolManager(collectionContext.schema().schemaSetting().getAnalyzerSettingList()); //find index node // CollectionContext collectionContext = irService.collectionContext(collectionId); String indexNodeId = collectionContext.collectionConfig().getIndexNode(); NodeService nodeService = ServiceManager.getInstance().getService(NodeService.class); Node indexNode = nodeService.getNodeById(indexNodeId); if(!nodeService.isMyNode(indexNode)){ //Pass job to index node //작업수행하지 않음. throw new RuntimeException("Invalid index node collection[" + collectionId + "] node[" + indexNodeId + "]"); } if(!updateIndexingStatusStart()) { logger.error("Cannot start indexing job. {} : {}", collectionId, indexNodeId); resultStatus = ResultStatus.CANCEL; return new JobResult(); } /* * Do indexing!! */ ////////////////////////////////////////////////////////////////////////////////////////// boolean isIndexed = false; int segmentSize = collectionContext.collectionConfig().getFullIndexingSegmentSize(); CollectionIndexerable collectionFullIndexer = null; if(segmentSize <= 1){ collectionFullIndexer = new CollectionFullIndexer(collectionContext, analyzerPoolManager); }else{ collectionFullIndexer = new MultiThreadCollectionFullIndexer(collectionContext, analyzerPoolManager); } indexer = collectionFullIndexer; collectionFullIndexer.setTaskState(indexingTaskState); Throwable indexingThrowable = null; try { collectionFullIndexer.doIndexing(); }catch(Throwable e){ indexingThrowable = e; } finally { if (collectionFullIndexer != null) { try{ isIndexed = collectionFullIndexer.close(); }catch(Throwable closeThrowable){ //이전에 이미 발생한 에러가 있다면 close 중에 발생한 에러보다 이전 에러를 throw한다. if(indexingThrowable == null){ indexingThrowable = closeThrowable; } } } if(indexingThrowable != null){ throw indexingThrowable; } } if(!isIndexed && stopRequested){ //여기서 끝낸다. throw new IndexingStopException(); } /* * 색인파일 원격복사. */ indexingTaskState.setStep(IndexingTaskState.STEP_FILECOPY); SegmentInfo segmentInfo = collectionContext.dataInfo().getLastSegmentInfo(); if (segmentInfo != null) { String segmentId = segmentInfo.getId(); logger.debug("Transfer index data collection[{}] >> {}", collectionId); FilePaths indexFilePaths = collectionContext.indexFilePaths(); File indexDir = indexFilePaths.file(); // File segmentDir = indexFilePaths.file(segmentId); List<Node> nodeList = new ArrayList<Node>(nodeService.getNodeById(collectionContext.collectionConfig().getDataNodeList())); //색인노드가 data node에 추가되어있다면 제거한다. nodeList.remove(nodeService.getMyNode()); // 색인전송할디렉토리를 먼저 비우도록 요청.segmentDir File relativeDataDir = environment.filePaths().relativise(indexDir); NodeDirectoryCleanJob cleanJob = new NodeDirectoryCleanJob(relativeDataDir); NodeJobResult[] nodeResultList = null; nodeResultList = ClusterUtils.sendJobToNodeList(cleanJob, nodeService, nodeList, false); //성공한 node만 전송. nodeList = new ArrayList<Node>(); for (int i = 0; i < nodeResultList.length; i++) { NodeJobResult r = nodeResultList[i]; logger.debug("node#{} >> {}", i, r); if (r.isSuccess()) { nodeList.add(r.node()); }else{ logger.warn("Do not send index file to {}", r.node()); } } // 색인된 Segment 파일전송. TransferIndexFileMultiNodeJob transferJob = new TransferIndexFileMultiNodeJob(indexDir, nodeList); ResultFuture resultFuture = JobService.getInstance().offer(transferJob); Object obj = resultFuture.take(); if(resultFuture.isSuccess() && obj != null){ nodeResultList = (NodeJobResult[]) obj; }else{ } //성공한 node만 전송. nodeList = new ArrayList<Node>(); for (int i = 0; i < nodeResultList.length; i++) { NodeJobResult r = nodeResultList[i]; logger.debug("node#{} >> {}", i, r); if (r.isSuccess()) { nodeList.add(r.node()); }else{ logger.warn("Do not send index file to {}", r.node()); } } if(stopRequested){ throw new IndexingStopException(); } Set<Node> reloadNodeSet = new HashSet<Node>(); //데이터노드 reloadNodeSet.addAll(nodeList); //인덱스노드 reloadNodeSet.add(indexNode); //마스터노드 (관리도구에 보여지기 위함) 추가 reloadNodeSet.add(nodeService.getMasterNode()); // for(String nodeId : collectionContext.collectionConfig().getSearchNodeList()){ reloadNodeSet.add(nodeService.getNodeById(nodeId)); } /* * 데이터노드에 컬렉션 리로드 요청. */ NodeCollectionReloadJob reloadJob = new NodeCollectionReloadJob(collectionContext); nodeResultList = ClusterUtils.sendJobToNodeSet(reloadJob, nodeService, reloadNodeSet, true); for (int i = 0; i < nodeResultList.length; i++) { NodeJobResult r = nodeResultList[i]; logger.debug("node#{} >> {}", i, r); if (r.isSuccess()) { logger.info("{} Collection reload OK.", r.node()); }else{ logger.warn("{} Collection reload Fail.", r.node()); } } // if (!nodeResult) { // throw new FastcatSearchException("Node Collection Reload Failed!"); // } } indexingTaskState.setStep(IndexingTaskState.STEP_FINALIZE); int duration = (int) (System.currentTimeMillis() - startTime); IndexStatus indexStatus = collectionContext.indexStatus().getFullIndexStatus(); indexingLogger.info("[{}] Collection Full Indexing Finished! time = {}", collectionId, duration); result = new IndexingJobResult(collectionId, indexStatus, duration); resultStatus = ResultStatus.SUCCESS; indexingTaskState.setStep(IndexingTaskState.STEP_END); return new JobResult(result); } catch (IndexingStopException e){ if(stopRequested){ resultStatus = ResultStatus.STOP; }else{ resultStatus = ResultStatus.CANCEL; } result = new IndexingJobResult(collectionId, null, (int) (System.currentTimeMillis() - startTime), false); return new JobResult(result); } catch (Throwable e) { indexingLogger.error("[" + collectionId + "] Indexing", e); throwable = e; resultStatus = ResultStatus.FAIL; throw new FastcatSearchException("ERR-00500", throwable, collectionId); // 전체색인실패. } finally { Streamable streamableResult = null; if (throwable != null) { streamableResult = new StreamableThrowable(throwable); } else if (result instanceof Streamable) { streamableResult = (Streamable) result; } updateIndexingStatusFinish(resultStatus, streamableResult); } } @Override public void readFrom(DataInput input) throws IOException { super.readFrom(input); StreamableCollectionContext streamableCollectionContext = new StreamableCollectionContext(environment); streamableCollectionContext.readFrom(input); this.collectionContext = streamableCollectionContext.collectionContext(); } @Override public void writeTo(DataOutput output) throws IOException { super.writeTo(output); StreamableCollectionContext streamableCollectionContext = new StreamableCollectionContext(collectionContext); streamableCollectionContext.writeTo(output); } }