Skip to content
shorrockin edited this page Sep 14, 2010 · 25 revisions

Cascal Introduction

Cascal is a simple Cassandra library built on the Scala language that provides a very consistent and simple means by which you can interact with the Cassandra system.

Cascal has several goals including:

  • Construct a way to use the Thrift library in a manner more conducive with the Scala language.
  • Ensure that no Cassandra specific thrift libraries need to be learned or used.
  • Create a type safe model that mimics the Cassandra system while providing various abstractions on top.
  • Built in support for connection Pooling.
  • 100% usable with Maven – No need to hunt down jars.

Installation Instructions

To get Cascal you can either use Maven, or get the sources directly from GitHub and build it yourself. To Use maven you need to first add the shorrockin.com maven repository, then add the dependency. The XML for this is as follows:


  <dependencies>
    <dependency>
      <groupId>com.shorrockin</groupId>
      <artifactId>cascal</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>

  <repositories>
    <repository>
      <id>shorrockin.com</id>
      <name>Shorrockin Repository</name>
      <url>http://maven.shorrockin.com/</url>
    </repository>
  </repositories>

Once you’ve added this to your pom.xml file you should be ready to start using Cascal.

Usage Instructions

Using Cascal is quite simple, you create a path of immutable structures to model what you want to work with, then use this object with a Cascal session to perform a variety of different functions.

Creating Cascal Paths

The Cascal model mirrors the Cassandra data model with several supertypes on top to identify common features across constructs. While familiarity with Cassandra will help with Cascal usage, it is not required. The terms objects referenced within this document are:

  • Keyspace: If you consider Cassandra as a 4 or 5 dimensional map, the Keyspace is the first dimension of this map.
  • ColumnFamily: If you continue to think of Cassandra as a map, the column ColumnFamily can be thought of as the 2nd dimension of the map.
    • StandardColumnFamily: column families come in two types. A standard column family is used when you wish to model a 4 dimension map. That is a map which follows Keyspace → StandardColumnFamily → StandardKey → Column.
    • SuperColumnFamily: the second type of column family. A super column family is used when you wish to model a 5 dimension map. That is a map which follows Keyspace → SuperColumnFamily → SuperKey → SuperColumn → Column.
  • Key: a key is the 3rd dimension of our map. It is a string identifier which maps to either a Column or a SuperColumn depending on the family in which its used.
    • StandardKey: a generalization of a Key that is used in a StandardColumnFamily.
    • SuperKey: a generalization of a Key that is used in SuperColumnFamily.
  • Column: the 4th (if used in a StandardColumnFamily) or 5th (if used in a SuperColumnFamily) dimension of the hash. Contains a name, value, and timestamp.
  • SuperColumn: if using a SuperColumnFamily the super column is the 4th dimension of the hash. Contains many ordered according to the Cassandra order configuration.
  • ColumnContainer: a categorization type which is that the object holds some typ of Column, is either a Key (standard or super) or a Supercolumn.
  • Gettable: a categorization type which denotes that an object may be used in the Session.get() method. Will be either a Column or a SuperColumn. looking for a better name.

A visual representation of the information above:


While this looks complicated, much of this information obvious when you start to use Cascal and think of Cassandra as a large hash. All paths are created using the \ method, and must begin with a Keyspace.

For example the following showcases how to create various objects in Cascal (using the Scala REPL):


scala> import com.shorrockin.cascal.utils.Conversions._
import com.shorrockin.cascal.utils.Conversions._

scala> val standardColumnFamily = "Keyspace" \ "ColumnFamily"
standardColumnFamily: com.shorrockin.cascal.model.StandardColumnFamily = StandardColumnFamily(ColumnFamily,Keyspace(Keyspace))

scala> val superColumnFamily = "Keyspace" \\ "SuperColumnFamily"
superColumnFamily: com.shorrockin.cascal.model.SuperColumnFamily = SuperColumnFamily(SuperColumnFamily,Keyspace(Keyspace))

scala> val standardKey = standardColumnFamily \ "1"
standardKey: com.shorrockin.cascal.model.StandardKey = StandardKey(1,StandardColumnFamily(ColumnFamily,Keyspace(Keyspace)))

scala> val superKey = superColumnFamily \ "1"
superKey: com.shorrockin.cascal.model.SuperKey = SuperKey(1,SuperColumnFamily(SuperColumnFamily,Keyspace(Keyspace)))

