C* Summit 2013: Java and .NET Client Drivers - Cassandra Developments on Fire by Michael Figuiere...

Preview:

DESCRIPTION

Speakers: Michael Figuiere and Patrick McFadin, Principal Solutions Architect at DataStax Cassandra 1.2 finalizes CQL3 and introduces a new binary protocol for client/server communication. These two components are the foundation of the new line of drivers developed by DataStax. Based on years of experience with Cassandra, these new drivers for Java, .Net and Python come with an asynchronous and lightweight architecture, a clean and simple API, a standardized way to discover nodes and to manage load balancing and fail over. This presentation will give an in depth look at these new drivers which will make your Cassandra-based applications even more robust, efficient and simple to write.

Citation preview

DataStax Java Driver Unleashed!

CQL The new face of Cassandra* CQL3

* Simpler Data Model using Denormalized Tables

* SQL-like Query Language

* Schema Definition

* CQL Native Protocol

* Introduced in Cassandra 1.2

* Designed for CQL3

* Thrift will keep being supported by Cassandra

* Cassandra lives closer to users and application

* Data model expresses your application intent

* Not made for generalizable queries

* Key to a successful project

CQL Data Model Overview

CQL3 Data Model

Data duplicated over several tables

CQL3 Query Language comes with a new Data

Model abstraction made of denormalized, statically defined tables

CQL3 Data Model

gmason

user_id

1735

tweet_id

phenry

author

Give me liberty or give me death

body

PartitionKey

gmason 1742 gwashington I chopped down the cherry tree

ahamilton 1767 jadams A government of laws, not men

ahamilton 1794 gwashington I chopped down the cherry tree

ClusteringKey

Timeline Table

CQL3 Data Model

gmason

user_id

1735

tweet_id

phenry

author

Give me liberty or give me death

body

PartitionKey

gmason 1742 gwashington I chopped down the cherry tree

ahamilton 1767 jadams A government of laws, not men

ahamilton 1794 gwashington I chopped down the cherry tree

ClusteringKey

Timeline Table

CREATE TABLE timeline ( user_id varchar, tweet_id timeuuid, author varchar, body varchar, PRIMARY KEY (user_id, tweet_id));

CQL

DB API

CQL Native Protocol

CQL API OO API

Next Generation Driver

New Driver Architecture

* Reference Implementation

* Asynchronous architecture based on Netty

* Prepared Statements Support

* Automatic Fail-over

* Node Discovery

* Cassandra Tracing Support

* Tunable Policies

Java Driver Overview

Client

WithoutRequest Pipelining

Cassandra

Client CassandraWith

Request Pipelining

Request Pipelining

Client

WithoutNotifications

WithNotifications

NodeNode

Node

Client

NodeNode

Node

Notifications

ClientThread

Node

Node

Node

ClientThread

ClientThread

Node

Driver

Asynchronous Architecture

1

23

45

6ClientThread

Node

Node

Node

ClientThread

ClientThread

Node

Driver

Asynchronous Architecture

contactPoints = {“10.0.0.1”,”10.0.0.2”}

keyspace = “videodb”

public VideoDbBasicImpl(List<String> contactPoints, String keyspace) {

cluster = Cluster .builder() .addContactPoints(! contactPoints.toArray(new String[contactPoints.size()])) .build();

session = cluster.connect(keyspace); }

Creating a Basic Connection

public void setUserByUsingString(User user) {

StringBuffer userInsert = new StringBuffer( "INSERT INTO users (username, firstname, lastname, email, password, created_date) VALUES (");

userInsert.append("'" + user.getUsername() + "'"); userInsert.append("'" + user.getFirstname() + "'"); userInsert.append("'" + user.getLastname() + "'"); userInsert.append("'" + user.getEmail() + "'"); userInsert.append("'" + user.getPassword() + "'"); userInsert.append("'" + user.getCreated_date().toString() + "'"); userInsert.append(")");

session.execute(userInsert.toString());}

Basic write using insert

! public User getUserByUsernameUsingString(String username) {

! ! User user = new User();

! ! ResultSet rs = session.execute("SELECT * FROM users WHERE username = '"! ! ! ! + username + "'");

! ! // A result set has Rows which can be iterated over! ! for (Row row : rs) {! ! ! user.setUsername(username);! ! ! user.setFirstname(row.getString("firstname"));! ! ! user.setLastname(row.getString("lastname"));! ! ! user.setEmail(row.getString("email"));! ! ! user.setPassword(row.getString("Password"));! ! ! user.setCreated_date(row.getDate("created_date"));! ! }

! ! return user;

! }

