/* -*- mode: java; c-basic-offset: 4; indent-tabs-mode: nil; -*-
* vim:expandtab:shiftwidth=4:tabstop=4:smarttab:
*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cluster.benchmark.tws;
import com.mysql.ndbjtie.ndbapi.Ndb_cluster_connection;
import com.mysql.ndbjtie.ndbapi.Ndb;
import com.mysql.ndbjtie.ndbapi.NdbDictionary.Dictionary;
import com.mysql.ndbjtie.ndbapi.NdbDictionary.TableConst;
import com.mysql.ndbjtie.ndbapi.NdbError;
import com.mysql.ndbjtie.ndbapi.NdbTransaction;
import com.mysql.ndbjtie.ndbapi.NdbOperation;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CharacterCodingException;
class NdbjtieLoad extends TwsLoad {
// NDB settings
protected String mgmdConnect;
protected String catalog;
protected String schema;
// NDB JTie resources
protected Ndb_cluster_connection mgmd;
protected Ndb ndb;
protected NdbTransaction tx;
protected int ndbOpLockMode;
// NDB JTie metadata resources
protected TableConst table_t0;
// NDB JTie data resources
protected ByteBuffer bb;
// NDB JTie static resources
static protected final ByteOrder bo = ByteOrder.nativeOrder();
static protected final Charset cs;
static protected final CharsetEncoder csEncoder;
static protected final CharsetDecoder csDecoder;
static {
// default charset for mysql is "ISO-8859-1" ("US-ASCII", "UTF-8")
cs = Charset.forName("ISO-8859-1");
csDecoder = cs.newDecoder();
csEncoder = cs.newEncoder();
// report any unclean transcodings
csEncoder
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
csDecoder
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
}
public NdbjtieLoad(TwsDriver driver) {
super(driver, null);
}
// ----------------------------------------------------------------------
// NDB Base intializers/finalizers
// ----------------------------------------------------------------------
protected void initProperties() {
out.println();
out.print("setting ndb properties ...");
final StringBuilder msg = new StringBuilder();
final String eol = System.getProperty("line.separator");
// the hostname and port number of NDB mgmd
mgmdConnect = driver.props.getProperty("ndb.mgmdConnect", "localhost");
assert mgmdConnect != null;
// the database
catalog = driver.props.getProperty("ndb.catalog", "crunddb");
assert catalog != null;
// the schema
schema = driver.props.getProperty("ndb.schema", "def");
assert schema != null;
if (msg.length() == 0) {
out.println(" [ok]");
} else {
out.println();
out.print(msg.toString());
}
// have mgmdConnect initialized first
descr = "ndbjtie(" + mgmdConnect + ")";
}
protected void printProperties() {
out.println("ndb.mgmdConnect: \"" + mgmdConnect + "\"");
out.println("ndb.catalog: \"" + catalog + "\"");
out.println("ndb.schema: \"" + schema + "\"");
}
public void init() throws Exception {
super.init();
assert mgmd == null;
// load native library (better diagnostics doing it explicitely)
Driver.loadSystemLibrary("ndbclient");
// instantiate NDB cluster singleton
out.print("creating cluster connection ...");
out.flush();
mgmd = Ndb_cluster_connection.create(mgmdConnect);
assert mgmd != null;
out.println(" [ok: mgmd@" + mgmdConnect + "]");
}
public void close() throws Exception {
assert mgmd != null;
out.println();
out.print("closing cluster connection ...");
out.flush();
Ndb_cluster_connection.delete(mgmd);
mgmd = null;
out.println(" [ok]");
super.close();
}
// ----------------------------------------------------------------------
// NDB JTie datastore operations
// ----------------------------------------------------------------------
public void initConnection() {
assert (mgmd != null);
assert (ndb == null);
out.println();
out.println("initializing ndbjtie resources ...");
// connect to cluster management node (ndb_mgmd)
out.print("connecting to cluster ...");
out.flush();
final int retries = 0; // retries (< 0 = indefinitely)
final int delay = 0; // seconds to wait after retry
final int verbose = 1; // print report of progess
// 0 = success, 1 = recoverable error, -1 = non-recoverable error
if (mgmd.connect(retries, delay, verbose) != 0) {
final String msg = ("mgmd@" + mgmdConnect
+ " was not ready within "
+ (retries * delay) + "s.");
out.println(msg);
throw new RuntimeException("!!! " + msg);
}
out.println(" [ok: " + mgmdConnect + "]");
// connect to data nodes (ndbds)
out.print("waiting for data nodes ...");
out.flush();
final int initial_wait = 10; // secs to wait until first node detected
final int final_wait = 0; // secs to wait after first node detected
// returns: 0 all nodes live, > 0 at least one node live, < 0 error
if (mgmd.wait_until_ready(initial_wait, final_wait) < 0) {
final String msg = ("data nodes were not ready within "
+ (initial_wait + final_wait) + "s.");
out.println(msg);
throw new RuntimeException(msg);
}
out.println(" [ok]");
// connect to database
out.print("connecting to database ...");
ndb = Ndb.create(mgmd, catalog, schema);
final int max_no_tx = 10; // maximum number of parallel tx (<=1024)
// note each scan or index scan operation uses one extra transaction
if (ndb.init(max_no_tx) != 0) {
String msg = "Error caught: " + ndb.getNdbError().message();
throw new RuntimeException(msg);
}
out.println(" [ok: " + catalog + "." + schema + "]");
metaData = new MetaData(ndb);
metaData.initNdbjtieModel();
initNdbjtieBuffers();
out.print("using lock mode for reads ...");
out.flush();
final String lm;
switch (driver.lockMode) {
case READ_COMMITTED:
ndbOpLockMode = NdbOperation.LockMode.LM_CommittedRead;
lm = "LM_CommittedRead";
break;
case SHARED:
ndbOpLockMode = NdbOperation.LockMode.LM_Read;
lm = "LM_Read";
break;
case EXCLUSIVE:
ndbOpLockMode = NdbOperation.LockMode.LM_Exclusive;
lm = "LM_Exclusive";
break;
default:
ndbOpLockMode = NdbOperation.LockMode.LM_CommittedRead;
lm = "LM_CommittedRead";
assert false;
}
out.println(" [ok: " + lm + "]");
}
public void closeConnection() {
assert (ndb != null);
out.println();
out.println("releasing ndbjtie resources ...");
closeNdbjtieBuffers();
closeNdbjtieModel();
out.print("closing database connection ...");
out.flush();
Ndb.delete(ndb);
ndb = null;
out.println(" [ok]");
}
protected void closeNdbjtieModel() {
assert (ndb != null);
assert (table_t0 != null);
assert (metaData.getColumn(0) != null);
out.print("clearing metadata cache...");
out.flush();
table_t0 = null;
out.println(" [ok]");
}
public void initNdbjtieBuffers() {
assert (metaData.getColumn(0) != null);
assert (bb == null);
out.print("allocating buffers...");
out.flush();
bb = ByteBuffer.allocateDirect(metaData.getRowWidth() * driver.nRows);
// initial order of a byte buffer is always BIG_ENDIAN
bb.order(bo);
out.println(" [ok]");
}
protected void closeNdbjtieBuffers() {
assert (metaData.getColumn(0) != null);
assert (bb != null);
out.print("releasing buffers...");
out.flush();
bb = null;
out.println(" [ok]");
}
// ----------------------------------------------------------------------
public void runOperations() {
out.println();
out.println("running NDB JTie operations ..."
+ " [nRows=" + driver.nRows + "]");
if (driver.doSingle) {
if (driver.doInsert) runNdbjtieInsert(TwsDriver.XMode.SINGLE);
if (driver.doLookup) runNdbjtieLookup(TwsDriver.XMode.SINGLE);
if (driver.doUpdate) runNdbjtieUpdate(TwsDriver.XMode.SINGLE);
if (driver.doDelete) runNdbjtieDelete(TwsDriver.XMode.SINGLE);
}
if (driver.doBulk) {
if (driver.doInsert) runNdbjtieInsert(TwsDriver.XMode.BULK);
if (driver.doLookup) runNdbjtieLookup(TwsDriver.XMode.BULK);
if (driver.doUpdate) runNdbjtieUpdate(TwsDriver.XMode.BULK);
if (driver.doDelete) runNdbjtieDelete(TwsDriver.XMode.BULK);
}
if (driver.doBatch) {
if (driver.doInsert) runNdbjtieInsert(TwsDriver.XMode.BATCH);
if (driver.doLookup) runNdbjtieLookup(TwsDriver.XMode.BATCH);
if (driver.doUpdate) runNdbjtieUpdate(TwsDriver.XMode.BATCH);
if (driver.doDelete) runNdbjtieDelete(TwsDriver.XMode.BATCH);
}
}
// ----------------------------------------------------------------------
protected void runNdbjtieInsert(TwsDriver.XMode mode) {
final String name = "insert_" + mode.toString().toLowerCase();
driver.begin(name);
initTable();
if (mode == TwsDriver.XMode.SINGLE) {
for(int i = 0; i < driver.nRows; i++) {
ndbjtieBeginTransaction();
ndbjtieInsert(i);
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
} else {
ndbjtieBeginTransaction();
for(int i = 0; i < driver.nRows; i++) {
ndbjtieInsert(i);
if (mode == TwsDriver.XMode.BULK)
ndbjtieExecuteTransaction();
}
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
driver.finish(name);
}
protected void initTable() {
if(table_t0 == null) {
final Dictionary dict = ndb.getDictionary();
if ((table_t0 = dict.getTable("mytable")) == null)
throw new RuntimeException(TwsUtils.toStr(dict.getNdbError()));
}
}
protected void ndbjtieInsert(int c0) {
// get an insert operation for the table
NdbOperation op = tx.getNdbOperation(table_t0);
if (op == null)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.insertTuple() != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
// include exception handling as part of transcoding pattern
final int i = c0;
final CharBuffer str = CharBuffer.wrap(Integer.toString(i));
try {
// set values; key attribute needs to be set first
//str.rewind();
ndbjtieTranscode(bb, str);
if (op.equal(metaData.getAttr(0), bb) != 0) // key
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(0));
str.rewind();
ndbjtieTranscode(bb, str);
if (op.setValue(metaData.getAttr(1), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(1));
if (op.setValue(metaData.getAttr(2), i) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(3), i) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
// XXX
if (op.setValue(metaData.getAttr(4), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
str.rewind();
ndbjtieTranscode(bb, str);
if (op.setValue(metaData.getAttr(5), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(5));
str.rewind();
ndbjtieTranscode(bb, str);
if (op.setValue(metaData.getAttr(6), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(6));
str.rewind();
ndbjtieTranscode(bb, str);
if (op.setValue(metaData.getAttr(7), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(7));
str.rewind();
ndbjtieTranscode(bb, str);
if (op.setValue(metaData.getAttr(8), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(8));
// XXX
if (op.setValue(metaData.getAttr(9), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(10), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(11), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(12), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(13), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(14), null) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
// ----------------------------------------------------------------------
protected void runNdbjtieLookup(TwsDriver.XMode mode) {
final String name = "lookup_" + mode.toString().toLowerCase();
driver.begin(name);
if (mode == TwsDriver.XMode.SINGLE) {
for(int i = 0; i < driver.nRows; i++) {
ndbjtieBeginTransaction();
ndbjtieLookup(i);
ndbjtieCommitTransaction();
ndbjtieRead(i);
ndbjtieCloseTransaction();
}
} else {
ndbjtieBeginTransaction();
for(int i = 0; i < driver.nRows; i++) {
ndbjtieLookup(i);
if (mode == TwsDriver.XMode.BULK)
ndbjtieExecuteTransaction();
}
ndbjtieCommitTransaction();
for(int i = 0; i < driver.nRows; i++) {
ndbjtieRead(i);
}
ndbjtieCloseTransaction();
}
driver.finish(name);
}
protected void ndbjtieLookup(int c0) {
// get a lookup operation for the table
NdbOperation op = tx.getNdbOperation(table_t0);
if (op == null)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.readTuple(ndbOpLockMode) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
int p = bb.position();
// include exception handling as part of transcoding pattern
final CharBuffer str = CharBuffer.wrap(Integer.toString(c0));
try {
// set values; key attribute needs to be set first
//str.rewind();
ndbjtieTranscode(bb, str);
if (op.equal(metaData.getAttr(0), bb) != 0) // key
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(p += metaData.getColumnWidth(0));
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
// get attributes (not readable until after commit)
for(int i = 1; i < metaData.getColumnCount(); i++) {
if (op.getValue(metaData.getAttr(i), bb) == null)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(p += metaData.getColumnWidth(i));
}
}
protected void ndbjtieRead(int c0) {
// include exception handling as part of transcoding pattern
//final int i = c0;
//final CharBuffer str = CharBuffer.wrap(Integer.toString(i));
//assert (str.position() == 0);
try {
int p = bb.position();
bb.position(p += metaData.getColumnWidth(0));
// not verifying at this time
// (str.equals(ndbjtieTranscode(bb_c1)));
// (i == bb_c2.asIntBuffer().get());
//CharBuffer y = ndbjtieTranscode(bb);
ndbjtieTranscode(bb);
bb.position(p += metaData.getColumnWidth(1));
bb.asIntBuffer().get();
bb.position(p += metaData.getColumnWidth(2));
bb.asIntBuffer().get();
bb.position(p += metaData.getColumnWidth(3));
bb.asIntBuffer().get();
bb.position(p += metaData.getColumnWidth(4));
for(int i = 5; i < metaData.getColumnCount(); i++) {
ndbjtieTranscode(bb);
bb.position(p += metaData.getColumnWidth(i));
}
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
// ----------------------------------------------------------------------
protected void runNdbjtieUpdate(TwsDriver.XMode mode) {
final String name = "update_" + mode.toString().toLowerCase();
driver.begin(name);
if (mode == TwsDriver.XMode.SINGLE) {
for(int i = 0; i < driver.nRows; i++) {
ndbjtieBeginTransaction();
ndbjtieUpdate(i);
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
} else {
ndbjtieBeginTransaction();
for(int i = 0; i < driver.nRows; i++) {
ndbjtieUpdate(i);
if (mode == TwsDriver.XMode.BULK)
ndbjtieExecuteTransaction();
}
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
driver.finish(name);
}
protected void ndbjtieUpdate(int c0) {
final CharBuffer str0 = CharBuffer.wrap(Integer.toString(c0));
final int r = -c0;
final CharBuffer str1 = CharBuffer.wrap(Integer.toString(r));
// get an update operation for the table
NdbOperation op = tx.getNdbOperation(table_t0);
if (op == null)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.updateTuple() != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
// include exception handling as part of transcoding pattern
try {
// set values; key attribute needs to be set first
//str0.rewind();
ndbjtieTranscode(bb, str0);
if (op.equal(metaData.getAttr(0), bb) != 0) // key
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(0));
//str1.rewind();
ndbjtieTranscode(bb, str1);
if (op.setValue(metaData.getAttr(1), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(1));
if (op.setValue(metaData.getAttr(2), r) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.setValue(metaData.getAttr(3), r) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
for(int i = 5; i < metaData.getColumnCount(); i++) {
str1.rewind();
ndbjtieTranscode(bb, str1);
if (op.setValue(metaData.getAttr(i), bb) != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(bb.position() + metaData.getColumnWidth(i));
}
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
// ----------------------------------------------------------------------
protected void runNdbjtieDelete(TwsDriver.XMode mode) {
final String name = "delete_" + mode.toString().toLowerCase();
driver.begin(name);
if (mode == TwsDriver.XMode.SINGLE) {
for(int i = 0; i < driver.nRows; i++) {
ndbjtieBeginTransaction();
ndbjtieDelete(i);
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
} else {
ndbjtieBeginTransaction();
for(int i = 0; i < driver.nRows; i++) {
ndbjtieDelete(i);
if (mode == TwsDriver.XMode.BULK)
ndbjtieExecuteTransaction();
}
ndbjtieCommitTransaction();
ndbjtieCloseTransaction();
}
driver.finish(name);
}
protected void ndbjtieDelete(int c0) {
// get a delete operation for the table
NdbOperation op = tx.getNdbOperation(table_t0);
if (op == null)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
if (op.deleteTuple() != 0)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
int p = bb.position();
// include exception handling as part of transcoding pattern
final int i = c0;
final CharBuffer str = CharBuffer.wrap(Integer.toString(c0));
try {
// set values; key attribute needs to be set first
//str.rewind();
ndbjtieTranscode(bb, str);
if (op.equal(metaData.getAttr(0), bb) != 0) // key
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
bb.position(p += metaData.getColumnWidth(0));
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
// ----------------------------------------------------------------------
protected void ndbjtieBeginTransaction() {
assert (tx == null);
// prepare buffer for writing
bb.clear();
// start a transaction
// must be closed with NdbTransaction.close
final TableConst table = null;
final ByteBuffer keyData = null;
final int keyLen = 0;
if ((tx = ndb.startTransaction(table, keyData, keyLen)) == null)
throw new RuntimeException(TwsUtils.toStr(ndb.getNdbError()));
}
protected void ndbjtieExecuteTransaction() {
assert (tx != null);
// execute but don't commit the current transaction
final int execType = NdbTransaction.ExecType.NoCommit;
final int abortOption = NdbOperation.AbortOption.AbortOnError;
final int force = 0;
if (tx.execute(execType, abortOption, force) != 0
|| tx.getNdbError().status() != NdbError.Status.Success)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
}
protected void ndbjtieCommitTransaction() {
assert (tx != null);
// commit the current transaction
final int execType = NdbTransaction.ExecType.Commit;
final int abortOption = NdbOperation.AbortOption.AbortOnError;
final int force = 0;
if (tx.execute(execType, abortOption, force) != 0
|| tx.getNdbError().status() != NdbError.Status.Success)
throw new RuntimeException(TwsUtils.toStr(tx.getNdbError()));
// prepare buffer for reading
bb.rewind();
}
protected void ndbjtieCloseTransaction() {
assert (tx != null);
// close the current transaction (required after commit, rollback)
ndb.closeTransaction(tx);
tx = null;
}
// ----------------------------------------------------------------------
protected CharBuffer ndbjtieTranscode(ByteBuffer from)
throws CharacterCodingException {
// mark position
final int p = from.position();
// read 1-byte length prefix
final int l = from.get();
assert ((0 <= l) && (l < 256)); // or (l <= 256)?
// prepare buffer for reading
from.limit(from.position() + l);
// decode
final CharBuffer to = csDecoder.decode(from);
assert (!from.hasRemaining());
// allow for repositioning
from.limit(from.capacity());
assert (to.position() == 0);
assert (to.limit() == to.capacity());
return to;
}
protected void ndbjtieTranscode(ByteBuffer to, CharBuffer from)
throws CharacterCodingException {
// mark position
final int p = to.position();
// advance 1-byte length prefix
to.position(p + 1);
//to.put((byte)0);
// encode
final boolean endOfInput = true;
final CoderResult cr = csEncoder.encode(from, to, endOfInput);
if (!cr.isUnderflow())
cr.throwException();
assert (!from.hasRemaining());
// write 1-byte length prefix
final int l = (to.position() - p) - 1;
assert (0 <= l && l < 256); // or (l <= 256)?
to.put(p, (byte)l);
// reset position
to.position(p);
}
}