51
0 to App faster with NodeJS and Ruby Rebecca Mills @RebccaMills Patrick McFadin @PatrickMcFadin

Cassandra Day Chicago 2015: 0 to App Faster with Node.js and Ruby

Embed Size (px)

Citation preview

0 to App faster with NodeJS and RubyRebecca Mills @RebccaMills

Patrick McFadin @PatrickMcFadin

The situation

• REST interface to Cassandra data • Support CRUD operations • It’s Friday and…

2

Umm yeah, and I’m going to need that by Monday morning.

Choose your language

• Support for all Cassandra features • Easy to work with • Performant

3

Fast prototype

4

5

Need to get this done now

6

DataStax NodeJS driver

• Works with current OSS Cassandra • Apache Licensed

7

https://github.com/datastax/nodejs-driver

Based on node-cassandra-cql by Jorge Bay

Get it!

8

> npm install cassandra-driver

DataStax Ruby driver

9

https://github.com/datastax/ruby-driver

• Works with current OSS Cassandra • Apache Licensed

Based on cql-rb by Theo Hultberg (@iconara)

Get it!

10

> gem install cassandra-driver

DataStax Cassandra Drivers

• Load Balancing Policies • Retry Policies • Asynchronous • Prepared statements • Connection and cluster management

11

A Cassandra Driver should have…

On to the code!

• RESTful web server • Need a few helpers

12

+ +

REST methods

• Operates on the “USERS” table in Cassandra

13

POST Insert a user

GET Select a user

PUT Update a user

DELETE Delete a user

CREATE TABLE users ( firstname text, lastname text, age int, email text, city text, PRIMARY KEY (lastname));

14

Connection for NodeJS• Express as the web server • body-parser to get POST data

15

var client = new cassandra.Client({ contactPoints: ['127.0.0.1'], keyspace: 'demo', policies: { retry: new cassandra.policies.retry.RetryPolicy(), loadBalancing: new cassandra.policies.loadBalancing.DCAwareRoundRobinPolicy('datacenter1') } });

var express = require('express') var bodyParser = require('body-parser'); var cassandra = require('cassandra-driver');

var client = new cassandra.Client({ contactPoints: ['127.0.0.1'], keyspace: 'demo', policies: { retry: new cassandra.policies.retry.RetryPolicy(), loadBalancing: new cassandra.policies.loadBalancing.DCAwareRoundRobinPolicy('datacenter1') } });

Connection for NodeJS• Express as the web server • body-parser to get POST data

16

var express = require('express') var bodyParser = require('body-parser'); var cassandra = require('cassandra-driver');

var client = new cassandra.Client({ contactPoints: ['127.0.0.1'], keyspace: 'demo', policies: { retry: new cassandra.policies.retry.RetryPolicy(), loadBalancing: new cassandra.policies.loadBalancing.DCAwareRoundRobinPolicy('datacenter1') } });

Connection for NodeJS• Express as the web server • body-parser to get POST data

17

var express = require('express') var bodyParser = require('body-parser'); var cassandra = require('cassandra-driver');

var client = new cassandra.Client({ contactPoints: ['127.0.0.1'], keyspace: 'demo', policies: { retry: new cassandra.policies.retry.RetryPolicy(), loadBalancing: new cassandra.policies.loadBalancing.DCAwareRoundRobinPolicy('datacenter1') } });

Connection for NodeJS• Express as the web server • body-parser to get POST data

18

var express = require('express') var bodyParser = require('body-parser'); var cassandra = require('cassandra-driver');

Load balancing

19

Client 10.0.0.1 00-25

10.0.0.4 76-100

10.0.0.2 26-50

10.0.0.3 51-75

SELECT firstNameFROM usersWHERE lastname = ‘mills’;

datacenter1

datacenter1

Node Primary Replica Replica

10.0.0.1 00-25 76-100 51-75

10.0.0.2 26-50 00-25 76-100

10.0.0.3 51-75 26-50 00-25

10.0.0.4 76-100 51-75 26-50

loadBalancing: new DCAwareRoundRobinPolicy('datacenter1')

Insert a user with a POST

20