Basic Read using Select

public void setUserByPreparedStatement(User user) {

BoundStatement bs = setUser.bind();

bs.setString("username", user.getUsername()); bs.setString("firstname", user.getFirstname()); bs.setString("lastname", user.getLastname()); bs.setString("email", user.getEmail()); bs.setString("password", user.getPassword()); bs.setDate("created_date", user.getCreated_date());! ! session.execute(bs);

}

Writing with Prepared Statements

public List<Video> getVideosByUsernameUsingAsyncRead(String username) {

BoundStatement bs = getVideosByUsernamePreparedStatement.bind();

List<ResultSetFuture> futures = new ArrayList<ResultSetFuture>(); List<Video> videos = new ArrayList<Video>();

bs.setString("username", username);

for (Row row : session.execute(bs)) {

futures.add(session.executeAsync(getVideoByIDPreparedStatement .bind(row.getUUID("videoid")))); }

for (ResultSetFuture future : futures) {

for (Row row : future.getUninterruptibly()) { Video video = new Video(); video.setVideoid(row.getUUID("videoid")); videos.add(video); } } return videos;}

Asynchronous Read

for (String tag : tags) { BoundStatement bs = getVideosByTagPreparedStatement.bind(tag); final ResultSetFuture future = session.executeAsync(bs);

future.addListener(new Runnable() {

public void run() { for (Row row : future.getUninterruptibly()) { UUID videoId = row.getUUID("videoid");

if (videoIds.putIfAbsent(videoId, PRESENT) == null) {

videoFutures.add(session .executeAsync(getVideoByIDPreparedStatement .bind(videoId))); } } } }, executor);}

Performing a Read with a Listener

public Video getVideoByIdUsingQueryBuilder(String videoId) {

Video video = new Video();

Query query = select().all().from("videodb", "videos") .where(eq("videoId", videoId)).limit(10);

query.setConsistencyLevel(ConsistencyLevel.ONE);

ResultSet rs = session.execute(query); for (Row row : rs) {

video.setVideoid(row.getUUID("videoid")); video.setVideoname(row.getString("videoName")); video.setUsername(row.getString("username")); video.setDescription(row.getString("description")); } return video;}

Using the Query Builder

Load Balancing

Policy

Node

Node

Node

Health Monitor

Load Balancing and Failover

ReconnectionNotifications

Client

RetryPolicy

ResponseDispatcher

1 3

2

4

5

6

Node

Node

NodeClient

Datacenter B

Node

Node

Node

Client

Client

Client

Client

Client

Datacenter ALocal nodes are queried first, if non are available, the request will be sent to a remote node.

Multi Datacenter Load Balancing

Multi Datacenter Load Balancing

Node

Node

ReplicaNode

Client Node

NodeReplica

Replica

Nodes that own a Replica of the data being read or written by the query will be contacted first.

contactPoints = {“10.0.0.1”,”10.0.0.2”}

keyspace = “videodb”

public VideoDbBasicImpl(List<String> contactPoints, String keyspace) {

cluster = Cluster .builder() .addContactPoints(! contactPoints.toArray(new String[contactPoints.size()])) .withLoadBalancingPolicy(Policies.defaultLoadBalancingPolicy()) .build();

session = cluster.connect(keyspace); }

Create your own policy!

Add a Load Balancing Policy

Client

If the requested Consistency Level cannot be reached (QUORUM here), a second request with a lower CL is sent automatically.

Node

Node Replica

Replica

NodeReplica

Downgrading Retry Policy

contactPoints = {“10.0.0.1”,”10.0.0.2”}

keyspace = “videodb”

public VideoDbBasicImpl(List<String> contactPoints, String keyspace) {

cluster = Cluster .builder() .addContactPoints(! contactPoints.toArray(new String[contactPoints.size()])) .withLoadBalancingPolicy(Policies.defaultLoadBalancingPolicy()) .withRetryPolicy(Policies.defaultRetryPolicy()) .build();

session = cluster.connect(keyspace); }

Create your own policy!

Specify a Retry Policy

public enum Gender {

@EnumValue("m") MALE, @EnumValue("f") FEMALE;}

@Table(name = "user")public class User { @PartitionKey @Column(name = "user_id") private String userId; private String name; private String email; private Gender gender;}

Object Mapping

DevCenter an IDE for Developers

Questions ?

Recommended