/** * Copyright (c) 2010 Yahoo! Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. See accompanying * LICENSE file. */ /* * Cassandra client specific to version 0.5 of Cassandra. */ package com.yahoo.ycsb.db; import com.yahoo.ycsb.*; import java.util.List; import java.util.Set; import java.util.HashMap; import java.util.HashSet; import java.util.Vector; import java.util.Random; import java.util.Properties; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.cassandra.service.*; // XXXX if we do replication, fix the consistency levels /** * Cassandra 0.5 client for YCSB framework */ public class CassandraClient5 extends DB { static Random random=new Random(); public static final int Ok=0; public static final int Error=-1; public int ConnectionRetries; public int OperationRetries; public static final String CONNECTION_RETRY_PROPERTY="cassandra.connectionretries"; public static final String CONNECTION_RETRY_PROPERTY_DEFAULT="300"; public static final String OPERATION_RETRY_PROPERTY="cassandra.operationretries"; public static final String OPERATION_RETRY_PROPERTY_DEFAULT="300"; TTransport tr; Cassandra.Client client; boolean _debug=false; /** * Initialize any state for this DB. * Called once per DB instance; there is one DB instance per client thread. */ public void init() throws DBException { String hosts=getProperties().getProperty("hosts"); if (hosts==null) { throw new DBException("Required property \"hosts\" missing for CassandraClient5"); } ConnectionRetries=Integer.parseInt(getProperties().getProperty(CONNECTION_RETRY_PROPERTY,CONNECTION_RETRY_PROPERTY_DEFAULT)); OperationRetries=Integer.parseInt(getProperties().getProperty(OPERATION_RETRY_PROPERTY,OPERATION_RETRY_PROPERTY_DEFAULT)); _debug=Boolean.parseBoolean(getProperties().getProperty("debug","false")); String[] allhosts=hosts.split(","); String myhost=allhosts[random.nextInt(allhosts.length)]; //System.out.println("My host: ["+myhost+"]"); //System.exit(0); Exception connectexception=null; for (int retry=0; retry<ConnectionRetries; retry++) { tr = new TSocket(myhost, 9160); TProtocol proto = new TBinaryProtocol(tr); client = new Cassandra.Client(proto); try { tr.open(); connectexception=null; break; } catch (Exception e) { connectexception=e; } try { Thread.sleep(1000); } catch (InterruptedException e) {} } if (connectexception!=null) { System.err.println("Unable to connect to "+myhost+" after "+ConnectionRetries+" tries"); System.out.println("Unable to connect to "+myhost+" after "+ConnectionRetries+" tries"); throw new DBException(connectexception); } } /** * Cleanup any state for this DB. * Called once per DB instance; there is one DB instance per client thread. */ public void cleanup() throws DBException { tr.close(); } /** * Read a record from the database. Each field/value pair from the result will be stored in a HashMap. * * @param table The name of the table * @param key The record key of the record to read. * @param fields The list of fields to read, or null for all of them * @param result A HashMap of field/value pairs for the result * @return Zero on success, a non-zero error code on error */ public int read(String table, String key, Set<String> fields, HashMap<String,ByteIterator> result) { Exception errorexception=null; for (int i=0; i<OperationRetries; i++) { try { SlicePredicate predicate; if (fields==null) { predicate = new SlicePredicate(null,new SliceRange(new byte[0], new byte[0],false,1000000)); } else { Vector<byte[]> fieldlist=new Vector<byte[]>(); for (String s : fields) { fieldlist.add(s.getBytes("UTF-8")); } predicate = new SlicePredicate(fieldlist,null); } ColumnParent parent = new ColumnParent("data", null); List<ColumnOrSuperColumn> results = client.get_slice(table, key, parent, predicate, ConsistencyLevel.ONE); if (_debug) { System.out.print("READ: "); } for (ColumnOrSuperColumn oneresult : results) { Column column=oneresult.column; result.put(new String(column.name),new ByteArrayByteIterator(column.value)); if (_debug) { System.out.print("("+new String(column.name)+"="+new String(column.value)+")"); } } if (_debug) { System.out.println(""); } return Ok; } catch (Exception e) { errorexception=e; } try { Thread.sleep(500); } catch (InterruptedException e) { } } errorexception.printStackTrace(); errorexception.printStackTrace(System.out); return Error; } /** * Perform a range scan for a set of records in the database. Each field/value pair from the result will be stored in a HashMap. * * @param table The name of the table * @param startkey The record key of the first record to read. * @param recordcount The number of records to read * @param fields The list of fields to read, or null for all of them * @param result A Vector of HashMaps, where each HashMap is a set field/value pairs for one record * @return Zero on success, a non-zero error code on error */ public int scan(String table, String startkey, int recordcount, Set<String> fields, Vector<HashMap<String,ByteIterator>> result) { Exception errorexception=null; for (int i=0; i<OperationRetries; i++) { try { SlicePredicate predicate; if (fields==null) { predicate = new SlicePredicate(null,new SliceRange(new byte[0], new byte[0],false,1000000)); } else { Vector<byte[]> fieldlist=new Vector<byte[]>(); for (String s : fields) { fieldlist.add(s.getBytes("UTF-8")); } predicate = new SlicePredicate(fieldlist,null); } ColumnParent parent = new ColumnParent("data", null); List<KeySlice> results = client.get_range_slice(table,parent,predicate,startkey,"",recordcount,ConsistencyLevel.ONE); if (_debug) { System.out.println("SCAN:"); } for (KeySlice oneresult : results) { HashMap<String,ByteIterator> tuple = new HashMap<String, ByteIterator>(); for (ColumnOrSuperColumn onecol : oneresult.columns) { Column column=onecol.column; tuple.put(new String(column.name),new ByteArrayByteIterator(column.value)); if (_debug) { System.out.print("("+new String(column.name)+"="+new String(column.value)+")"); } } result.add(tuple); if (_debug) { System.out.println(); } } return Ok; } catch (Exception e) { errorexception=e; } try { Thread.sleep(500); } catch (InterruptedException e) { } } errorexception.printStackTrace(); errorexception.printStackTrace(System.out); return Error; } /** * Update a record in the database. Any field/value pairs in the specified values HashMap will be written into the record with the specified * record key, overwriting any existing values with the same field name. * * @param table The name of the table * @param key The record key of the record to write. * @param values A HashMap of field/value pairs to update in the record * @return Zero on success, a non-zero error code on error */ public int update(String table, String key, HashMap<String,ByteIterator> values) { return insert(table,key,values); } /** * Insert a record in the database. Any field/value pairs in the specified values HashMap will be written into the record with the specified * record key. * * @param table The name of the table * @param key The record key of the record to insert. * @param values A HashMap of field/value pairs to insert in the record * @return Zero on success, a non-zero error code on error */ public int insert(String table, String key, HashMap<String,ByteIterator> values) { Exception errorexception=null; for (int i=0; i<OperationRetries; i++) { // insert data long timestamp = System.currentTimeMillis(); HashMap<String, List<ColumnOrSuperColumn>> batch_mutation=new HashMap<String, List<ColumnOrSuperColumn>>(); Vector<ColumnOrSuperColumn> v=new Vector<ColumnOrSuperColumn>(); batch_mutation.put("data",v); try { for (String field : values.keySet()) { String val=values.get(field).toString(); Column col=new Column(field.getBytes("UTF-8"),val.getBytes("UTF-8"),timestamp); ColumnOrSuperColumn c=new ColumnOrSuperColumn(); c.setColumn(col); c.unsetSuper_column(); v.add(c); } client.batch_insert(table, key, batch_mutation, ConsistencyLevel.ONE); if (_debug) { System.out.println("INSERT"); } return Ok; } catch (Exception e) { errorexception=e; } try { Thread.sleep(500); } catch (InterruptedException e) { } } errorexception.printStackTrace(); errorexception.printStackTrace(System.out); return Error; } /** * Delete a record from the database. * * @param table The name of the table * @param key The record key of the record to delete. * @return Zero on success, a non-zero error code on error */ public int delete(String table, String key) { Exception errorexception=null; for (int i=0; i<OperationRetries; i++) { try { client.remove(table,key,new ColumnPath("data", null, null),System.currentTimeMillis(),ConsistencyLevel.ONE); if (_debug) { System.out.println("DELETE"); } return Ok; } catch (Exception e) { errorexception=e; } try { Thread.sleep(500); } catch (InterruptedException e) { } } errorexception.printStackTrace(); errorexception.printStackTrace(System.out); return Error; } public static void main(String[] args) { CassandraClient5 cli=new CassandraClient5(); Properties props=new Properties(); props.setProperty("hosts",args[0]); cli.setProperties(props); try { cli.init(); } catch (Exception e) { e.printStackTrace(); System.exit(0); } HashMap<String,ByteIterator> vals=new HashMap<String,ByteIterator>(); vals.put("age",new StringByteIterator("57")); vals.put("middlename",new StringByteIterator("bradley")); vals.put("favoritecolor",new StringByteIterator("blue")); int res=cli.insert("usertable","BrianFrankCooper",vals); System.out.println("Result of insert: "+res); HashMap<String,ByteIterator> result=new HashMap<String,ByteIterator>(); HashSet<String> fields=new HashSet<String>(); fields.add("middlename"); fields.add("age"); fields.add("favoritecolor"); res=cli.read("usertable", "BrianFrankCooper", null, result); System.out.println("Result of read: "+res); for (String s: result.keySet()) { System.out.println("["+s+"]=["+result.get(s)+"]"); } res=cli.delete("usertable","BrianFrankCooper"); System.out.println("Result of delete: "+res); } /* public static void main(String[] args) throws TException, InvalidRequestException, UnavailableException, UnsupportedEncodingException, NotFoundException { String key_user_id = "1"; client.insert("Keyspace1", key_user_id, new ColumnPath("Standard1", null, "age".getBytes("UTF-8")), "24".getBytes("UTF-8"), timestamp, ConsistencyLevel.ONE); // read single column ColumnPath path = new ColumnPath("Standard1", null, "name".getBytes("UTF-8")); System.out.println(client.get("Keyspace1", key_user_id, path, ConsistencyLevel.ONE)); // read entire row SlicePredicate predicate = new SlicePredicate(null, new SliceRange(new byte[0], new byte[0], false, 10)); ColumnParent parent = new ColumnParent("Standard1", null); List<ColumnOrSuperColumn> results = client.get_slice("Keyspace1", key_user_id, parent, predicate, ConsistencyLevel.ONE); for (ColumnOrSuperColumn result : results) { Column column = result.column; System.out.println(new String(column.name, "UTF-8") + " -> " + new String(column.value, "UTF-8")); } } */ }