/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.iv2; import java.util.List; import java.util.Map; import java.util.concurrent.Future; import org.voltcore.logging.VoltLogger; import org.voltcore.utils.CoreUtils; import org.voltcore.utils.DBBPool; import org.voltcore.utils.Pair; import org.voltdb.BackendTarget; import org.voltdb.CatalogContext; import org.voltdb.CatalogSpecificPlanner; import org.voltdb.DRConsumerDrIdTracker; import org.voltdb.DRIdempotencyResult; import org.voltdb.DependencyPair; import org.voltdb.HsqlBackend; import org.voltdb.LoadedProcedureSet; import org.voltdb.NonVoltDBBackend; import org.voltdb.ParameterSet; import org.voltdb.PartitionDRGateway; import org.voltdb.PostGISBackend; import org.voltdb.PostgreSQLBackend; import org.voltdb.ProcedureRunner; import org.voltdb.SiteProcedureConnection; import org.voltdb.SiteSnapshotConnection; import org.voltdb.StatsSelector; import org.voltdb.SystemProcedureExecutionContext; import org.voltdb.TableStreamType; import org.voltdb.TheHashinator; import org.voltdb.TupleStreamStateInfo; import org.voltdb.VoltDB; import org.voltdb.VoltProcedure.VoltAbortException; import org.voltdb.VoltTable; import org.voltdb.catalog.Cluster; import org.voltdb.catalog.Database; import org.voltdb.catalog.Procedure; import org.voltdb.dtxn.SiteTracker; import org.voltdb.dtxn.TransactionState; import org.voltdb.dtxn.UndoAction; import org.voltdb.exceptions.EEException; import org.voltdb.messaging.FastDeserializer; import org.voltdb.settings.ClusterSettings; import org.voltdb.settings.NodeSettings; /** * An implementation of Site which provides only the functionality * necessary to run read-only multi-partition transactions. A pool * of these will be used to run multiple read-only transactions * concurrently. */ public class MpRoSite implements Runnable, SiteProcedureConnection { @SuppressWarnings("unused") private static final VoltLogger tmLog = new VoltLogger("TM"); // Set to false trigger shutdown. volatile boolean m_shouldContinue = true; // HSId of this site's initiator. final long m_siteId; // What type of EE is controlled final BackendTarget m_backend; // Manages pending tasks. final SiteTaskerQueue m_scheduler; // Still need m_non_voltdb_backend (formerly m_hsql) here NonVoltDBBackend m_non_voltdb_backend; // Current catalog volatile CatalogContext m_context; // Currently available procedure volatile LoadedProcedureSet m_loadedProcedures; // Current topology int m_partitionId; @Override public long getLatestUndoToken() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } SiteProcedureConnection getSiteProcedureConnection() { return this; } /** * SystemProcedures are "friends" with ExecutionSites and granted * access to internal state via m_systemProcedureContext. * * The only sysproc which should run on the RO MP Site is Adhoc. Everything * else will yell at you. */ SystemProcedureExecutionContext m_sysprocContext = new SystemProcedureExecutionContext() { @Override public ClusterSettings getClusterSettings() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public NodeSettings getPaths() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public Database getDatabase() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public Cluster getCluster() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public long getSpHandleForSnapshotDigest() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public long getSiteId() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public int getLocalSitesCount() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public boolean isLowestSiteId() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public int getClusterId() { return getCorrespondingClusterId(); } @Override public int getHostId() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public int getPartitionId() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public long getCatalogCRC() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public byte[] getCatalogHash() { // AdHoc invocations need to be able to check the hash of the current catalog // against the hash of the catalog they were planned against. return m_context.getCatalogHash(); } @Override public byte[] getDeploymentHash() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } // Needed for Adhoc queries @Override public int getCatalogVersion() { return m_context.catalogVersion; } @Override public SiteTracker getSiteTrackerForSnapshot() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public int getNumberOfPartitions() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public void setNumberOfPartitions(int partitionCount) { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public SiteProcedureConnection getSiteProcedureConnection() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public SiteSnapshotConnection getSiteSnapshotConnection() { throw new RuntimeException("Not needed for RO MP Site, shouldn't be here."); } @Override public void updateBackendLogLevels() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public boolean updateCatalog(String diffCmds, CatalogContext context, CatalogSpecificPlanner csp, boolean requiresSnapshotIsolation, long uniqueId, long spHandle, boolean requireCatalogDiffCmdsApplyToEE, boolean requiresNewExportGeneration) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public boolean updateSettings(CatalogContext context, CatalogSpecificPlanner csp) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public TheHashinator getCurrentHashinator() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void updateHashinator(TheHashinator hashinator) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public boolean activateTableStream(int tableId, TableStreamType type, boolean undo, byte[] predicates) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void forceAllDRNodeBuffersToDisk(final boolean nofsync) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public DRIdempotencyResult isExpectedApplyBinaryLog(int producerClusterId, int producerPartitionId, long lastReceivedDRId) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void appendApplyBinaryLogTxns(int producerClusterId, int producerPartitionId, long localUniqueId, DRConsumerDrIdTracker tracker) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void recoverWithDrAppliedTrackers(Map<Integer, Map<Integer, DRConsumerDrIdTracker>> trackers) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void resetDrAppliedTracker() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void resetDrAppliedTracker(byte clusterId) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public boolean hasRealDrAppliedTracker(byte clusterId) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void initDRAppliedTracker(Map<Byte, Integer> clusterIdToPartitionCountMap) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Map<Integer, Map<Integer, DRConsumerDrIdTracker>> getDrAppliedTrackers() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Pair<Long, Long> getDrLastAppliedUniqueIds() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Pair<Long, int[]> tableStreamSerializeMore(int tableId, TableStreamType type, List<DBBPool.BBContainer> outputBuffers) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Procedure ensureDefaultProcLoaded(String procName) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } }; /** Create a new RO MP execution site */ public MpRoSite( SiteTaskerQueue scheduler, long siteId, BackendTarget backend, CatalogContext context, int partitionId) { m_siteId = siteId; m_context = context; m_partitionId = partitionId; m_scheduler = scheduler; m_backend = backend; } /** Update the loaded procedures. */ void setLoadedProcedures(LoadedProcedureSet loadedProcedure) { m_loadedProcedures = loadedProcedure; } /** Thread specific initialization */ void initialize() { if (m_backend == BackendTarget.HSQLDB_BACKEND) { m_non_voltdb_backend = HsqlBackend.initializeHSQLBackend(m_siteId, m_context); } else if (m_backend == BackendTarget.POSTGRESQL_BACKEND) { m_non_voltdb_backend = PostgreSQLBackend.initializePostgreSQLBackend(m_context); } else if (m_backend == BackendTarget.POSTGIS_BACKEND) { m_non_voltdb_backend = PostGISBackend.initializePostGISBackend(m_context); } else { m_non_voltdb_backend = null; } } @Override public void run() { initialize(); try { while (m_shouldContinue) { // Normal operation blocks the site thread on the sitetasker queue. SiteTasker task = m_scheduler.take(); task.run(getSiteProcedureConnection()); } } catch (OutOfMemoryError e) { // Even though OOM should be caught by the Throwable section below, // it sadly needs to be handled seperately. The goal here is to make // sure VoltDB crashes. String errmsg = "Site: " + org.voltcore.utils.CoreUtils.hsIdToString(m_siteId) + " ran out of Java memory. " + "This node will shut down."; VoltDB.crashLocalVoltDB(errmsg, true, e); } catch (Throwable t) { String errmsg = "Site: " + org.voltcore.utils.CoreUtils.hsIdToString(m_siteId) + " encountered an " + "unexpected error and will die, taking this VoltDB node down."; VoltDB.crashLocalVoltDB(errmsg, true, t); } shutdown(); } /** * Commence the shutdown of the Site. This will allow the run loop to escape, but * it will be blocked on taking from the SiteTaskerQueue. To make shutdown actually * happen, the site needs to be unblocked by offering a null SiteTasker to the queue, see * Scheduler.m_nullTask for an example (or just use it). */ public void startShutdown() { m_shouldContinue = false; } void shutdown() { } // // Legacy SiteProcedureConnection needed by ProcedureRunner // @Override public long getCorrespondingSiteId() { return m_siteId; } @Override public int getCorrespondingPartitionId() { return m_partitionId; } @Override public int getCorrespondingHostId() { return CoreUtils.getHostIdFromHSId(m_siteId); } @Override public int getCorrespondingClusterId() { return m_context.cluster.getDrclusterid(); } @Override public PartitionDRGateway getDRGateway() { throw new UnsupportedOperationException("RO MP Site doesn't have DR gateway"); } @Override public byte[] loadTable(long txnId, long spHandle, long unqiueId, String clusterName, String databaseName, String tableName, VoltTable data, boolean returnUniqueViolations, boolean shouldDRStream, boolean undo) throws VoltAbortException { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public byte[] loadTable(long txnId, long spHandle, long uniqueId, int tableId, VoltTable data, boolean returnUniqueViolations, boolean shouldDRStream, boolean undo) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void updateBackendLogLevels() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Map<Integer, List<VoltTable>> recursableRun( TransactionState currentTxnState) { return currentTxnState.recursableRun(this); } @Override public void setSpHandleForSnapshotDigest(long spHandle) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void truncateUndoLog(boolean rollback, long beginUndoToken, long spHandle, List<UndoAction> undoActions) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void stashWorkUnitDependencies(Map<Integer, List<VoltTable>> dependencies) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public DependencyPair executeSysProcPlanFragment( TransactionState txnState, Map<Integer, List<VoltTable>> dependencies, long fragmentId, ParameterSet params) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public NonVoltDBBackend getNonVoltDBBackendIfExists() { return m_non_voltdb_backend; } @Override public long[] getUSOForExportTable(String signature) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public TupleStreamStateInfo getDRTupleStreamStateInfo() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void setDRSequenceNumbers(Long partitionSequenceNumber, Long mpSequenceNumber) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void toggleProfiler(int toggle) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void tick() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void quiesce() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void exportAction(boolean syncAction, long ackOffset, Long sequenceNumber, Integer partitionId, String tableSignature) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public VoltTable[] getStats(StatsSelector selector, int[] locators, boolean interval, Long now) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public Future<?> doSnapshotWork() { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void setRejoinComplete( JoinProducerBase.JoinCompletionAction replayComplete, Map<String, Map<Integer, Pair<Long, Long>>> exportSequenceNumbers, Map<Integer, Long> drSequenceNumbers, Map<Integer, Map<Integer, Map<Integer, DRConsumerDrIdTracker>>> allConsumerSiteTrackers, boolean requireExistingSequenceNumbers, long clusterCreateTime) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public FastDeserializer executePlanFragments( int numFragmentIds, long[] planFragmentIds, long[] inputDepIds, Object[] parameterSets, DeterminismHash determinismHash, String[] sqlTexts, boolean[] isWriteFrags, int[] sqlCRCs, long txnId, long spHandle, long uniqueId, boolean readOnly, boolean traceOn) throws EEException { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public boolean usingFallbackBuffer() { return false; } @Override public ProcedureRunner getProcedureRunner(String procedureName) { return m_loadedProcedures.getProcByName(procedureName); } /** * Update the catalog. If we're the MPI, don't bother with the EE. */ public boolean updateCatalog(String diffCmds, CatalogContext context, CatalogSpecificPlanner csp, boolean requiresSnapshotIsolationboolean, boolean isMPI) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void setPerPartitionTxnIds(long[] perPartitionTxnIds, boolean skipMultipart) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public TheHashinator getCurrentHashinator() { return null; } @Override public void updateHashinator(TheHashinator hashinator) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } /** * For the specified list of table ids, return the number of mispartitioned rows using * the provided hashinator and hashinator config */ @Override public long[] validatePartitioning(long[] tableIds, int hashinatorType, byte[] hashinatorConfig) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void setBatch(int batchIndex) { // don't need to do anything here } @Override public void setProcedureName(String procedureName) { // don't need to do anything here I think? } @Override public void notifyOfSnapshotNonce(String nonce, long snapshotSpHandle) { // TODO Auto-generated method stub } @Override public long applyBinaryLog(long txnId, long spHandle, long uniqueId, int remoteClusterId, byte log[]) { throw new UnsupportedOperationException("RO MP Site doesn't do this, shouldn't be here"); } @Override public void setBatchTimeout(int batchTimeout) { throw new UnsupportedOperationException("RO MP Site doesn't do this, shouldn't be here"); } @Override public int getBatchTimeout() { throw new UnsupportedOperationException("RO MP Site doesn't do this, shouldn't be here"); } @Override public void setDRProtocolVersion(int drVersion) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public void setDRProtocolVersion(int drVersion, long spHandle, long uniqueId) { throw new RuntimeException("RO MP Site doesn't do this, shouldn't be here."); } @Override public SystemProcedureExecutionContext getSystemProcedureExecutionContext() { return m_sysprocContext; } }