/** * */ package com.linkedin.databus.bootstrap.server; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * 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. * */ import java.io.IOException; import java.io.StringWriter; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import org.apache.log4j.Logger; import org.codehaus.jackson.map.ObjectMapper; import com.linkedin.databus.bootstrap.common.BootstrapDBMetaDataDAO; import com.linkedin.databus.bootstrap.common.BootstrapDBMetaDataDAO.SourceStatusInfo; import com.linkedin.databus.bootstrap.common.BootstrapHttpStatsCollector; import com.linkedin.databus.core.Checkpoint; import com.linkedin.databus2.core.container.request.BootstrapDatabaseTooOldException; import com.linkedin.databus2.core.container.request.BootstrapDatabaseTooYoungException; import com.linkedin.databus2.core.container.request.DatabusRequest; import com.linkedin.databus2.core.container.request.RequestProcessingException; public class StartSCNRequestProcessor extends BootstrapRequestProcessorBase { public static final String MODULE = StartSCNRequestProcessor.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); public final static String COMMAND_NAME = "startSCN"; public final static String SOURCES_PARAM = "sources"; public final static String SOURCE_DELIMITER = ","; public StartSCNRequestProcessor(ExecutorService executorService, BootstrapServerStaticConfig config, BootstrapHttpServer bootstrapServer) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { super(executorService,config,bootstrapServer); } @Override protected DatabusRequest doProcess(DatabusRequest request) throws IOException, RequestProcessingException { BootstrapHttpStatsCollector bootstrapStatsCollector = _bootstrapServer.getBootstrapStatsCollector(); long startTime = System.currentTimeMillis(); String sources = request.getRequiredStringParam(SOURCES_PARAM); List<String> srcList = getSources(sources); Checkpoint ckpt = new Checkpoint(request.getRequiredStringParam(CHECKPOINT_PARAM)); LOG.info("StartSCN requested for sources : (" + sources + "). CheckPoint is :" + ckpt); long sinceScn = ckpt.getBootstrapSinceScn(); ObjectMapper mapper = new ObjectMapper(); StringWriter out = new StringWriter(1024); long startSCN = -1; BootstrapSCNProcessor processor = null; try { processor = new BootstrapSCNProcessor(_config, _bootstrapServer.getInboundEventStatisticsCollector()); List<SourceStatusInfo> srcStatusPairs = null; try { srcStatusPairs = processor.getSourceIdAndStatusFromName(srcList); startSCN = processor.getMinApplierWindowScn(sinceScn, srcStatusPairs); if (processor.shouldBypassSnapshot(sinceScn, startSCN, srcStatusPairs)) { LOG.info("Bootstrap Snapshot phase will be bypassed for startScn request :" + request); LOG.info("Original startSCN is:" + startSCN + ", Setting startSCN to the sinceSCN:" + sinceScn); startSCN = sinceScn; } else { if (startSCN == BootstrapDBMetaDataDAO.DEFAULT_WINDOWSCN) { throw new RequestProcessingException("Bootstrap DB is being initialized! startSCN=" + startSCN); } if (_config.isEnableMinScnCheck()) { //snapshot isn't bypassed. Check if snapshot is possible from sinceScn by checking minScn long minScn = processor.getBootstrapMetaDataDAO().getMinScnOfSnapshots(srcStatusPairs); LOG.info("Min scn for tab tables is: " + minScn); if (minScn == BootstrapDBMetaDataDAO.DEFAULT_WINDOWSCN) { throw new BootstrapDatabaseTooYoungException("BootstrapDB has no minScn for these sources, but minScn check is enabled! minScn=" + minScn); } //Note: The cleaner deletes rows less than or equal to scn: BootstrapDBCleaner::doClean //sinceSCN should be greater than minScn, unless sinceScn=minScn=0 if ((sinceScn <= minScn) && !(sinceScn==0 && minScn==0)) { LOG.error("Bootstrap Snapshot doesn't have requested data . sinceScn too old! sinceScn is " + sinceScn + " but minScn available is " + minScn); throw new BootstrapDatabaseTooYoungException("Min scn=" + minScn + " Since scn=" + sinceScn); } } else { LOG.debug("Bypassing minScn check! "); } } } catch (BootstrapDatabaseTooOldException tooOldException) { if (bootstrapStatsCollector != null) { bootstrapStatsCollector.registerErrStartSCN(); bootstrapStatsCollector.registerErrDatabaseTooOld(); } LOG.error("The bootstrap database is too old!", tooOldException); throw new RequestProcessingException(tooOldException); } catch (BootstrapDatabaseTooYoungException e) { if (bootstrapStatsCollector != null) { bootstrapStatsCollector.registerErrStartSCN(); bootstrapStatsCollector.registerErrBootstrap(); } LOG.error("The bootstrap database is too young!", e); throw new RequestProcessingException(e); } catch (SQLException e) { if (bootstrapStatsCollector != null) { bootstrapStatsCollector.registerErrStartSCN(); bootstrapStatsCollector.registerErrSqlException(); } LOG.error("Error encountered while fetching startSCN from database.", e); throw new RequestProcessingException(e); } mapper.writeValue(out, String.valueOf(startSCN)); byte[] resultBytes = out.toString().getBytes(Charset.defaultCharset()); request.getResponseContent().write(ByteBuffer.wrap(resultBytes)); LOG.info("startSCN: " + startSCN + " with server Info :" + _serverHostPort); } catch (RequestProcessingException ex) { LOG.error("Got exception while calculating startSCN", ex); throw ex; } catch (Exception ex) { LOG.error("Got exception while calculating startSCN", ex); throw new RequestProcessingException(ex); } finally { if ( null != processor) processor.shutdown(); } if (bootstrapStatsCollector != null) { bootstrapStatsCollector.registerStartSCNReq(System.currentTimeMillis()-startTime); } return request; } private List<String> getSources(String sources) { List<String> srcList = new ArrayList<String>(); if ( null == sources ) { return null; } String[] sourceList = sources.split(SOURCE_DELIMITER); for(String s : sourceList) { srcList.add(s); } return srcList; } }