@Override public void close() throws IOException, InterruptedException { try { m_es.shutdown(); m_es.awaitTermination(356, TimeUnit.DAYS); m_syncTask.cancel(false); m_fc.force(false); m_fc.close(); m_tempFile.renameTo(m_file); } finally { m_onCloseTask.run(); } }
@Override public ListenableFuture<?> write(final Callable<BBContainer> tupleData, int tableId) { final ListenableFuture<BBContainer> computedData = VoltDB.instance().getComputationService().submit(tupleData); return m_es.submit( new Callable<Object>() { @Override public Object call() throws Exception { try { final BBContainer data = computedData.get(); /* * If a filter nulled out the buffer do nothing. */ if (data == null) return null; if (m_writeFailed) { data.discard(); return null; } try { int totalWritten = 0; final ByteBuffer dataBuf = data.b(); DefaultSnapshotDataTarget.enforceSnapshotRateLimit(dataBuf.remaining()); while (dataBuf.hasRemaining()) { int written = m_fc.write(dataBuf); if (written > 0) { m_bytesWritten += written; totalWritten += written; } } if (m_bytesSinceLastSync.addAndGet(totalWritten) > m_bytesAllowedBeforeSync) { m_fc.force(false); m_bytesSinceLastSync.set(0); } } finally { data.discard(); } } catch (Throwable t) { m_writeException = t; m_writeFailed = true; throw Throwables.propagate(t); } return null; } }); }
/* * Prepend length is basically synonymous with writing actual tuple data and not * the header. */ private ListenableFuture<?> write( final Callable<BBContainer> tupleDataC, final boolean prependLength) { /* * Unwrap the data to be written. For the traditional * snapshot data target this should be a noop. */ BBContainer tupleDataTemp; try { tupleDataTemp = tupleDataC.call(); /* * Can be null if the dedupe filter nulled out the buffer */ if (tupleDataTemp == null) { return Futures.immediateFuture(null); } } catch (Throwable t) { return Futures.immediateFailedFuture(t); } final BBContainer tupleData = tupleDataTemp; if (m_writeFailed) { tupleData.discard(); return null; } m_outstandingWriteTasks.incrementAndGet(); Future<BBContainer> compressionTask = null; if (prependLength) { BBContainer cont = DBBPool.allocateDirectAndPool(SnapshotSiteProcessor.m_snapshotBufferCompressedLen); // Skip 4-bytes so the partition ID is not compressed // That way if we detect a corruption we know what partition is bad tupleData.b.position(tupleData.b.position() + 4); /* * Leave 12 bytes, it's going to be a 4-byte length prefix, a 4-byte partition id, * and a 4-byte CRC32C of just the header bytes, in addition to the compressed payload CRC * that is 16 bytes, but 4 of those are done by CompressionService */ cont.b.position(12); compressionTask = CompressionService.compressAndCRC32cBufferAsync(tupleData.b, cont); } final Future<BBContainer> compressionTaskFinal = compressionTask; ListenableFuture<?> writeTask = m_es.submit( new Callable<Object>() { @Override public Object call() throws Exception { try { if (m_acceptOneWrite) { m_acceptOneWrite = false; } else { if (m_simulateBlockedWrite != null) { m_simulateBlockedWrite.await(); } if (m_simulateFullDiskWritingChunk) { throw new IOException("Disk full"); } } int totalWritten = 0; if (prependLength) { BBContainer payloadContainer = compressionTaskFinal.get(); try { final ByteBuffer payloadBuffer = payloadContainer.b; payloadBuffer.position(0); ByteBuffer lengthPrefix = ByteBuffer.allocate(12); m_bytesAllowedBeforeSync.acquire(payloadBuffer.remaining()); // Length prefix does not include 4 header items, just compressd payload // that follows lengthPrefix.putInt(payloadBuffer.remaining() - 16); // length prefix lengthPrefix.putInt(tupleData.b.getInt(0)); // partitionId /* * Checksum the header and put it in the payload buffer */ PureJavaCrc32C crc = new PureJavaCrc32C(); crc.update(lengthPrefix.array(), 0, 8); lengthPrefix.putInt((int) crc.getValue()); lengthPrefix.flip(); payloadBuffer.put(lengthPrefix); payloadBuffer.position(0); /* * Write payload to file */ while (payloadBuffer.hasRemaining()) { totalWritten += m_channel.write(payloadBuffer); } } finally { payloadContainer.discard(); } } else { while (tupleData.b.hasRemaining()) { totalWritten += m_channel.write(tupleData.b); } } m_bytesWritten += totalWritten; m_bytesWrittenSinceLastSync.addAndGet(totalWritten); } catch (IOException e) { m_writeException = e; SNAP_LOG.error( "Error while attempting to write snapshot data to file " + m_file, e); m_writeFailed = true; throw e; } finally { try { tupleData.discard(); } finally { m_outstandingWriteTasksLock.lock(); try { if (m_outstandingWriteTasks.decrementAndGet() == 0) { m_noMoreOutstandingWriteTasksCondition.signalAll(); } } finally { m_outstandingWriteTasksLock.unlock(); } } } return null; } }); return writeTask; }