/* 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.sysprocs.saverestore; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.voltdb.VoltTable; import org.voltdb.VoltTable.ColumnInfo; import org.voltdb.VoltTableRow; import org.voltdb.VoltType; public class ClusterSaveFileState { public static VoltTable constructEmptySaveFileStateVoltTable() { ColumnInfo[] result_columns = new ColumnInfo[11]; int ii = 0; result_columns[ii++] = new ColumnInfo("CURRENT_HOST_ID", VoltType.INTEGER); result_columns[ii++] = new ColumnInfo("CURRENT_HOSTNAME", VoltType.STRING); result_columns[ii++] = new ColumnInfo("ORIGINAL_HOST_ID", VoltType.INTEGER); result_columns[ii++] = new ColumnInfo("ORIGINAL_HOSTNAME", VoltType.STRING); result_columns[ii++] = new ColumnInfo("CLUSTER", VoltType.STRING); result_columns[ii++] = new ColumnInfo("DATABASE", VoltType.STRING); result_columns[ii++] = new ColumnInfo("TABLE", VoltType.STRING); result_columns[ii++] = new ColumnInfo("TXNID", VoltType.BIGINT); result_columns[ii++] = new ColumnInfo("IS_REPLICATED", VoltType.STRING); result_columns[ii++] = new ColumnInfo("PARTITION", VoltType.INTEGER); result_columns[ii++] = new ColumnInfo("TOTAL_PARTITIONS", VoltType.INTEGER); return new VoltTable(result_columns); } private TableSaveFileState constructTableState( VoltTableRow row) { TableSaveFileState table_state = null; String table_name = row.getString("TABLE"); long txnId = row.getLong("TXNID"); if (row.getString("IS_REPLICATED").equals("TRUE")) { table_state = new ReplicatedTableSaveFileState(table_name, txnId); } else if (row.getString("IS_REPLICATED").equals("FALSE")) { table_state = new PartitionedTableSaveFileState(table_name, txnId); } else { // XXX not reached assert(false); } return table_state; } public ClusterSaveFileState(VoltTable saveFileState) throws IOException { // Checks cluster/database name consistency between rows. ConsistencyChecker checker = new ConsistencyChecker(); m_tableStateMap = new HashMap<String, TableSaveFileState>(); long txnId = -1; while (saveFileState.advanceRow()) { checker.checkRow(saveFileState); // throws if inconsistent String table_name = saveFileState.getString("TABLE"); // Check if the transaction IDs match if (txnId == -1) { txnId = saveFileState.getLong("TXNID"); } else if (txnId != saveFileState.getLong("TXNID")) { String error = "Table: " + table_name + " has inconsistent" + " transaction ID "; throw new IOException(error); } TableSaveFileState table_state = null; if (!(getSavedTableNames().contains(table_name))) { table_state = constructTableState(saveFileState); m_tableStateMap.put(table_name, table_state); } table_state = getTableState(table_name); table_state.addHostData(saveFileState); // throws if inconsistent } for (TableSaveFileState table_state : m_tableStateMap.values()) { if (!table_state.isConsistent()) { String error = table_state.getConsistencyResult(); throw new IOException(error); } } } public Set<String> getSavedTableNames() { return m_tableStateMap.keySet(); } public TableSaveFileState getTableState(String tableName) { return m_tableStateMap.get(tableName); } private static class ConsistencyChecker { private int m_numRows = 0; private String m_clusterName = null; private String m_databaseName = null; private void checkRow(VoltTableRow row) throws IOException { // Get the comparison cluster/database names from the first row received. if (m_numRows++ == 0) { m_clusterName = row.getString("CLUSTER"); m_databaseName = row.getString("DATABASE"); } else if (!row.getString("CLUSTER").equals(m_clusterName)) { String error = "Site: " + row.getLong("CURRENT_HOST_ID") + ", Table: " + row.getString("TABLE") + " has an inconsistent " + "cluster name: " + row.getString("CLUSTER") + " (previous was: " + m_clusterName + ")."; throw new IOException(error); } else if (!row.getString("DATABASE").equals(m_databaseName)) { String error = "Site: " + row.getLong("CURRENT_HOST_ID") + ", Table: " + row.getString("TABLE") + " has an inconsistent " + "database name: " + row.getString("DATABASE") + " (previous was: " + m_databaseName + ")."; throw new IOException(error); } } } private Map<String, TableSaveFileState> m_tableStateMap; }