scala> val standardColumn = standardKey \ ("Hello", "World")
standardColumn: com.shorrockin.cascal.model.Column[com.shorrockin.cascal.model.StandardKey] = Column([B@55ca6954,[B@5bc935cc,1270311661906,StandardKey(1,StandardColumnFamily(ColumnFamily,Keyspace(Keyspace))))

scala> val standardColumnInlined = "Keyspace" \ "ColumnFamily" \ "Hello" \ "World"
standardColumn3: com.shorrockin.cascal.model.Column[com.shorrockin.cascal.model.StandardKey] = Column([B@49233bdc,null,1270311740990,StandardKey(Hello,StandardColumnFamily(ColumnFamily,Keyspace(Keyspace))))

scala> import com.shorrockin.cascal.utils.UUID
import com.shorrockin.cascal.utils.UUID

scala> val superKey = superColumnFamily \ "super key"
superKey: com.shorrockin.cascal.model.SuperKey = SuperKey(superkey,SuperColumnFamily(SuperColumnFamily,Keyspace(Keyspace)))

scala> val superColumn = superKey \ UUID()
superColumn: com.shorrockin.cascal.model.SuperColumn = SuperColumn([B@6127ffd7,SuperKey(superkey,SuperColumnFamily(SuperColumnFamily,Keyspace(Keyspace))))

scala> val superColumnColumn = superColumn \ ("Hello", "World")
superColumnColumn: com.shorrockin.cascal.model.Column[com.shorrockin.cascal.model.SuperColumn] = Column([B@1a46a171,[B@13110f31,1270311872432,SuperColumn([B@6127ffd7,SuperKey(superkey,SuperColumnFamily(SuperColumnFamily,Keyspace(Keyspace)))))

In this example we use the Conversions to implicitly convert a String into a Keyspace, as well as to implicitly convert strings into bytes for the objects which require them. We also use the UUID object to easily create UUIDs which can be used as Cassandra TimeUUID.

Once we have a keyspace all sub-values are chained together using the \ method. The return object always contains a reference to the object which created it – parent objects however do not have references to their children. The objects returned have a fairly complex type model that – while may not be readable allows for great flexibility once you start using these objects with the cascal Session.

Creating A Cascal Session

A cascal session holds a connection to the Cassandra DB. It can either be created explicitly, or created through the provided SessionPool. For all but the most basic examples it is recommended that you use the SessionPool.

The session pool takes in a sequence of host objects and a pool parameter object. Once you have a reference to a SessionPool you can call the borrow method, passing in a function, to execute some Cassandra logic.


  val hosts  = Host("localhost", 9160, 250) :: Nil
  val params = new PoolParams(10, ExhaustionPolicy.Fail, 500L, 6, 2)
  val pool   = new SessionPool(hosts, params, Consistency.One)  

  pool.borrow { session =>
    log.debug("Count Value: " + session.count("Test" \ "Standard" \ "1"))
  }

Using A Cascal Session

Cascal sessions support a variety of operations, including:

  • get: takes in a column (with only name populated) or a super column.
    • column returns a column.
    • super column returns a sequence of columns.
  • insert: takes in and inserts a column.
  • count: counts the number of entries in the specified column container.
  • remove: removes the specified column container, or column.
  • list: performs different types of lists (slices) based on the object provided, examples are:
    • lists the contents of the specified column container using the specified predicate.
    • lists the contents of all sequence of keys
    • lists the contents of a range of keys
  • batch: performs a batch function. Takes in a sequence of Operations. Operations are either:
    • Insert – an insert operation takes in a column to insert.
    • Delete – deletes a column container using the specified predicate.

All methods have multiple method signatures which allow you to control in various levels of details (predicate, consistency, etc) what you want to retrieve.

The scaladocs should provide more information on all the various method signatures and objects used in Session.

Cascal Examples

The following are some usage examples on how to use Cascal. Most of these examples assume session already exists and the Conversions object has been imported for implicit conversion of string → bytes and string → column family:

Insertion


  session.insert("Test" \ "Standard" \ "Key" \ ("ColumnName", "ColumnValue") // standard column family
  session.insert("Test" \\ "Super" \ "Key" \ "SuperColumn" \ ("ColumnName", "ColumnValue") // super column family

Get


  // returns Option[Column]
  session.get("Test" \ "Standard" \ "Key" \ "ColumnName")  // standard column family
  session.get("Test" \\ "Super" \ "Key" \ "SuperColumn" \ "ColumnName")  // super column family

  // returns Option[Seq[Column]]
  session.get("Test" \\ "Super" \ "Key" \ "SuperColumn") // super column family

Count


  session.count("Test" \ "Standard" \ "Key") // standard column family
  session.count("Test" \\ "Super" \ "Key") // super column family
  session.count("Test" \\ "Super" \ "Key" \ "SuperColumn") // super column family

Remove


  session.remove("Test" \ "Standard" \ "Key" \ "Column") // standard column family
  session.remove("Test" \ "Standard" \ "Key") // standard column family
  session.remove("Test" \\ "Super" \ "Key") // super column family
  session.remove("Test" \\ "Super" \ "Key" \ "SuperColumn" \ "Column") // super column family
  session.remove("Test" \\ "Super" \ "Key" \ "SuperColumn") // super column family

List


  val key = "Test" \ "Standard" \ "Key"
  session.list(key) // returns Seq[Column]
  session.list(key, RangePredicate("Column-1", "Column-3"))
  session.list(key, ColumnPredicate(List("Column-1", "Column-3")))

  val superKey = "Test" \\ "Super" \ "Key"
  session.list(superKey) // returns Map[SuperColumn, Seq[Column]]

  val superCol = "Test" \\ "Super" \ "Key" \ "SuperColumn"
  session.list(superCol) // returns Seq[Column]

  val family = "Test" \ "Standard"
  session.list(family, KeyRange("Key1", "Key3", 100)

Batch Insert


    val key  = "Test" \ "Standard" \ UUID()
    val col1 = key \ ("Column-1", "Value-1")
    val col2 = key \ ("Column-2", "Value-2")
    val col3 = key \ ("Column-3", "Value-3")

    session.batch(Insert(col1) :: Insert(col2) :: Insert(col3))

Batch Delete


    val key  = "Test" \ "Standard" \ UUID()
    val col3 = key \ ("Column-3", "Value-3")

    session.batch(Delete(key, ColumnPredicate("Column-1" :: "Column-2" :: Nil)) :: Insert(col3))

Clone this wiki locally