org.voltdb.SimpleFileSnapshotDataTarget Maven / Gradle / Ivy
/* This file is part of VoltDB.
* Copyright (C) 2008-2018 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 .
*/
package org.voltdb;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.voltcore.logging.VoltLogger;
import org.voltcore.utils.Bits;
import org.voltcore.utils.CoreUtils;
import org.voltcore.utils.DBBPool.BBContainer;
import com.google_voltpatches.common.base.Throwables;
import com.google_voltpatches.common.util.concurrent.ListenableFuture;
import com.google_voltpatches.common.util.concurrent.ListeningExecutorService;
public class SimpleFileSnapshotDataTarget implements SnapshotDataTarget {
private static final VoltLogger SNAP_LOG = new VoltLogger("SNAPSHOT");
private final File m_tempFile;
private final File m_file;
private final FileChannel m_fc;
private final RandomAccessFile m_ras;
private long m_bytesWritten = 0;
private Runnable m_onCloseTask;
private boolean m_needsFinalClose;
/*
* Remember to sync regularly. SimpleFileSnapshotDataTarget
* takes a simpler approach to bounding the number of bytes synced
* and keeping the disk working on a regular basis.
*
* The two roles are split, one thread syncs periodically to keep the disk busy
* and the main thread writing will only stop on syncing if it has written
* 256 megabytes past what the sync thread is doing
*
* This removes the messy coordination you see in DefaultSnapshotDataTarget
* where the two threads use a semaphore to keep track of permits
*/
private static final int m_bytesAllowedBeforeSync = (1024 * 1024) * 256;
private AtomicInteger m_bytesSinceLastSync = new AtomicInteger(0);
private final ScheduledFuture> m_syncTask;
/*
* If a write fails then this snapshot is hosed.
* Set the flag so all writes return immediately. The system still
* needs to scan all the tables to clear the dirty bits
* so the process continues as if the writes are succeeding.
* A more efficient failure mode would do the scan but not the
* extra serialization work.
*/
private volatile boolean m_writeFailed = false;
private volatile Throwable m_writeException = null;
private volatile IOException m_reportedSerializationFailure = null;
public SimpleFileSnapshotDataTarget(
File file, boolean needsFinalClose) throws IOException {
m_file = file;
m_tempFile = new File(m_file.getParentFile(), m_file.getName() + ".incomplete");
m_ras = new RandomAccessFile(m_tempFile, "rw");
m_fc = m_ras.getChannel();
m_needsFinalClose = needsFinalClose;
m_es = CoreUtils.getListeningSingleThreadExecutor("Snapshot write thread for " + m_file);
ScheduledFuture> syncTask = null;
syncTask = DefaultSnapshotDataTarget.m_syncService.scheduleAtFixedRate(new Runnable() {
private long syncedBytes = 0;
@Override
public void run() {
//Only sync for at least 4 megabyte of data, enough to amortize the cost of seeking
//on ye olden platters. Since we are appending to a file it's actually 2 seeks.
while (m_bytesSinceLastSync.get() > 1024 * 1024 * 4) {
try {
final long syncStart = syncedBytes;
syncedBytes = Bits.sync_file_range(SNAP_LOG, m_ras.getFD(), m_fc, syncStart, m_fc.position());
} catch (IOException e) {
if (!(e instanceof java.nio.channels.AsynchronousCloseException )) {
SNAP_LOG.error("Error syncing snapshot", e);
} else {
SNAP_LOG.debug("Asynchronous close syncing snapshot data, presumably graceful", e);
}
}
//Blind setting to 0 means we could technically write more than
//256 megabytes at a time but 512 is the worst case and that is fine
m_bytesSinceLastSync.set(0);
}
}
}, DefaultSnapshotDataTarget.SNAPSHOT_SYNC_FREQUENCY, DefaultSnapshotDataTarget.SNAPSHOT_SYNC_FREQUENCY, TimeUnit.MILLISECONDS);
m_syncTask = syncTask;
}
private final ListeningExecutorService m_es;
@Override
public int getHeaderSize() {
return 0;
}
@Override
public ListenableFuture> write(final Callable tupleData, int tableId) {
final ListenableFuture computedData = VoltDB.instance().getComputationService().submit(tupleData);
return m_es.submit(new Callable