app.post('/users', function (req, res) { var lastname = req.body.lastname; var age = req.body.age; var city = req.body.city; var email = req.body.email; var firstname = req.body.firstname; var query = "INSERT INTO users (lastname, age, city, email, firstname) VALUES ( ?,?,?,?,?)"; var params = [lastname, age, city, email, firstname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Inserted"); } else { res.sendStatus(404) } })})

Insert a user with a POST

21

app.post('/users', function (req, res) { var lastname = req.body.lastname; var age = req.body.age; var city = req.body.city; var email = req.body.email; var firstname = req.body.firstname; var query = "INSERT INTO users (lastname, age, city, email, firstname) VALUES ( ?,?,?,?,?)"; var params = [lastname, age, city, email, firstname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Inserted"); } else { res.sendStatus(404) } })})

Insert a user with a POST

22

app.post('/users', function (req, res) { var lastname = req.body.lastname; var age = req.body.age; var city = req.body.city; var email = req.body.email; var firstname = req.body.firstname; var query = "INSERT INTO users (lastname, age, city, email, firstname) VALUES ( ?,?,?,?,?)"; var params = [lastname, age, city, email, firstname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Inserted"); } else { res.sendStatus(404) } })})

Insert a user with a POST

23

app.post('/users', function (req, res) { var lastname = req.body.lastname; var age = req.body.age; var city = req.body.city; var email = req.body.email; var firstname = req.body.firstname; var query = "INSERT INTO users (lastname, age, city, email, firstname) VALUES ( ?,?,?,?,?)"; var params = [lastname, age, city, email, firstname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Inserted"); } else { res.sendStatus(404) } })})

Select user with GET

24

app.get('/users/:lastname',function (req, res) { var query = "SELECT lastname, age, city, email, firstname FROM users WHERE lastname= ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err){ if ( result.rows.length > 0 ) { var user = result.rows[0]; console.log("name = %s, age = %d", user.firstname, user.age); res.send(user) } else { res.sendStatus(404); } } }); })

Select user with GET

25

app.get('/users/:lastname',function (req, res) { var query = "SELECT lastname, age, city, email, firstname FROM users WHERE lastname= ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err){ if ( result.rows.length > 0 ) { var user = result.rows[0]; console.log("name = %s, age = %d", user.firstname, user.age); res.send(user) } else { res.sendStatus(404); } } }); })

Select user with GET

26

app.get('/users/:lastname',function (req, res) { var query = "SELECT lastname, age, city, email, firstname FROM users WHERE lastname= ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err){ if ( result.rows.length > 0 ) { var user = result.rows[0]; console.log("name = %s, age = %d", user.firstname, user.age); res.send(user) } else { res.sendStatus(404); } } }); })

Select user with GET

27

app.get('/users/:lastname',function (req, res) { var query = "SELECT lastname, age, city, email, firstname FROM users WHERE lastname= ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err){ if ( result.rows.length > 0 ) { var user = result.rows[0]; console.log("name = %s, age = %d", user.firstname, user.age); res.send(user) } else { res.sendStatus(404); } } }); })

Select user with GET

28

app.get('/users/:lastname',function (req, res) { var query = "SELECT lastname, age, city, email, firstname FROM users WHERE lastname= ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err){ if ( result.rows.length > 0 ) { var user = result.rows[0]; console.log("name = %s, age = %d", user.firstname, user.age); res.send(user) } else { res.sendStatus(404); } } }); })

Update a user with PUT

29

app.put('/users/:lastname', function (req, res) { var age = req.body.age; console.log("lastname = " + req.params.lastname + ", age= " + age); var query = "UPDATE users SET age = ? WHERE lastname = ?"; var params = [age, req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Updated"); } else { res.sendStatus(404) } }); })

Remove a user with DELETE

30

app.delete('/users/:lastname', function (req, res) { var query = "DELETE FROM users WHERE lastname = ?"; var params = [req.params.lastname]; client.execute(query, params, {prepare: true}, function (err, result) { if (!err) { res.send("Deleted"); } else { res.sendStatus(404) } }); })

31

Connection with Ruby

• Sinatra as the web server • JSON for returning formatted results

32

cluster = Cassandra.cluster( :hosts => ['127.0.01'], :load_balancing_policy => Cassandra::LoadBalancing::Policies::RoundRobin.new, :retry_policy => Cassandra::Retry::Policies::Default.new, logger: log) keyspace = 'demo'session = cluster.connect(keyspace)

require 'sinatra'require 'JSON'require 'cassandra'require 'logger'

Connection with Ruby

• Sinatra as the web server • JSON for returning formatted results

33

cluster = Cassandra.cluster( :hosts => ['127.0.01'], :load_balancing_policy => Cassandra::LoadBalancing::Policies::RoundRobin.new, :retry_policy => Cassandra::Retry::Policies::Default.new, logger: log) keyspace = 'demo'session = cluster.connect(keyspace)

require 'sinatra'require 'JSON'require 'cassandra'require 'logger'

Connection with Ruby

• Sinatra as the web server • JSON for returning formatted results

34

cluster = Cassandra.cluster( :hosts => ['127.0.01'], :load_balancing_policy => Cassandra::LoadBalancing::Policies::RoundRobin.new, :retry_policy => Cassandra::Retry::Policies::Default.new, logger: log) keyspace = 'demo'session = cluster.connect(keyspace)

require 'sinatra'require 'JSON'require 'cassandra'require 'logger'

Retry Policies

• Retry requests on server errors • Write Timeout • Read Timeout • Unavailable

35

Connection with Ruby

• Sinatra as the web server • JSON for returning formatted results

36

cluster = Cassandra.cluster( :hosts => ['127.0.01'], :load_balancing_policy => Cassandra::LoadBalancing::Policies::RoundRobin.new, :retry_policy => Cassandra::Retry::Policies::Default.new, logger: log) keyspace = 'demo'session = cluster.connect(keyspace)

require 'sinatra'require 'JSON'require 'cassandra'require 'logger'

Insert a user with a POST

37

post '/users' do begin session.execute(userInsertStatement, :arguments => [params[:firstname], params[:lastname], params[:age].to_i, params[:city], params[:email]]) "Inserted" rescue Exception => e log.error 'Error in insert a user' log.error(e) halt(404) endend

userInsertStatement = session.prepare("INSERT INTO users (firstname, lastname, age, city, email) VALUES (?,?,?,?,?)")

Insert a user with a POST

38

post '/users' do begin session.execute(userInsertStatement, :arguments => [params[:firstname], params[:lastname], params[:age].to_i, params[:city], params[:email]]) "Inserted" rescue Exception => e log.error 'Error in insert a user' log.error(e) halt(404) endend

userInsertStatement = session.prepare("INSERT INTO users (firstname, lastname, age, city, email) VALUES (?,?,?,?,?)")

Insert a user with a POST

39

post '/users' do begin session.execute(userInsertStatement, :arguments => [params[:firstname], params[:lastname], params[:age].to_i, params[:city], params[:email]]) "Inserted" rescue Exception => e log.error 'Error in insert a user' log.error(e) halt(404) endend

userInsertStatement = session.prepare("INSERT INTO users (firstname, lastname, age, city, email) VALUES (?,?,?,?,?)")

Select user with GET

40

get '/users/:lastname' do begin result = session.execute(userSelectStatement, :arguments => [params[:lastname]]) if result.size < 1 halt(404) end result.first.to_json rescue Exception => e log.error 'Error in select a user' log.error(e) halt(404) endend

userSelectStatement = session.prepare("SELECT firstname,lastname, age, email, city FROM users where lastname = ?")

Select user with GET

41

get '/users/:lastname' do begin result = session.execute(userSelectStatement, :arguments => [params[:lastname]]) if result.size < 1 halt(404) end result.first.to_json rescue Exception => e log.error 'Error in select a user' log.error(e) halt(404) endend

userSelectStatement = session.prepare("SELECT firstname,lastname, age, email, city FROM users where lastname = ?") …

Select user with GET

42

get '/users/:lastname' do begin result = session.execute(userSelectStatement, :arguments => [params[:lastname]]) if result.size < 1 halt(404) end result.first.to_json rescue Exception => e log.error 'Error in select a user' log.error(e) halt(404) endend

userSelectStatement = session.prepare("SELECT firstname,lastname, age, email, city FROM users where lastname = ?")

Select user with GET

43

get '/users/:lastname' do begin result = session.execute(userSelectStatement, :arguments => [params[:lastname]]) if result.size < 1 halt(404) end result.first.to_json rescue Exception => e log.error 'Error in select a user' log.error(e) halt(404) endend

userSelectStatement = session.prepare("SELECT firstname,lastname, age, email, city FROM users where lastname = ?")

Quick note on Async

• Generates a Future • Non-blocking until get

44

future = session.execute_async(statement)# register success listenerfuture.on_success do |rows| rows.each do |row| puts "#{row["artist"]}: #{row["title"]} / #{row["album"]}" endend

Quick note on Async

• Generates a Future • Non-blocking until get

45

future = session.execute_async(statement)# register success listenerfuture.on_success do |rows| rows.each do |row| puts "#{row["artist"]}: #{row["title"]} / #{row["album"]}" endend

Quick note on Async

• Generates a Future • Non-blocking until get

46

future = session.execute_async(statement)# register success listenerfuture.on_success do |rows| rows.each do |row| puts "#{row["artist"]}: #{row["title"]} / #{row["album"]}" endend

Update a user with PUT

47

put '/users' do begin session.execute(userUpdateStatement, :arguments => [params[:age].to_i, params[:lastname]]) "Updated" rescue Exception => e log.error 'Error in update a user' log.error(e) halt(404) endend

userUpdateStatement = session.prepare("UPDATE users SET age = ? WHERE lastname = ?")

Remove a user with DELETE

48

delete '/users/:lastname' do begin session.execute(userDeleteStatement, :arguments => [params[:lastname]]) "Deleted" rescue Exception => e log.error 'Error in delete a user' log.error(e) halt(404) endend

userDeleteStatement = session.prepare("DELETE FROM users WHERE lastname = ?")

Made it!

49

Nice code. Have a beer.

Get the code! Try yourself!

50

https://github.com/beccam/rest_server_ruby

https://github.com/beccam/rest_server_nodejs

NodeJS Code

Ruby Code

Questions?Don’t forget to follow us on Twitter for more:

@RebccaMills @PatrickMcFadin