/* 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.ArrayList;
import org.voltcore.logging.VoltLogger;
import org.voltdb.CommandLog;
import org.voltdb.CommandLog.DurabilityListener;
import org.voltdb.iv2.SpScheduler.DurableUniqueIdListener;
import org.voltdb.utils.MiscUtils;
import org.voltdb.utils.VoltTrace;
/**
* This class is not thread-safe. Most of its usage is on the Site thread.
*/
public class SpDurabilityListener implements DurabilityListener {
private static final VoltLogger log = new VoltLogger("LOGGING");
// No command logging
class NoCompletionChecks implements CommandLog.CompletionChecks {
NoCompletionChecks() {}
public CommandLog.CompletionChecks startNewCheckList(int startSize) {
return this;
}
@Override
public void addTask(TransactionTask task) {}
@Override
public void setLastDurableUniqueId(long uniqueId) {}
@Override
public boolean isChanged() {
return false;
}
@Override
public int getTaskListSize() {
return 0;
}
@Override
public void processChecks() {}
}
class AsyncCompletionChecks implements CommandLog.CompletionChecks {
protected long m_lastSpUniqueId;
protected long m_lastMpUniqueId;
protected boolean m_changed = false;
AsyncCompletionChecks(long lastSpUniqueId, long lastMpUniqueId) {
m_lastSpUniqueId = lastSpUniqueId;
m_lastMpUniqueId = lastMpUniqueId;
}
@Override
public CommandLog.CompletionChecks startNewCheckList(int startSize) {
return new AsyncCompletionChecks(m_lastSpUniqueId, m_lastMpUniqueId);
}
@Override
public void addTask(TransactionTask task) {
if (!task.m_txnState.isReadOnly()) {
setLastDurableUniqueId(task.m_txnState.uniqueId);
}
}
@Override
public void setLastDurableUniqueId(long uniqueId) {
if (UniqueIdGenerator.getPartitionIdFromUniqueId(uniqueId) == MpInitiator.MP_INIT_PID) {
assert m_lastMpUniqueId <= uniqueId;
m_lastMpUniqueId = uniqueId;
}
else {
assert m_lastSpUniqueId <= uniqueId;
m_lastSpUniqueId = uniqueId;
}
m_changed = true;
}
@Override
public boolean isChanged() {
return m_changed;
}
@Override
public int getTaskListSize() {
return 0;
}
@Override
public void processChecks() {
if (m_changed) {
if (log.isTraceEnabled()) {
log.trace("Notifying of last made durable: SP " + UniqueIdGenerator.toShortString(m_lastSpUniqueId) +
", MP " + UniqueIdGenerator.toShortString(m_lastMpUniqueId));
}
// Notify the SP UniqueId listeners
for (DurableUniqueIdListener listener : m_uniqueIdListeners) {
listener.lastUniqueIdsMadeDurable(m_lastSpUniqueId, m_lastMpUniqueId);
}
}
}
}
class SyncCompletionChecks extends AsyncCompletionChecks {
ArrayList<TransactionTask> m_pendingTransactions;
public SyncCompletionChecks(long lastSpUniqueId, long lastMpUniqueId, int startSize) {
super(lastSpUniqueId, lastMpUniqueId);
m_pendingTransactions = new ArrayList<TransactionTask>(startSize);
}
@Override
public CommandLog.CompletionChecks startNewCheckList(int startSize) {
return new SyncCompletionChecks(m_lastSpUniqueId, m_lastMpUniqueId, startSize);
}
@Override
public void addTask(TransactionTask task) {
m_pendingTransactions.add(task);
super.addTask(task);
}
@Override
public boolean isChanged() {
return !m_pendingTransactions.isEmpty();
}
@Override
public int getTaskListSize() {
return m_pendingTransactions.size();
}
private void queuePendingTasks() {
// Notify all sync transactions and the SP UniqueId listeners
for (TransactionTask o : m_pendingTransactions) {
final VoltTrace.TraceEventBatch traceLog = VoltTrace.log(VoltTrace.Category.SPI);
if (traceLog != null) {
traceLog.add(() -> VoltTrace.endAsync("durability",
MiscUtils.hsIdTxnIdToString(m_spScheduler.m_mailbox.getHSId(),
o.getSpHandle())));
}
m_pendingTasks.offer(o);
// Make sure all queued tasks for this MP txn are released
if (!o.getTransactionState().isSinglePartition()) {
m_spScheduler.offerPendingMPTasks(o.getTxnId());
}
}
}
@Override
public void processChecks() {
queuePendingTasks();
super.processChecks();
}
}
private CommandLog.CompletionChecks m_currentCompletionChecks = null;
private final SpScheduler m_spScheduler;
private final TransactionTaskQueue m_pendingTasks;
private boolean m_commandLoggingEnabled;
private final ArrayList<DurableUniqueIdListener> m_uniqueIdListeners = new ArrayList<DurableUniqueIdListener>(2);
public SpDurabilityListener(SpScheduler spScheduler, TransactionTaskQueue pendingTasks) {
m_spScheduler = spScheduler;
m_pendingTasks = pendingTasks;
}
@Override
public void setUniqueIdListener(DurableUniqueIdListener listener) {
m_uniqueIdListeners.add(listener);
if (m_currentCompletionChecks != null && !m_commandLoggingEnabled) {
// Since command logging is disabled set the durable uniqueId to maxLong
listener.lastUniqueIdsMadeDurable(Long.MAX_VALUE, Long.MAX_VALUE);
}
}
@Override
public void addTransaction(TransactionTask pendingTask) {
m_currentCompletionChecks.addTask(pendingTask);
}
@Override
public void initializeLastDurableUniqueId(long uniqueId) {
m_currentCompletionChecks.setLastDurableUniqueId(uniqueId);
}
@Override
public CommandLog.CompletionChecks startNewTaskList(int nextStartTaskListSize) {
CommandLog.CompletionChecks lastChecks = m_currentCompletionChecks;
m_currentCompletionChecks = m_currentCompletionChecks.startNewCheckList(nextStartTaskListSize);
return lastChecks;
}
@Override
public int getNumberOfTasks() {
return m_currentCompletionChecks.getTaskListSize();
}
@Override
public void createFirstCompletionCheck(boolean isSyncLogging, boolean commandLoggingEnabled) {
m_commandLoggingEnabled = commandLoggingEnabled;
if (!commandLoggingEnabled) {
m_currentCompletionChecks = new NoCompletionChecks();
// Since command logging is disabled set the durable uniqueId to maxLong
for (DurableUniqueIdListener listener : m_uniqueIdListeners) {
listener.lastUniqueIdsMadeDurable(Long.MAX_VALUE, Long.MAX_VALUE);
}
}
else
if (isSyncLogging) {
m_currentCompletionChecks = new SyncCompletionChecks(Long.MIN_VALUE, Long.MIN_VALUE, 16);
}
else {
m_currentCompletionChecks = new AsyncCompletionChecks(Long.MIN_VALUE, Long.MIN_VALUE);
}
}
@Override
public boolean completionCheckInitialized() {
return (m_currentCompletionChecks != null);
}
@Override
public void processDurabilityChecks(CommandLog.CompletionChecks completionChecks) {
if (completionChecks.isChanged()) {
m_spScheduler.processDurabilityChecks(completionChecks);
}
}
}