package ch.usi.da.smr;
/*
* Copyright (c) 2013 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos 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, either version 3 of the License, or
* (at your option) any later version.
*
* URingPaxos 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 URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import ch.usi.da.smr.message.Command;
import ch.usi.da.smr.message.CommandType;
import ch.usi.da.smr.transport.Response;
import com.yahoo.ycsb.ByteIterator;
import com.yahoo.ycsb.DB;
import com.yahoo.ycsb.DBException;
import com.yahoo.ycsb.Status;
import com.yahoo.ycsb.StringByteIterator;
import com.yahoo.ycsb.workloads.CoreWorkload;
/**
* Name: YCSB<br>
* Description: <br>
*
* See https://github.com/brianfrankcooper/YCSB/wiki/Adding-a-Database
*
* Creation date: Nov 15, 2013<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class YCSB extends DB {
private static final String HOST = "zoo.host";
private static final String HOST_DEFAULT = "127.0.0.1:2181";
private static final String MAP = "smr.map";
private static final String MAP_DEFAULT = "1,1;16,1";
private PartitionManager partitions;
private Client client;
private AtomicInteger cmd_id = new AtomicInteger(0);
private boolean writeallfields;
@Override
public void init() throws DBException {
final Map<Integer,Integer> connectMap = Client.parseConnectMap(getProperties().getProperty(MAP, MAP_DEFAULT));
try {
partitions = new PartitionManager(getProperties().getProperty(HOST, HOST_DEFAULT));
partitions.init();
client = new Client(partitions,connectMap);
client.init();
writeallfields = Boolean.parseBoolean(getProperties().getProperty(CoreWorkload.WRITE_ALL_FIELDS_PROPERTY,
CoreWorkload.WRITE_ALL_FIELDS_PROPERTY_DEFAULT));
} catch (Exception e) {
throw new DBException(e);
}
}
@Override
public Status read(String table, String key, Set<String> fields, HashMap<String, ByteIterator> result) {
Command cmd = new Command(cmd_id.incrementAndGet(),CommandType.GET,key,new byte[0]);
try {
Response r = client.send(cmd);
if(r != null){
List<Command> lc = r.getResponse(10000);
if(lc.size() == 0){
return Status.NOT_FOUND;
}else if(lc.get(0).getType() == CommandType.RESPONSE){
if(lc.get(0).getValue() != null){
decode(fields, new String(lc.get(0).getValue()), result);
return Status.OK;
}else{
return Status.NOT_FOUND;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return Status.ERROR;
}
@Override
public Status scan(String table, String startkey, int recordcount, Set<String> fields, Vector<HashMap<String, ByteIterator>> result) {
Command cmd = new Command(cmd_id.incrementAndGet(),CommandType.GETRANGE,startkey,("").getBytes(),recordcount);
try {
Response r = client.send(cmd);
if(r != null){
List<Command> lc = r.getResponse(10000);
if(lc.isEmpty()){ return Status.ERROR; }
for(Command c : lc){
if(c.getType() == CommandType.RESPONSE){
HashMap<String, ByteIterator> tuple = new HashMap<String, ByteIterator>();
tuple.put("key", new StringByteIterator(c.getKey()));
if(c.getValue() != null && c.getValue().length > 0){
decode(fields, new String(c.getValue()), tuple);
result.add(tuple);
}
}
}
return Status.OK;
}
} catch (Exception e) {
e.printStackTrace();
}
return Status.ERROR;
}
@Override
public Status update(String table, String key, HashMap<String, ByteIterator> values) {
if(!writeallfields) {
HashMap<String, ByteIterator> oldval = new HashMap<String, ByteIterator>();
read(table, key, null, oldval);
for(String k: values.keySet()) {
oldval.put(k, values.get(k));
}
values = oldval;
}
return insert(table, key, values);
}
@Override
public Status insert(String table, String key, HashMap<String, ByteIterator> values) {
Command cmd = new Command(cmd_id.incrementAndGet(),CommandType.PUT,key,encode(values).array());
try {
Response r = client.send(cmd);
if(r != null){
List<Command> lc = r.getResponse(10000);
if(lc.isEmpty()){ return Status.ERROR; }
if(lc.get(0).getType() == CommandType.RESPONSE){
return Status.OK;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return Status.ERROR;
}
@Override
public Status delete(String table, String key) {
Command cmd = new Command(cmd_id.incrementAndGet(),CommandType.DELETE,key,new byte[0]);
try {
Response r = client.send(cmd);
if(r != null){
List<Command> lc = r.getResponse(10000);
if(lc.isEmpty()){ return Status.ERROR; }
if(lc.get(0).getType() == CommandType.RESPONSE){
return Status.OK;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return Status.ERROR;
}
public Client getClient(){
return client;
}
/* see MapKeeperClient.java */
ByteBuffer encode(HashMap<String, ByteIterator> values) {
int len = 0;
for(String k : values.keySet()) {
len += (k.length() + 1 + values.get(k).bytesLeft() + 1);
}
byte[] array = new byte[len];
int i = 0;
for(String k : values.keySet()) {
for(int j = 0; j < k.length(); j++) {
array[i] = (byte)k.charAt(j);
i++;
}
array[i] = '\t';
i++;
ByteIterator v = values.get(k);
i = v.nextBuf(array, i);
array[i] = '\t';
i++;
}
array[array.length-1] = 0;
ByteBuffer buf = ByteBuffer.wrap(array);
buf.rewind();
return buf;
}
void decode(Set<String> fields, String tups, HashMap<String, ByteIterator> tup) {
String[] tok = tups.split("\\t");
if(tok.length == 0) { throw new IllegalStateException("split returned empty array!"); }
for(int i = 0; i < tok.length; i+=2) {
if(fields == null || fields.contains(tok[i])) {
if(tok.length < i+2) { throw new IllegalStateException("Couldn't parse tuple <" + tups + "> at index " + i); }
if(tok[i] == null || tok[i+1] == null) throw new NullPointerException("Key is " + tok[i] + " val is + " + tok[i+1]);
tup.put(tok[i], new StringByteIterator(tok[i+1]));
}
}
if(tok.length == 0) {
System.err.println("Empty tuple: " + tups);
}
}
}