Getting Started with Cassandra & Python

presentation by:

Philip Doctor & Amber Doctor

DePy 2015 Conference

Tornado

Tornado is a Python web framework and asynchronous networking library. By using non-blocking network I/O, Tornado can scale to tens of thousands of open connections, making it ideal for long pollingWebSockets, and other applications that require a long-lived connection to each user.

Cassandra

The Apache Cassandra database is the right choice when you need scalability and high availability without compromising performance. Linear scalability and proven fault-tolerance on commodity hardware or cloud infrastructure make it the perfect platform for mission-critical data.

Set Up Tornado

  • Create a project folder
    • make sure there are no spaces in its name or path
  • Install virtualenv
    • $ pip install virtualenv
  • Create your virtualenv
    • $ virtualenv ct
  • Activate virtualenv
    • $ source ct/bin/activate
  • Install Tornado
    • $ pip install tornado

Hello DePy

from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, Application, url

class HelloHandler(RequestHandler):
    def get(self):
        self.write("Hello DePy 2015!")

def make_app():
    return Application([
        url(r"/", HelloHandler),
        ])

def main():
    app = make_app()
    app.listen(8889)
    IOLoop.current().start()

if __name__ == "__main__":
	main()

Set Up Cassandra

  • While in your virtualenv ct
  • Install CCM dependancies
    • $ pip install pyYaml
    • $ pip install six
    • $ brew install ant
    • $ pip install psutil
  • Create a folder to clone the CCM git repo
  • Clone CCM
    • $ git clone https://github.com/pcmanus/ccm.git
  • Install Cassandra Driver for Python
    • $ pip install cassandra-driver

Start Cassandra Server

  • Create your cluster
    • $ ccm create cassandra_dev_cluster -n 3 -v 2.1.2
    • $ sudo ifconfig lo0 alias 127.0.0.2 up
    • $ sudo ifconfig lo0 alias 127.0.0.3 up
  • Start your cluster
    • $ ccm start

Todo List Database

create_database.cql
CREATE KEYSPACE tornado_app
  WITH REPLICATION = 
{ 'class' : 'SimpleStrategy', 
  'replication_factor' : 2 };

use tornado_app;

CREATE TABLE todo_list (
  user_name text,
  id uuid,
  todo text,
  is_completed boolean,
  PRIMARY KEY((user_name), id)
);
ccm node1 cqlsh < create_database.cql 

Run above script from command line:

CQL

Cassandra Query Language
ccm node1 cqlsh
cqlsh> insert into tornado_app.todo_list (user_name, id, todo, is_completed) 
        values ('DePy2015', f6728f9c-0326-11e5-a758-b8e85644fe9b, 'test to do', false);
cqlsh> select * from tornado_app.todo_list;
cqlsh> select * from tornado_app.todo_list where user_name = 'DePy2015';
cqlsh> drop KEYSPACE tornado_app;
cqlsh> exit;

Sample CQL 

Connect to database

Connect App to DB

todo_list.py
from cassandra.cluster import Cluster

# note: without defining parameters Cluster() connects to localhost
cassandra_cluster = Cluster()

# note: you don't have to provide a keyspace argument below
# but then you'd have to specify it in every query
session = cassandra_cluster.connect('tornado_app')
# use this if connected with keyspace as an argument
session.execute('SELECT id, todo, is_completed FROM todo_list 
    WHERE user_name = %s', (self.user_name,))

# if keyspace was not used as a connection argument,
# specify the keyspace in the query
session.execute('SELECT id, todo, is_completed FROM tornado_app.todo_list 
    WHERE user_name = %s', (self.user_name,))

Simple Get

todo_list.py
class ToDoList(RequestHandler):
    user_name = "DePy2015"

def get(self):
        rows = session.execute('SELECT id, todo, is_completed 
               FROM todo_list WHERE user_name = %s', (self.user_name,))
        self.render("todo_list.html", user_name=self.user_name, todo_list=rows)

Using a Template

todo_list.html
<html><header>.....</header>
<body>
<h1>{{user_name}} To Do List</h1>

{% for item in todo_list %}
<div>
    <input type="checkbox" name="todo" id="{{item.id}}" 
                      {%if item.is_completed %}checked{% end %} >
    <label>{{item.todo}}</label>
    <button onclick="deleteCompletedItems('{{item.id}}')">Delete</button>
</div>
{% end %}

<form method="post">
    <input type="text" name="new_todo" placeholder="New To Do Item">
    <input type="submit" value="Add">
</form>

</body>
</html>

In the browser

localhost:8888

Simple Post

todo_list.html
<html><header>.....</header>

<body>.....
<form method="post">
    <input type="text" name="new_todo" placeholder="New To Do Item">
    <input type="submit" value="Add">
</form>

</body></html>
def post(self):
        new_todo = self.get_argument('new_todo', '')
        session.execute("INSERT INTO todo_list (user_name, id, todo, is_completed) 
            VALUES (%s, %s, %s, %s)", (self.user_name, uuid.uuid1(), new_todo, False))
        self.redirect('/') 
todo_list.py

Simple Put

todo_list.html
<header><script type="text/javascript">
        $(function(){
            $(":checkbox").click(function(){
                $.ajax({
                    type: 'PUT',
                    data: "todo_id=" + this.id + "&is_completed_status=" + this.checked,
                    success: function(data) {
                        location.href = location.href;
        }})})});
</script></header>
def put(self):
        todo_id = uuid.UUID(self.get_argument('todo_id', ''))
        is_completed = 
            True if self.get_argument('is_completed_status', '')==u'true' else False
        session.execute("UPDATE todo_list SET is_completed = %s 
            WHERE id = %s AND user_name = %s", (is_completed, todo_id, self.user_name))
        self.finish()
todo_list.py

Simple Delete

todo_list.html
<script>
    function deleteCompletedItems(todo_id) {
        $.ajax({
                type: 'DELETE',
                data: "todo_id=" + todo_id,
                success: function(data) {
                    location.href = location.href;
    }})};
</script>
def delete(self):
        todo_id = uuid.UUID(self.get_argument('todo_id', ''))
        session.execute("DELETE FROM todo_list WHERE id = %s 
            AND user_name = %s", (todo_id, self.user_name))
        self.finish()
todo_list.py

is_deleted flag

  • For many use cases in Cassandra you'll want to have your delete function set an is_deleted flag rather than deleting the record.
  • You  can manage your is_deleted flag just like the is_completed flag in the example To Do application.
  • This helps to keep accurate historical records but not show them.

Amber

amberdoctor@gmail.com

github.com/amberdoctor

Philip

philip.s.doctor@gmail.com

github.com/philipsdoctor

DePy 2015

Getting Started with Cassandra and Python

Getting Started with Cassandra & Python

By Amber Doctor

Getting Started with Cassandra & Python

A walk through on setting up a To Do application using Tornado, a python framework, and performing CRUD operations with Cassandra.

  • 6,196