/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.voltdb;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import org.junit.Test;
import org.voltdb.VoltDB.Configuration;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ProcCallException;
import org.voltdb.compiler.VoltCompiler;
import org.voltdb.compiler.VoltProjectBuilder;
import org.voltdb.utils.InMemoryJarfile;
import org.voltdb.utils.MiscUtils;
public class TestLiveDDLSchemaSwitch extends AdhocDDLTestBase {
// Test cases:
// 1) Configure for master and UAC
// - Verify UAC works
// - Verify deployment-only UAC works
// - Verify Adhoc DDL fails
// - Verify adhoc query succeeds
// 2) Configure for master and Adhoc DDL
// - Verify UAC fails
// - Verify deployment-only UAC works
// - Verify Adhoc DDL succeeds
// - Verify adhoc query succeeds
// 3) Configure for replica and UAC
// - Verify failures (dunno how to fabricate "replicated" txns
// - Verify adhoc queries work
// - Promote cluster
// - Re-verify (1)
// 4) Configure for replica and adhoc DDL
// - Verify failures
// - Verify adhoc queries
// - Promote
// - Re-verify (2)
String m_pathToCatalog;
String m_pathToDeployment;
String m_pathToReplicaDeployment;
String m_pathToOtherCatalog;
String m_pathToOtherDeployment;
String m_pathToOtherReplicaDeployment;
void generateCatalogsAndDeployments(boolean useLiveDDL) throws Exception
{
m_pathToCatalog = Configuration.getPathToCatalogForTest("adhocddl.jar");
m_pathToDeployment = Configuration.getPathToCatalogForTest("adhocddl.xml");
m_pathToReplicaDeployment = Configuration.getPathToCatalogForTest("replicaadhocddl.xml");
m_pathToOtherCatalog = Configuration.getPathToCatalogForTest("newadhocddl.jar");
m_pathToOtherDeployment = Configuration.getPathToCatalogForTest("newadhocddl.xml");
m_pathToOtherReplicaDeployment = Configuration.getPathToCatalogForTest("newreplicaadhocddl.xml");
VoltProjectBuilder builder = new VoltProjectBuilder();
builder.addLiteralSchema(
"create table FOO (" +
"ID integer not null," +
"VAL bigint, " +
"constraint PK_TREE primary key (ID)" +
");\n" +
"create table FOO_R (" +
"ID integer not null," +
"VAL bigint, " +
"constraint PK_TREE_R primary key (ID)" +
");\n"
);
builder.addPartitionInfo("FOO", "ID");
builder.setUseDDLSchema(useLiveDDL);
builder.setDRMasterHost("localhost"); // fake DR connection so that replica can start
boolean success = builder.compile(m_pathToCatalog, 2, 1, 0);
assertTrue("Schema compilation failed", success);
MiscUtils.copyFile(builder.getPathToDeployment(), m_pathToDeployment);
builder.setDrReplica();
success = builder.compile(m_pathToCatalog, 2, 1, 0);
assertTrue("Schema compilation failed", success);
MiscUtils.copyFile(builder.getPathToDeployment(), m_pathToReplicaDeployment);
// get an alternate deployment file
builder = new VoltProjectBuilder();
builder.addLiteralSchema(
"create table BAZ (" +
"ID integer not null," +
"VAL bigint, " +
"constraint PK_TREE primary key (ID)" +
");\n" +
"create table FOO_R (" +
"ID integer not null," +
"VAL bigint, " +
"constraint PK_TREE_R primary key (ID)" +
");\n"
);
builder.addPartitionInfo("BAZ", "ID");
builder.setUseDDLSchema(useLiveDDL);
builder.setDRMasterHost("localhost");
builder.setDeadHostTimeout(6);
success = builder.compile(m_pathToOtherCatalog, 2, 1, 0);
assertTrue("2nd schema compilation failed", success);
MiscUtils.copyFile(builder.getPathToDeployment(), m_pathToOtherDeployment);
builder.setDrReplica();
success = builder.compile(m_pathToOtherCatalog, 2, 1, 0);
assertTrue("2nd schema compilation failed", success);
MiscUtils.copyFile(builder.getPathToDeployment(), m_pathToOtherReplicaDeployment);
}
int getHeartbeatTimeout() throws Exception
{
boolean found = false;
int timeout = -1;
VoltTable result = m_client.callProcedure("@SystemInformation", "DEPLOYMENT").getResults()[0];
while (result.advanceRow()) {
if (result.getString("PROPERTY").equalsIgnoreCase("heartbeattimeout")) {
found = true;
timeout = Integer.valueOf(result.getString("VALUE"));
}
}
assertTrue(found);
return timeout;
}
void verifyDeploymentOnlyUAC() throws Exception
{
int timeout = getHeartbeatTimeout();
assertEquals(org.voltcore.common.Constants.DEFAULT_HEARTBEAT_TIMEOUT_SECONDS, timeout);
ClientResponse results =
m_client.updateApplicationCatalog(null, new File(m_pathToOtherDeployment));
assertEquals(ClientResponse.SUCCESS, results.getStatus());
timeout = getHeartbeatTimeout();
assertEquals(6, timeout);
}
void verifyAdhocQuery() throws Exception
{
ClientResponse result = m_client.callProcedure("@AdHoc", "select * from foo;");
assertEquals(ClientResponse.SUCCESS, result.getStatus());
}
// GOing to want to retest this after we promote a replica, so bust it out
void verifyMasterWithUAC() throws Exception
{
// UAC should work.
ClientResponse results = m_client.updateApplicationCatalog(new File(m_pathToCatalog), null);
assertEquals(ClientResponse.SUCCESS, results.getStatus());
assertTrue(findTableInSystemCatalogResults("FOO"));
// Adhoc DDL should be rejected
assertFalse(findTableInSystemCatalogResults("BAR"));
boolean threw = false;
try {
results = m_client.callProcedure("@AdHoc",
"create table BAR (ID integer, VAL varchar(50));");
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("AdHoc DDL is forbidden"));
}
assertTrue("Adhoc DDL should have failed", threw);
assertFalse(findTableInSystemCatalogResults("BAR"));
// @UpdateClasses should be rejected
assertFalse(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
threw = false;
try {
InMemoryJarfile jarfile = new InMemoryJarfile();
VoltCompiler comp = new VoltCompiler(false);
comp.addClassToJar(jarfile, org.voltdb_testprocs.fullddlfeatures.testImportProc.class);
m_client.callProcedure("@UpdateClasses", jarfile.getFullJarBytes(), null);
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("@UpdateClasses is forbidden"));
}
assertTrue("@UpdateClasses should have failed", threw);
assertFalse(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
verifyAdhocQuery();
}
@Test
public void testMasterWithUAC() throws Exception
{
generateCatalogsAndDeployments(false);
// Fire up a cluster with no catalog
VoltDB.Configuration config = new VoltDB.Configuration();
config.m_pathToDeployment = m_pathToDeployment;
try {
startSystem(config);
verifyDeploymentOnlyUAC();
verifyMasterWithUAC();
}
finally {
teardownSystem();
}
}
void verifyMasterWithAdhocDDL() throws Exception
{
// UAC with schema should fail
assertFalse(findTableInSystemCatalogResults("FOO"));
boolean threw = false;
try {
m_client.updateApplicationCatalog(new File(m_pathToCatalog), null);
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("Use of @UpdateApplicationCatalog is forbidden"));
}
assertTrue("@UAC should have failed", threw);
assertFalse(findTableInSystemCatalogResults("FOO"));
// But, we can create that table with Adhoc DDL
try {
m_client.callProcedure("@AdHoc",
"create table FOO (ID integer, VAL varchar(50));");
}
catch (ProcCallException pce) {
fail("Should be able to use Adhoc DDL to create a table.");
}
assertTrue(findTableInSystemCatalogResults("FOO"));
// And so should adhoc queries
verifyAdhocQuery();
// If the procedure doesn't already exist, add it using @UpdateClasses
if (!findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc")) {
// Also, @UpdateClasses should only work with adhoc DDL
InMemoryJarfile jarfile = new InMemoryJarfile();
VoltCompiler comp = new VoltCompiler(false);
comp.addClassToJar(jarfile, org.voltdb_testprocs.fullddlfeatures.testImportProc.class);
try {
m_client.callProcedure("@UpdateClasses", jarfile.getFullJarBytes(), null);
} catch (ProcCallException pce) {
fail("Should be able to call @UpdateClasses when adhoc DDL enabled.");
}
assertTrue(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
}
}
@Test
public void testMasterWithAdhocDDL() throws Exception
{
generateCatalogsAndDeployments(true);
// Fire up a cluster with no catalog
VoltDB.Configuration config = new VoltDB.Configuration();
config.m_pathToDeployment = m_pathToDeployment;
try {
startSystem(config);
// Deployment-only UAC should work, though
verifyDeploymentOnlyUAC();
verifyMasterWithAdhocDDL();
}
finally {
teardownSystem();
}
}
@Test
public void testReplicaWithUAC() throws Exception
{
generateCatalogsAndDeployments(false);
// Fire up a cluster with no catalog
VoltDB.Configuration config = new VoltDB.Configuration();
config.m_pathToCatalog = m_pathToOtherCatalog;
config.m_pathToDeployment = m_pathToReplicaDeployment;
try {
startSystem(config);
// UAC with schema should succeed
assertFalse(findTableInSystemCatalogResults("FOO"));
boolean threw = false;
try {
m_client.updateApplicationCatalog(new File(m_pathToCatalog), null);
}
catch (ProcCallException pce) {
threw = true;
}
assertFalse("@UAC add table should be accepted on the consumer cluster", threw);
assertTrue(findTableInSystemCatalogResults("FOO"));
// deployment-only UAC should succeed
threw = false;
try {
m_client.updateApplicationCatalog(null, new File(m_pathToOtherReplicaDeployment));
}
catch (ProcCallException pce) {
threw = true;
}
assertFalse("@UAC to new catalog on consumer cluster should have succeed", threw);
assertEquals(getHeartbeatTimeout(), 6);
// Adhoc DDL should be rejected
assertFalse(findTableInSystemCatalogResults("BAR"));
threw = false;
try {
m_client.callProcedure("@AdHoc",
"create table BAR (ID integer, VAL varchar(50));");
}
catch (ProcCallException pce) {
threw = true;
System.out.println(pce.getMessage());
assertTrue(pce.getMessage().contains("AdHoc DDL is forbidden"));
}
assertTrue("Adhoc DDL should have failed", threw);
assertFalse(findTableInSystemCatalogResults("BAR"));
// @UpdateClasses (which is an AdHoc capability) should be rejected
assertFalse(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
threw = false;
try {
InMemoryJarfile jarfile = new InMemoryJarfile();
VoltCompiler comp = new VoltCompiler(false);
comp.addClassToJar(jarfile, org.voltdb_testprocs.fullddlfeatures.testImportProc.class);
m_client.callProcedure("@UpdateClasses", jarfile.getFullJarBytes(), null);
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("@UpdateClasses is forbidden"));
}
assertTrue("@UpdateClasses should have failed", threw);
assertFalse(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
// Promote, should behave like the original master test
m_client.callProcedure("@Promote");
verifyMasterWithUAC();
}
finally {
teardownSystem();
}
}
@Test
public void testReplicaWithAdhocDDL() throws Exception
{
generateCatalogsAndDeployments(true);
// Fire up a cluster with no catalog
VoltDB.Configuration config = new VoltDB.Configuration();
config.m_pathToCatalog = m_pathToOtherCatalog;
config.m_pathToDeployment = m_pathToReplicaDeployment;
try {
startSystem(config);
// UAC with schema should fail
assertFalse(findTableInSystemCatalogResults("FOO"));
boolean threw = false;
try {
m_client.updateApplicationCatalog(new File(m_pathToCatalog), null);
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("Cluster is configured to use AdHoc DDL"));
}
assertTrue("@UAC should have failed", threw);
assertFalse(findTableInSystemCatalogResults("FOO"));
// deployment-only UAC should fail
threw = false;
try {
m_client.updateApplicationCatalog(null, new File(m_pathToOtherReplicaDeployment));
}
catch (ProcCallException pce) {
threw = true;
}
assertFalse("@UAC should should succeed with just a deployment file", threw);
assertEquals(getHeartbeatTimeout(), 6);
// Adhoc DDL should be rejected
assertFalse(findTableInSystemCatalogResults("BAR"));
try {
m_client.callProcedure("@AdHoc",
"create table BAR (ID integer, VAL varchar(50));");
}
catch (ProcCallException pce) {
fail("@AdHoc should succeed on replica cluster");
}
assertTrue(findTableInSystemCatalogResults("BAR"));
// Adhoc DML updates should be rejected in the replica
threw = false;
try {
m_client.callProcedure("@AdHoc", "insert into BAR values (100, 'ABC');");
}
catch (ProcCallException pce) {
threw = true;
System.out.println(pce.getMessage());
assertTrue(pce.getMessage().contains("Write procedure @AdHoc_RW_MP is not allowed in replica cluster"));
}
assertTrue("Adhoc DDL should have failed", threw);
// @UpdateClasses should be rejected
assertFalse(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
threw = false;
try {
InMemoryJarfile jarfile = new InMemoryJarfile();
VoltCompiler comp = new VoltCompiler(false);
comp.addClassToJar(jarfile, org.voltdb_testprocs.fullddlfeatures.testImportProc.class);
m_client.callProcedure("@UpdateClasses", jarfile.getFullJarBytes(), null);
}
catch (ProcCallException pce) {
threw = true;
assertTrue(pce.getMessage().contains("Write procedure @UpdateClasses is not allowed"));
}
assertFalse("@UpdateClasses should have worked", threw);
assertTrue(findClassInSystemCatalog("org.voltdb_testprocs.fullddlfeatures.testImportProc"));
// adhoc queries still work
ClientResponse result = m_client.callProcedure("@AdHoc", "select * from baz;");
assertEquals(ClientResponse.SUCCESS, result.getStatus());
// Promote, should behave like the original master test
m_client.callProcedure("@Promote");
verifyMasterWithAdhocDDL();
}
finally {
teardownSystem();
}
}
}