/* 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.
*/
/*
* DDL in the Java client?
*
* It's because there can't be two different socket import streams
* in the deployment file.
*
* So the strategy is the create the target table, importtable partitioned,
* run the test, then it's possible to drop the table and its SP's and recreate
* it without partitioning.
*
* If at some point this limitation is eliminated -- ENG-9074, then the
* DDL in the client could be eliminated.
*
* However it might be a useful test variant since there's not much DDL
* in the system test client.
*/
package socketimporter.client.socketimporter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import org.voltcore.utils.Pair;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.NoConnectionsException;
import org.voltdb.client.ProcCallException;
import org.voltdb.client.ProcedureCallback;
import org.voltcore.logging.VoltLogger;
public class DataUtils {
static VoltLogger log = new VoltLogger("DataUtils");
static Queue<Pair<String, String>> m_queue;
static Queue<Pair<String, String>> m_delete_queue;
Client m_client;
private static final int KEY = 0;
private static final int VALUE = 1;
private static final String m_select = "SelectOnly"; // would use default IMPORTTABLE.select but it's not created on replicated table
private static final String m_delete = "IMPORTTABLE.delete";
private static final String m_max = "SelectMaxTime";
public DataUtils(Queue<Pair<String, String>> q, Queue<Pair<String, String>> dq, Client c, boolean partitioned) {
m_client = c;
m_queue = q;
m_delete_queue = dq;
}
public void processQueue() {
while (m_queue.size() > 0 || m_delete_queue.size() > 0) {
Pair<String, String> p = m_queue.poll();
try {
if (p != null) {
String key = p.getFirst();
boolean ret = m_client.callProcedure(new SelectCallback(m_queue, p, key), m_select, key);
if (!ret) {
log.info("Select call failed!");
}
}
Pair<String, String> p2 = m_delete_queue.poll();
if (p2 != null) {
boolean ret = m_client.callProcedure(new DeleteCallback(m_delete_queue, p2), m_delete, p2.getFirst());
if (!ret) {
log.info("Delete call failed!");
}
}
AsyncBenchmark.rowsChecked.incrementAndGet();
} catch (NoConnectionsException e) {
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
public long maxInsertTime() {
ClientResponse response = null;
try {
response = m_client.callProcedure(m_max);
} catch (NoConnectionsException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ProcCallException e) {
e.printStackTrace();
}
VoltTable[] countQueryResult = response.getResults();
VoltTable data = countQueryResult[0];
if (data.asScalarLong() == VoltType.NULL_BIGINT)
return 0;
return data.asScalarLong();
}
public void ddlSetup(boolean partitioned) {
final String[] DDLStmts = {
"CREATE TABLE IMPORTTABLE ( " +
" key varchar(250) not null " +
", value varchar(1048576 BYTES) not null " +
", insert_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL " +
", PRIMARY KEY (key))",
"CREATE PROCEDURE SelectOnly as select * from importtable where key = ?",
"CREATE PROCEDURE InsertOnly as insert into IMPORTTABLE(key, value) VALUES(?, ?)",
"CREATE PROCEDURE SelectMaxTime as select since_epoch(millis, max(insert_time)) from IMPORTTABLE",
};
final String[] PartitionStmts = {
"PARTITION table IMPORTTABLE ON COLUMN key",
"PARTITION PROCEDURE InsertOnly ON TABLE importtable COLUMN key",
"PARTITION PROCEDURE SelectOnly ON TABLE importtable COLUMN key"
};
dropTables();
try {
for (int i = 0; i < DDLStmts.length; i++) {
m_client.callProcedure("@AdHoc",
DDLStmts[i]).getResults();
}
} catch (Exception e) {
e.printStackTrace();
}
if (partitioned) {
try {
for (int i = 0; i < PartitionStmts.length; i++) {
m_client.callProcedure("@AdHoc",
PartitionStmts[i]).getResults();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void dropTables() {
final String[] dropStmts = {
"procedure InsertOnly IF EXISTS",
"procedure SelectMaxTime IF EXISTS",
"procedure SelectOnly IF EXISTS",
"table IMPORTTABLE IF EXISTS"
};
try {
for (int i = 0; i < dropStmts.length; i++) {
m_client.callProcedure("@AdHoc",
"DROP " + dropStmts[i]).getResults();
}
} catch (IOException | ProcCallException e) {
e.printStackTrace();
}
}
static class SelectCallback implements ProcedureCallback {
private static final int KEY = 0;
private static final int VALUE = 1;
Pair<String, String> m_pair;
Queue<Pair<String, String>> m_queue;
String m_key;
public SelectCallback(Queue<Pair<String, String>> q, Pair<String, String> p, String key) {
m_pair = p;
m_queue = q;
m_key = key;
}
@Override
public void clientCallback(ClientResponse response)
throws Exception {
if (response.getStatus() != ClientResponse.SUCCESS) {
log.info(response.getStatusString());
return;
}
List<String> pair = getDataFromResponse(response);
String key, value;
if (pair.size() == 2) {
key = pair.get(KEY);
value = pair.get(VALUE);
m_delete_queue.offer(m_pair);
} else {
// push the tuple back onto the queue we can try again
m_queue.offer(m_pair);
return;
}
if (!value.equals(m_pair.getSecond())) {
log.info("Pair from DB: " + key + ", " + value);
log.info("Pair from queue: " + m_pair.getFirst() + ", " + m_pair.getSecond());
AsyncBenchmark.rowsMismatch.incrementAndGet();
}
}
private List<String> getDataFromResponse(ClientResponse response) {
List<String> m_pair = new ArrayList<String>();
VoltTable[] m_results = response.getResults();
if (m_results.length == 0) {
log.info("zero length results");
return m_pair;
}
VoltTable recordset = m_results[0];
if (recordset.advanceRow()) {
m_pair.add((String) recordset.get(KEY, VoltType.STRING));
m_pair.add((String) recordset.get(VALUE, VoltType.STRING));
}
return m_pair;
}
}
static class DeleteCallback implements ProcedureCallback {
Pair<String, String> m_pair;
Queue<Pair<String, String>> m_queue;
public DeleteCallback(Queue<Pair<String, String>> q, Pair<String, String> p) {
m_pair = p;
m_queue = q;
}
@Override
public void clientCallback(ClientResponse response)
throws Exception {
if (response.getStatus() != ClientResponse.SUCCESS) {
log.info(response.getStatusString());
return;
}
m_queue.remove(m_pair);
}
}
}