/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB Inc. * * VoltDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VoltDB 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.jni; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import org.voltdb.BackendTarget; import org.voltdb.DependencySet; import org.voltdb.ParameterSet; import org.voltdb.PrivateVoltTableFactory; import org.voltdb.SysProcSelector; import org.voltdb.TableStreamType; import org.voltdb.VoltTable; import org.voltdb.catalog.Table; import org.voltdb.exceptions.EEException; import org.voltdb.exceptions.SerializableException; import org.voltdb.export.ExportProtoMessage; import org.voltdb.messaging.FastDeserializer; import org.voltdb.messaging.FastSerializer; import org.voltdb.utils.DBBPool.BBContainer; import org.voltdb.utils.NotImplementedException; import org.voltdb.types.AntiCacheDBType; import edu.brown.hstore.HStore; import edu.brown.hstore.PartitionExecutor; /* Serializes data over a connection that presumably is being read * by a voltdb execution engine. The serialization is currently a * a packed binary big endian buffer. Each buffer has a header * that provides the total length (as an int) and the command being * serialized (also as an int). Commands are described by the Commands * enum. * * These serializations are implemented as required since a lot of * the native interface isn't needed in many cases. * * The serialization could be more robust. Probably the right way to * get that robustness would be to create FastSerializable classes * for the more complex calls and read them through the fast serializable * C++ interface. * * (The fastserializer still requires hand-authoring the ser/deser * logic in java and C and doesn't result in any more favorable * packing; in fact the resulting wire content might be the same in * most cases... And you can't fast serialize an array of primitive * types without writing a wrapper object.) * * Responses to the IPC interface start with a 1 byte result code. If the * result code is failure then no message follows. If the result code * is success and a response is warranted it will follow and is length prefixed if necessary. * Unlike requests to the EE, the length prefix in responses does not include the 1 byte * result code nor the 4 byte length field. When we correct the 1 and 4 byte network reads and writes * we can this. A length will not prefix the response * if the length of the response can be deduced from the original request. * * The return message format for DMLPlanFragments is all big endian: * 1 byte result code * 8 byte results codes. Same number of results as numPlanFragments. * * The return message format for QueryPlanFragments is all big endian: * 1 byte result code * 4 byte result length. Does not include the length of the result code or result length field. * X bytes of serialized VoltTables. Same number of tables as numPlanFragments * * The return message format for PlanFragment is all big endian: * 1 byte result code * 4 byte result length. Does not include the length of the result code or result length field. * 4 byte result indicating number of dependencies returned * The dependency tables * * The return message format for ReceiveDependency consists of a single byte result code. * * The return message format for Load table consists of a single byte result code. */ public class ExecutionEngineIPC extends ExecutionEngine { /** * Exposes error generated by the Valgrind client to org.voltdb.regressionsuites.LocalSingeProcessServer * which will fail a test if this list is not empty. */ public static final List<String> m_valgrindErrors = Collections.synchronizedList(new ArrayList<String>()); /** Commands are serialized over the connection */ private enum Commands { Initialize(0), LoadCatalog(2), ToggleProfiler(3), Tick(4), GetStats(5), QueryPlanFragments(6), PlanFragment(7), LoadTable(9), releaseUndoToken(10), undoUndoToken(11), CustomPlanFragment(12), SetLogLevels(13), Quiesce(16), ActivateTableStream(17), TableStreamSerializeMore(18), UpdateCatalog(19), ExportAction(20), RecoveryMessage(21), TableHashCode(22), Hashinate(23); Commands(final int id) { m_id = id; } int m_id; } private static AtomicInteger eeCount = new AtomicInteger(21214); /** * One connection per ExecutionEngineIPC. This connection also interfaces * with Valgrind to report any problems that Valgrind may find including * reachable heap blocks on exit. Unfortunately it is not enough to inspect * the Valgrind return code as it considers reachable blocks to not be an * error. **/ private class Connection { private Socket m_socket = null; private SocketChannel m_socketChannel = null; private Process m_eeProcess; private String m_eePID = null; private Thread m_stdoutParser = null; // Has a received String from Valgrind saying that all heap blocks were freed // ignored when running directly againsts the IPC client private boolean m_allHeapBlocksFreed = false; Connection(final BackendTarget target) { /* * String ee_exec_path = System.getenv("EEIPC_PATH"); if * (ee_exec_path != null) { try { eeProcess = * Runtime.getRuntime().exec("valgrind " + ee_exec_path + * " > ~aweisberg/valgrind_out 2>&1 "); } catch (IOException e) { * throw new RuntimeException(e); } try { Thread.sleep(1000); } * catch (InterruptedException e) { // TODO Auto-generated catch * block e.printStackTrace(); } } */ int port = 21214; if (target == BackendTarget.NATIVE_EE_IPC) { System.out .println("Press enter after you have started the EE process to initiate the connection to the EE"); try { System.in.read(); } catch (final IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } else if (target == BackendTarget.NATIVE_EE_VALGRIND_IPC) { System.out.println("Running " + target); final ArrayList<String> args = new ArrayList<String>(); final String voltdbIPCPath = System.getenv("VOLTDBIPC_PATH"); args.add("valgrind"); args.add("--leak-check=full"); args.add("--show-reachable=yes"); args.add("--num-callers=32"); args.add("--error-exitcode=-1"); /* * VOLTDBIPC_PATH is set as part of the regression suites and ant check * In that scenario junit will handle logging of Valgrind output. * stdout and stderr is forwarded from the backend. */ if (voltdbIPCPath == null) { args.add("--quiet"); args.add("--log-file=site_" + m_siteId + ".log"); } args.add(voltdbIPCPath == null ? "./voltdbipc" : voltdbIPCPath); port = eeCount.getAndIncrement(); args.add(Integer.toString(port)); final ProcessBuilder pb = new ProcessBuilder(args); pb.redirectErrorStream(true); try { m_eeProcess = pb.start(); final Process p = m_eeProcess; Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { p.destroy(); } }); } catch (final IOException e) { e.printStackTrace(); HStore.crashDB(); } /* * This block attempts to read the PID and then waits for the listening message * indicating that the IPC EE is ready to accept a connection on a socket */ final BufferedReader r = new BufferedReader(new InputStreamReader(m_eeProcess.getInputStream())); try { boolean failure = false; String pidString = r.readLine(); if (pidString == null) { failure = true; } else { System.out.println("PID string \"" + pidString + "\""); pidString = pidString.substring(2); pidString = pidString.substring(0, pidString.indexOf("=")); m_eePID = pidString; } while (true) { String line = null; if (!failure) { line = r.readLine(); } if (line != null && !failure) { System.out.println("[ipc=" + m_eePID + "]:::" + line); if (line.contains("listening")) { break; } else { continue; } } else { try { m_eeProcess.waitFor(); } catch (final InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("[ipc=" + m_eePID + "] Returned end of stream and exit value " + m_eeProcess.exitValue()); HStore.crashDB(); } } } catch (final IOException e) { e.printStackTrace(); return; } /* * Create a thread to parse Valgrind's output and populdate m_valgrindErrors with errors. */ final Process p = m_eeProcess; m_stdoutParser = new Thread() { @Override public void run() { while (true) { try { final String line = r.readLine(); if (line != null) { System.out.println("[ipc=" + p.hashCode() + "]:::" + line); if (line.startsWith("==" + m_eePID + "==")) { processValgrindOutput(line); } } else { try { p.waitFor(); } catch (final InterruptedException e) { e.printStackTrace(); } System.out.println("[ipc=" + m_eePID + "] Returned end of stream and exit value " + p.exitValue()); if (!m_allHeapBlocksFreed) { m_valgrindErrors.add("Not all heap blocks were freed"); } return; } } catch (final IOException e) { e.printStackTrace(); return; } } } private void processValgrindOutput(final String line) { final String errorLineString = "ERROR SUMMARY: "; final String heapBlocksFreedString = "All heap blocks were freed"; /* * An indirect way of making sure Valgrind reports no error memory accesses */ if (line.contains(errorLineString)) { final int index = line.indexOf(errorLineString) + errorLineString.length(); final char errorNumChar = line.charAt(index); if (!(errorNumChar == '0')) { m_valgrindErrors.add(line); } } else if (line.contains(heapBlocksFreedString)) { m_allHeapBlocksFreed = true; } } }; m_stdoutParser.setDaemon(false); m_stdoutParser.start(); } else { throw new RuntimeException("Shouldn't instantiate an ExecutionEngineIPC with BackendTarget " + target); } try { m_socketChannel = SocketChannel.open(new InetSocketAddress( "localhost", port)); m_socketChannel.configureBlocking(true); m_socket = m_socketChannel.socket(); m_socket.setTcpNoDelay(true); } catch (final Exception e) { System.out.println(e.getMessage()); System.out .println("Failed to initialize IPC EE connection. Quitting."); while (true) {} //System.exit(-1); } System.out.println("Created IPC connection for site."); } /* Close the socket indicating to the EE it should terminate */ public void close() throws InterruptedException { if (m_socketChannel != null) { try { m_socketChannel.close(); } catch (final IOException e) { throw new RuntimeException(e); } m_socketChannel = null; m_socket = null; } if (m_eeProcess != null) { m_eeProcess.waitFor(); } if (m_stdoutParser != null) { m_stdoutParser.join(); } } // /** blocking write of all m_data to outputstream */ // void write(final byte data[], final int amount) throws IOException { // // write 4 byte length (which includes its own 4 bytes) in big-endian // // order. this hurts .. but I'm not sure of a better way. // final byte[] length = new byte[4]; // int amt = amount + 4; // // System.out.println("Sending " + amt + " bytes"); // assert (amt >= 8); // for (int i = 3; i >= 0; --i) { // length[i] = (byte) (amt & 0xff); // amt >>= 8; // } // m_socket.getOutputStream().write(length); // m_socket.getOutputStream().write(data, 0, amount); // } /** blocking write of all m_data to outputstream */ void write() throws IOException { // write 4 byte length (which includes its own 4 bytes) in big-endian // order. this hurts .. but I'm not sure of a better way. m_dataNetwork.clear(); final int amt = m_data.remaining(); m_dataNetwork.putInt(amt + 4); m_dataNetwork.limit(4 + amt); m_dataNetwork.rewind(); while (m_dataNetwork.hasRemaining()) { m_socketChannel.write(m_dataNetwork); } } /** * An error code specific to the IPC backend that indicates * that as part of fulfilling a previous request the IPC * backend requires a dependency table. This is not really * an error code. */ static final int kErrorCode_RetrieveDependency = 100; /** * An error code to be sent in a response to an RetrieveDependency request. * Indicates that a dependency table was found and that it follows */ static final int kErrorCode_DependencyFound = 101; /** * An error code to be sent in a response to an RetrieveDependency request. * Indicates that no dependency tables could be found and that no data follows. */ static final int kErrorCode_DependencyNotFound = 102 ; /** * Invoke crash VoltDB */ static final int kErrorCode_CrashVoltDB = 104; /** * Read a single byte indicating a return code. This method has evolved * to include providing dependency tables necessary for the completion of previous * request. The method loops ready status bytes instead of recursing to avoid * excessive recursion in the case where a large number of dependency tables * must be fetched before the request can be satisfied. * * Facing further evolutionary pressure, readStatusByte has grown * EL buffer reading fins. EL buffers arrive here mid-command execution. * @return * @throws IOException */ int readStatusByte() throws IOException { int status = kErrorCode_RetrieveDependency; while (true) { status = m_socket.getInputStream().read(); if (status == kErrorCode_RetrieveDependency) { final ByteBuffer dependencyIdBuffer = ByteBuffer.allocate(4); while (dependencyIdBuffer.hasRemaining()) { final int read = m_socketChannel.read(dependencyIdBuffer); if (read == -1) { throw new IOException("Unable to read enough bytes for dependencyId in order to " + " satisfy IPC backend request for a dependency table"); } } dependencyIdBuffer.rewind(); sendDependencyTable(dependencyIdBuffer.getInt()); continue; } if (status == kErrorCode_CrashVoltDB) { ByteBuffer lengthBuffer = ByteBuffer.allocate(4); while (lengthBuffer.hasRemaining()) { final int read = m_socket.getChannel().read(lengthBuffer); if (read == -1) { throw new EOFException(); } } lengthBuffer.flip(); ByteBuffer messageBuffer = ByteBuffer.allocate(lengthBuffer.getInt()); while (messageBuffer.hasRemaining()) { final int read = m_socket.getChannel().read(messageBuffer); if (read == -1) { throw new EOFException(); } } final int reasonLength = messageBuffer.getInt(); final byte reasonBytes[] = new byte[reasonLength]; messageBuffer.get(reasonBytes); final String message = new String(reasonBytes, "UTF-8"); final int filenameLength = messageBuffer.getInt(); final byte filenameBytes[] = new byte[filenameLength]; messageBuffer.get(filenameBytes); final String filename = new String(filenameBytes, "UTF-8"); final int lineno = messageBuffer.getInt(); final int numTraces = messageBuffer.getInt(); final String traces[] = new String[numTraces]; for (int ii = 0; ii < numTraces; ii++) { final int traceLength = messageBuffer.getInt(); final byte traceBytes[] = new byte[traceLength]; traces[ii] = new String(traceBytes, "UTF-8"); } ExecutionEngine.crashVoltDB(message, traces, filename, lineno); } try { checkErrorCode(status); break; } catch (final RuntimeException e) { if (e instanceof SerializableException) { throw e; } else { throw (IOException)e.getCause(); } } } return status; } /** * Read and deserialize some number of tables from the wire. Assumes that the message is length prefixed. * @param tables Output array as well as indicator of exactly how many tables to read off of the wire * @throws IOException */ public void readResultTables(final VoltTable tables[]) throws IOException { final ByteBuffer resultTablesLengthBytes = ByteBuffer.allocate(4); //resultTablesLengthBytes.order(ByteOrder.LITTLE_ENDIAN); while (resultTablesLengthBytes.hasRemaining()) { int read = m_socketChannel.read(resultTablesLengthBytes); if (read == -1) { throw new EOFException(); } } resultTablesLengthBytes.flip(); final int resultTablesLength = resultTablesLengthBytes.getInt(); final ByteBuffer resultTablesBuffer = ByteBuffer .allocate(resultTablesLength); //resultTablesBuffer.order(ByteOrder.LITTLE_ENDIAN); while (resultTablesBuffer.hasRemaining()) { int read = m_socketChannel.read(resultTablesBuffer); if (read == -1) { throw new EOFException(); } } resultTablesBuffer.flip(); final FastDeserializer ds = new FastDeserializer(resultTablesBuffer); // check if anything was changed final boolean dirty = ds.readBoolean(); if (dirty) m_dirty = true; for (int ii = 0; ii < tables.length; ii++) { final int dependencyCount = ds.readInt(); // ignore the table count assert(dependencyCount == 1); //Expect one dependency generated per plan fragment ds.readInt(); // ignore the dependency ID tables[ii] = (VoltTable) ds.readObject(tables[ii], null); } } /** * Read the result dependencies returned from the execution of a plan fragment. * Returns a list of pairs of dependency ids and dependency tables. */ public DependencySet readDependencies() throws IOException { // read the result set size, which doesn't include this 4 byte // length notification! final ByteBuffer resultSetSizeBuff = ByteBuffer.allocate(4); resultSetSizeBuff.rewind(); while (resultSetSizeBuff.hasRemaining()) { int read = m_socketChannel.read(resultSetSizeBuff); if (read == -1) { throw new EOFException(); } } resultSetSizeBuff.rewind(); final int resultsSize = resultSetSizeBuff.getInt(); // read the serialized dependencies final ByteBuffer depsBuff = ByteBuffer.allocate(resultsSize); depsBuff.clear().rewind(); while (depsBuff.hasRemaining()) { int read = m_socketChannel.read(depsBuff); if (read == -1) { throw new EOFException(); } } // deserialize the dependencies depsBuff.rewind(); final boolean dirty = depsBuff.get() > 0; if (dirty) { m_dirty = true; } final int numDependencies = depsBuff.getInt(); final int[] depIds = new int[numDependencies]; final VoltTable[] dependencies = new VoltTable[numDependencies]; final FastDeserializer fds = new FastDeserializer(depsBuff); for (int ii = 0; ii < numDependencies; ++ii) { depIds[ii] = fds.readInt(); dependencies[ii] = fds.readObject(VoltTable.class); } assert(depIds.length == 1); // and finally return the constructed dependency set return new DependencySet(depIds, dependencies); } public void throwException(final int errorCode) throws IOException { final ByteBuffer lengthBuffer = ByteBuffer.allocate(4); while (lengthBuffer.hasRemaining()) { int read = m_socketChannel.read(lengthBuffer); if (read == -1) { throw new EOFException(); } } lengthBuffer.flip(); final int exceptionLength = lengthBuffer.getInt();//Length is only between EE and Java. if (exceptionLength == 0) { throw new EEException(errorCode); } else { final ByteBuffer exceptionBuffer = ByteBuffer.allocate(exceptionLength + 4); exceptionBuffer.putInt(exceptionLength); while(exceptionBuffer.hasRemaining()) { int read = m_socketChannel.read(exceptionBuffer); if (read == -1) { throw new EOFException(); } } assert(!exceptionBuffer.hasRemaining()); exceptionBuffer.rewind(); throw SerializableException.deserializeFromBuffer(exceptionBuffer); } } } /** Local m_data */ private final int m_clusterIndex; private final int m_siteId; private final int m_partitionId; private final int m_hostId; private final String m_hostname; // private final FastSerializer m_fser; private final Connection m_connection; private final BBContainer m_dataNetworkOrigin; private final ByteBuffer m_dataNetwork; private ByteBuffer m_data; // private int m_counter; public ExecutionEngineIPC( final PartitionExecutor site, final int clusterIndex, final int siteId, final int partitionId, final int hostId, final String hostname, final BackendTarget target) { super(site); // m_counter = 0; m_clusterIndex = clusterIndex; m_siteId = siteId; m_partitionId = partitionId; m_hostId = hostId; m_hostname = hostname; // m_fser = new FastSerializer(false, false); m_connection = new Connection(target); // voltdbipc assumes host byte order everywhere m_dataNetworkOrigin = org.voltdb.utils.DBBPool.allocateDirect(1024 * 1024 * 10); m_dataNetwork = m_dataNetworkOrigin.b; m_dataNetwork.position(4); m_data = m_dataNetwork.slice(); initialize(m_clusterIndex, m_siteId, m_partitionId, m_hostId, m_hostname); } /** Utility method to generate an EEXception that can be overriden by derived classes**/ @Override protected void throwExceptionForError(final int errorCode) { try { m_connection.throwException(errorCode); } catch (final IOException e) { throw new RuntimeException(e); } } @Override public void release() throws EEException, InterruptedException { m_connection.close(); m_dataNetworkOrigin.discard(); } /** * the abstract api assumes construction initializes but here initialization * is just another command. */ public void initialize( final int clusterIndex, final int siteId, final int partitionId, final int hostId, final String hostname ) { int result = ExecutionEngine.ERRORCODE_ERROR; m_data.clear(); m_data.putInt(Commands.Initialize.m_id); m_data.putInt(clusterIndex); m_data.putInt(siteId); m_data.putInt(partitionId); m_data.putInt(hostId); m_data.putLong(EELoggers.getLogLevels()); m_data.putShort((short)hostname.length()); try { m_data.put(hostname.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } try { m_data.flip(); m_connection.write(); result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } checkErrorCode(result); } /** write the catalog as a UTF-8 byte string via connection */ @Override public void loadCatalog(final String serializedCatalog) throws EEException { int result = ExecutionEngine.ERRORCODE_ERROR; m_data.clear(); try { final byte catalogBytes[] = serializedCatalog.getBytes("UTF-8"); if (m_data.capacity() < catalogBytes.length + 100) { m_data = ByteBuffer.allocate(catalogBytes.length + 100); } m_data.putInt(Commands.LoadCatalog.m_id); m_data.put(catalogBytes); m_data.put((byte)'\0'); } catch (final UnsupportedEncodingException ex) { Logger.getLogger(ExecutionEngineIPC.class.getName()).log( Level.SEVERE, null, ex); } try { m_data.flip(); m_connection.write(); result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } checkErrorCode(result); } /** write the diffs as a UTF-8 byte string via connection */ @Override public void updateCatalog(final String catalogDiffs, int catalogVersion) throws EEException { int result = ExecutionEngine.ERRORCODE_ERROR; m_data.clear(); try { final byte catalogBytes[] = catalogDiffs.getBytes("UTF-8"); if (m_data.capacity() < catalogBytes.length + 100) { m_data = ByteBuffer.allocate(catalogBytes.length + 100); } m_data.putInt(Commands.UpdateCatalog.m_id); m_data.putInt(catalogVersion); m_data.put(catalogBytes); m_data.put((byte)'\0'); } catch (final UnsupportedEncodingException ex) { Logger.getLogger(ExecutionEngineIPC.class.getName()).log( Level.SEVERE, null, ex); } try { m_data.flip(); m_connection.write(); result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } checkErrorCode(result); } @Override public void tick(final long time, final long lastCommittedTxnId) { int result = ExecutionEngine.ERRORCODE_ERROR; m_data.clear(); m_data.putInt(Commands.Tick.m_id); m_data.putLong(time); m_data.putLong(lastCommittedTxnId); try { m_data.flip(); m_connection.write(); result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } checkErrorCode(result); // no return code for tick. } @Override public void quiesce(long lastCommittedTransactionId) { int result = ExecutionEngine.ERRORCODE_ERROR; m_data.clear(); m_data.putInt(Commands.Quiesce.m_id); m_data.putLong(lastCommittedTransactionId); try { m_data.flip(); m_connection.write(); result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Excpeption: " + e.getMessage()); throw new RuntimeException(); } checkErrorCode(result); } private void sendPlanFragmentsInvocation(final Commands cmd, final long[] planFragmentIds, final int numFragmentIds, final int[] input_depIds, final int[] output_depIds, final ParameterSet[] parameterSets, final int numParameterSets, final long txnId, final long lastCommittedTxnId, final long undoToken) { // big endian, not direct final FastSerializer fser = new FastSerializer(); try { for (int i = 0; i < numFragmentIds; ++i) { parameterSets[i].writeExternal(fser); } } catch (final IOException exception) { throw new RuntimeException(exception); } m_data.clear(); m_data.putInt(cmd.m_id); m_data.putLong(txnId); m_data.putLong(lastCommittedTxnId); m_data.putLong(undoToken); m_data.putInt(numFragmentIds); m_data.putInt(numParameterSets); for (int i = 0; i < numFragmentIds; ++i) { m_data.putLong(planFragmentIds[i]); } /** PAVLO **/ for (int i = 0; i < numFragmentIds; ++i) { m_data.putInt(input_depIds[i]); } for (int i = 0; i < numFragmentIds; ++i) { m_data.putInt(output_depIds[i]); } /** PAVLO **/ m_data.put(fser.getBuffer()); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } } @Override public DependencySet executePlanFragment(final long planFragmentId, final int outputDepId, final int inputDepId, final ParameterSet parameterSet, final long txnId, final long lastCommittedTxnId, final long undoToken) throws EEException { // big endian, not direct final FastSerializer fser = new FastSerializer(); try { parameterSet.writeExternal(fser); } catch (final IOException exception) { throw new RuntimeException(exception); } m_data.clear(); m_data.putInt(Commands.PlanFragment.m_id); m_data.putLong(txnId); m_data.putLong(lastCommittedTxnId); m_data.putLong(undoToken); m_data.putLong(planFragmentId); m_data.putInt(outputDepId); m_data.putInt(inputDepId); m_data.put(fser.getBuffer()); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { throw new EEException(result); } else { try { return m_connection.readDependencies(); } catch (final IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } @Override public VoltTable executeCustomPlanFragment(final String plan, int outputDepId, int inputDepId, final long txnId, final long lastCommittedTxnId, final long undoQuantumToken) throws EEException { final FastSerializer fser = new FastSerializer(); try { fser.writeString(plan); } catch (final IOException exception) { throw new RuntimeException(exception); } m_data.clear(); m_data.putInt(Commands.CustomPlanFragment.m_id); m_data.putLong(txnId); m_data.putLong(lastCommittedTxnId); m_data.putLong(undoQuantumToken); m_data.putInt(outputDepId); m_data.putInt(inputDepId); m_data.put(fser.getBuffer()); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result == ExecutionEngine.ERRORCODE_SUCCESS) { final VoltTable resultTables[] = new VoltTable[1]; resultTables[0] = PrivateVoltTableFactory.createUninitializedVoltTable(); try { m_connection.readResultTables(resultTables); } catch (final IOException e) { throw new EEException( ExecutionEngine.ERRORCODE_WRONG_SERIALIZED_BYTES); } return resultTables[0]; } return null; } @Override public DependencySet executeQueryPlanFragmentsAndGetDependencySet( long[] planFragmentIds, int numFragmentIds, int[] input_depIds, int[] output_depIds, ParameterSet[] parameterSets, int numParameterSets, long txnId, long lastCommittedTxnId, long undoToken) throws EEException { // TODO return (null); } // @Override // public VoltTable[] executeQueryPlanFragmentsAndGetResults( // final long[] planFragmentIds, final int numFragmentIds, // final int[] input_depIds, // final int[] output_depIds, // final ParameterSet[] parameterSets, final int numParameterSets, final long txnId, // final long lastCommittedTxnId, final long undoToken) throws EEException { // sendPlanFragmentsInvocation(Commands.QueryPlanFragments, // planFragmentIds, numFragmentIds, // input_depIds, // output_depIds, // parameterSets, // numParameterSets, txnId, lastCommittedTxnId, undoToken); // int result = ExecutionEngine.ERRORCODE_ERROR; // try { // result = m_connection.readStatusByte(); // } catch (final IOException e) { // System.out.println("Exception: " + e.getMessage()); // throw new RuntimeException(e); // } // if (result == ExecutionEngine.ERRORCODE_SUCCESS) { // final VoltTable resultTables[] = new VoltTable[numFragmentIds]; // for (int ii = 0; ii < numFragmentIds; ii++) { // resultTables[ii] = PrivateVoltTableFactory.createUninitializedVoltTable(); // } // try { // m_connection.readResultTables(resultTables); // } catch (final IOException e) { // throw new EEException( // ExecutionEngine.ERRORCODE_WRONG_SERIALIZED_BYTES); // } // return resultTables; // } // return null; // } @Override public VoltTable serializeTable(final int tableId) throws EEException { throw new UnsupportedOperationException("Not supported yet."); } /** * Unsupported implementation of toggleProfiler */ @Override public int toggleProfiler(final int toggle) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void loadTable(final int tableId, final VoltTable table, final long txnId, final long lastCommittedTxnId, final long undoToken, boolean allowExport) throws EEException { m_data.clear(); m_data.putInt(Commands.LoadTable.m_id); m_data.putInt(tableId); m_data.putLong(txnId); m_data.putLong(lastCommittedTxnId); m_data.putLong(undoToken); if(allowExport) m_data.putShort((short) 1); else m_data.putShort((short) 0); final ByteBuffer tableBytes = table.getTableDataReference(); if (m_data.remaining() < tableBytes.remaining()) { m_data.flip(); final ByteBuffer newBuffer = ByteBuffer.allocate(m_data.remaining() + tableBytes.remaining()); newBuffer.rewind(); //newBuffer.order(ByteOrder.LITTLE_ENDIAN); newBuffer.put(m_data); m_data = newBuffer; } m_data.put(tableBytes); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { throw new EEException(result); } } @Override public VoltTable[] getStats( final SysProcSelector selector, final int[] locators, final boolean interval, final Long now) { m_data.clear(); m_data.putInt(Commands.GetStats.m_id); m_data.putInt(selector.ordinal()); if (interval) { m_data.put((byte)1); } else { m_data.put((byte)0); } m_data.putLong(now); m_data.putInt(locators.length); for (final int locator : locators) { m_data.putInt(locator); } m_data.flip(); try { m_connection.write(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } try { if (result == ExecutionEngine.ERRORCODE_SUCCESS) { final ByteBuffer messageLengthBuffer = ByteBuffer.allocate(4); while (messageLengthBuffer.hasRemaining()) { int read = m_connection.m_socketChannel.read(messageLengthBuffer); if (read == -1) { throw new EOFException(); } } messageLengthBuffer.rewind(); final ByteBuffer messageBuffer = ByteBuffer.allocate(messageLengthBuffer.getInt()); while (messageBuffer.hasRemaining()) { int read = m_connection.m_socketChannel.read(messageBuffer); if (read == -1) { throw new EOFException(); } } messageBuffer.rewind(); final FastDeserializer fds = new FastDeserializer(messageBuffer); final VoltTable results[] = new VoltTable[1]; final VoltTable resultTable = PrivateVoltTableFactory.createUninitializedVoltTable(); results[0] = (VoltTable)fds.readObject(resultTable, this); return results; } } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } return null; } @Override public boolean releaseUndoToken(final long undoToken) { m_data.clear(); m_data.putInt(Commands.releaseUndoToken.m_id); m_data.putLong(undoToken); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { e.printStackTrace(); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { return false; } return true; } @Override public boolean undoUndoToken(final long undoToken) { m_data.clear(); m_data.putInt(Commands.undoUndoToken.m_id); m_data.putLong(undoToken); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { return false; } return true; } @Override public boolean setLogLevels(final long logLevels) throws EEException { m_data.clear(); m_data.putInt(Commands.SetLogLevels.m_id); m_data.putLong(logLevels); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { return false; } return true; } /** * Retrieve a dependency table and send it via the connection. If * no table is available send a response code indicating such. * The message is prepended with two lengths. One length is for * the network layer and is the size of the whole message not including * the length prefix. * @param dependencyId ID of the dependency table to send to the client */ private void sendDependencyTable(final int dependencyId) throws IOException{ final byte[] dependencyBytes = nextDependencyAsBytes(dependencyId); if (dependencyBytes == null) { m_connection.m_socket.getOutputStream().write(Connection.kErrorCode_DependencyNotFound); return; } // 1 for response code + 4 for dependency length prefix + dependencyBytes.length final ByteBuffer message = ByteBuffer.allocate(1 + 4 + dependencyBytes.length); // write the response code message.put((byte)Connection.kErrorCode_DependencyFound); // write the dependency's length prefix message.putInt(dependencyBytes.length); // finally, write dependency table itself message.put(dependencyBytes); message.rewind(); if (m_connection.m_socketChannel.write(message) != message.capacity()) { throw new IOException("Unable to send dependency table to client. Attempted blocking write of " + message.capacity() + " but not all of it was written"); } } @Override public boolean activateTableStream(int tableId, TableStreamType streamType) { m_data.clear(); m_data.putInt(Commands.ActivateTableStream.m_id); m_data.putInt(tableId); m_data.putInt(streamType.ordinal()); try { m_data.flip(); m_connection.write(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } int result = ExecutionEngine.ERRORCODE_ERROR; try { result = m_connection.readStatusByte(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } if (result != ExecutionEngine.ERRORCODE_SUCCESS) { return false; } return true; } @Override public int tableStreamSerializeMore(BBContainer c, int tableId, TableStreamType streamType) { int bytesReturned = -1; ByteBuffer view = c.b.duplicate(); try { m_data.clear(); m_data.putInt(Commands.TableStreamSerializeMore.m_id); m_data.putInt(tableId); m_data.putInt(streamType.ordinal()); m_data.putInt(c.b.remaining()); m_data.flip(); m_connection.write(); m_connection.readStatusByte(); ByteBuffer lengthBuffer = ByteBuffer.allocate(4); while (lengthBuffer.hasRemaining()) { int read = m_connection.m_socketChannel.read(lengthBuffer); if (read == -1) { throw new EOFException(); } } lengthBuffer.flip(); final int length = lengthBuffer.getInt(); bytesReturned = length; /* * Error or no more tuple data for this table. */ if (length == -1 || length == 0) { return length; } view.limit(view.position() + length); while (view.hasRemaining()) { m_connection.m_socketChannel.read(view); } } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } return bytesReturned; } @Override public ExportProtoMessage exportAction(boolean ackAction, boolean pollAction, boolean resetAction, boolean syncAction, long ackOffset, long seqNo, int partitionId, long mTableId) { try { m_data.clear(); m_data.putInt(Commands.ExportAction.m_id); m_data.putInt(ackAction ? 1 : 0); m_data.putInt(pollAction ? 1 : 0); m_data.putInt(resetAction ? 1 : 0); m_data.putInt(syncAction ? 1 : 0); m_data.putLong(ackOffset); m_data.putLong(seqNo); m_data.putLong(mTableId); m_data.flip(); m_connection.write(); ByteBuffer data = null; ByteBuffer results = ByteBuffer.allocate(8); while (results.remaining() > 0) m_connection.m_socketChannel.read(results); results.flip(); long result_offset = results.getLong(); if (result_offset < 0) { ExportProtoMessage reply = null; reply = new ExportProtoMessage(partitionId, mTableId); reply.error(); return reply; } else { results = ByteBuffer.allocate(4); while (results.remaining() > 0) m_connection.m_socketChannel.read(results); results.flip(); int result_sz = results.getInt(); data = ByteBuffer.allocate(result_sz + 4); data.putInt(result_sz); while (data.remaining() > 0) m_connection.m_socketChannel.read(data); data.flip(); ExportProtoMessage reply = null; if (pollAction) { reply = new ExportProtoMessage(partitionId, mTableId); reply.pollResponse(result_offset, data); } return reply; } } catch (final IOException e) { throw new RuntimeException(e); } } @Override public void processRecoveryMessage( ByteBuffer buffer, long pointer) { try { m_data.clear(); m_data.putInt(Commands.RecoveryMessage.m_id); m_data.putInt(buffer.remaining()); m_data.put(buffer); m_data.flip(); m_connection.write(); m_connection.readStatusByte(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } } @Override public long tableHashCode(int tableId) { try { m_data.clear(); m_data.putInt(Commands.TableHashCode.m_id); m_data.putInt(tableId); m_data.flip(); m_connection.write(); m_connection.readStatusByte(); ByteBuffer hashCode = ByteBuffer.allocate(8); while (hashCode.hasRemaining()) { int read = m_connection.m_socketChannel.read(hashCode); if (read <= 0) { throw new EOFException(); } } hashCode.flip(); return hashCode.getLong(); } catch (final IOException e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } } @Override public int hashinate(Object value, int partitionCount) { ParameterSet parameterSet = new ParameterSet(true); parameterSet.setParameters(value); final FastSerializer fser = new FastSerializer(); try { parameterSet.writeExternal(fser); } catch (final IOException exception) { throw new RuntimeException(exception); } m_data.clear(); m_data.putInt(Commands.Hashinate.m_id); m_data.putInt(partitionCount); m_data.put(fser.getBuffer()); try { m_data.flip(); m_connection.write(); m_connection.readStatusByte(); ByteBuffer part = ByteBuffer.allocate(4); while (part.hasRemaining()) { int read = m_connection.m_socketChannel.read(part); if (read <= 0) { throw new EOFException(); } } part.flip(); return part.getInt(); } catch (final Exception e) { System.out.println("Exception: " + e.getMessage()); throw new RuntimeException(e); } } @Override public void trackingEnable(Long txnId) throws EEException { throw new NotImplementedException("Read/Write Set Tracking is disabled for IPC ExecutionEngine"); } @Override public void trackingFinish(Long txnId) throws EEException { throw new NotImplementedException("Read/Write Set Tracking is disabled for IPC ExecutionEngine"); } @Override public VoltTable trackingReadSet(Long txnId) throws EEException { throw new NotImplementedException("Read/Write Set Tracking is disabled for IPC ExecutionEngine"); } @Override public VoltTable trackingWriteSet(Long txnId) throws EEException { throw new NotImplementedException("Read/Write Set Tracking is disabled for IPC ExecutionEngine"); } @Override public void antiCacheInitialize(File dbFilePath, AntiCacheDBType dbType, boolean blocking, long blockSize, long maxSize, boolean blockMerge) throws EEException { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public void antiCacheAddDB(File dbFilePath, AntiCacheDBType dbType, boolean blocking, long blockSize, long maxSize, boolean blockMerge) throws EEException { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public void antiCacheReadBlocks(Table catalog_tbl, int[] block_ids, int[] tuple_offsets) { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public void antiCacheMergeBlocks(Table catalog_tbl) { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public VoltTable antiCacheEvictBlock(Table catalog_tbl, long block_size, int num_blocks) { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public VoltTable antiCacheEvictBlockInBatch(Table catalog_tbl, Table child_tbl, long block_size, int num_blocks) { throw new NotImplementedException("Anti-Caching is disabled for IPC ExecutionEngine"); } @Override public void MMAPInitialize(File dbDir, long mapSize, long syncFrequency) throws EEException { throw new NotImplementedException("Storage MMAP is disabled for IPC ExecutionEngine"); } // ARIES @Override public void ARIESInitialize(File dbDir, File logFile) throws EEException { throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } @Override public long getArieslogBufferLength() { // XXX: do nothing, we only implement this for JNI now. throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } @Override public void getArieslogData(int bufferLength, byte[] arieslogDataArray) { throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } @Override public void doAriesRecoveryPhase(long replayPointer, long replayLogSize, long replayTxnId) { throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } @Override public void freePointerToReplayLog(long ariesReplayPointer) { throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } @Override public long readAriesLogForReplay(long[] size) { throw new NotImplementedException("ARIES recovery is disabled for IPC ExecutionEngine"); } }