Skip to content
This repository has been archived by the owner on Sep 18, 2021. It is now read-only.

Documentation cleanup: Fresh links, improved formatting, build.sbt examp... #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 44 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
Cassie
======

Cassie is a small, lightweight Cassandra client built on
[Finagle](http://github.com/twitter/finagle) with with all that provides plus column name/value
encoding and decoding.
Cassie is a small, lightweight Cassandra client built on [Finagle](http://github.com/twitter/finagle) with with all that provides plus column name/value encoding and decoding.

It is heavily used in production at Twitter so such be considered stable, yet it is incomplete in that it doesn't support the full feature set of Cassandra and will continue to evolve.

Expand All @@ -21,17 +19,27 @@ to make it easier.
Let's Get This Party Started
----------------------------

In your [simple-build-tool](http://code.google.com/p/simple-build-tool/) project
In your [simple-build-tool](https://github.com/harrah/xsbt/) project
file, add Cassie as a dependency:

val twttr = "Twitter's Repository" at "http://maven.twttr.com/"
val cassie = "com.twitter" % "cassie" % "0.19.0"

Or, for your _build.sbt:_

resolvers += "Twitter's Repository" at "http://maven.twttr.com/"

libraryDependencies += "com.twitter" % "cassie" % "0.19.0"
excludeAll(
ExclusionRule(organization = "com.sun.jdmk"),
ExclusionRule(organization = "com.sun.jmx"),
ExclusionRule(organization = "javax.jms")
)

Finagle
-------

Before going further, you should probably learn about Finagle and its paradigm for asynchronous
computing– https://github.com/twitter/finagle.
Before going further, you should probably learn about Finagle and its paradigm for asynchronous computing--[https://github.com/twitter/finagle](https://github.com/twitter/finagle.)

Connecting To Your Cassandra Cluster
------------------------------------
Expand All @@ -50,9 +58,9 @@ connection pools and do retries:

(If you have some nodes with dramatically different latency—e.g., in another
data center–or if you have a huge cluster, you can disable keyspace mapping
via "mapHostsEvery(0.minutes)" in which case clients will connect directly to
via `mapHostsEvery(0.minutes)` in which case clients will connect directly to
the seed hosts passed to "new Cluster".)

"
A Quick Note On Timestamps
--------------------------

Expand Down Expand Up @@ -106,31 +114,29 @@ Now that you've got your `ColumnFamily`, you can read some data from Cassandra:

people.getColumn("codahale", "name")

`getColumn` returns an `Future[Option[Column[Name, Value]]]` where `Name` and `Value` are the type
parameters of the `ColumnFamily`. If the row or column doesn't exist, `None` is returned. Explaining
Futures is out of scope for this README, go the Finagle docs to learn more. But in essence you can
do this:
`getColumn` returns an `Future[Option[Column[Name, Value]]]` where `Name` and `Value` are the type parameters of the `ColumnFamily`. If the row or column doesn't exist, `None` is returned.

people.getColumn("codahale", "name") map {
_ match {
case col: Some(Column[String, String]) => # we have data
case None => # there was no column
}
} handle {
case e => {
# there was an exception, do something about it
Explaining Futures is out of scope for this README, go the Finagle docs to learn more. But in essence you can do this:

people.getColumn("codahale", "name") map {
_ match {
case col: Some(Column[String, String]) => # we have data
case None => # there was no column
}
} handle {
case e => {
# there was an exception, do something about it
}
}
}

This whole block returns a Future which will be satisfied when the thrift rpc is done and the
callbacks have run.

Anyway, continuingyou can also get a set of columns:
Anyway, continuing--you can also get a set of columns:

people.getColumns("codahale", Set("name", "motto"))

This returns a `Future[java.util.Map[Name, Column[Name, Value]]]`, where each column is mapped by
its name.
This returns a `Future[java.util.Map[Name, Column[Name, Value]]]`, where each column is mapped by its name.

If you want to get all columns of a row, that's cool too:

Expand All @@ -141,26 +147,23 @@ Cassie also supports multiget for columns and sets of columns:
people.multigetColumn(Set("codahale", "darlingnikles"), "name")
people.multigetColumns(Set("codahale", "darlingnikles"), Set("name", "motto"))

`multigetColumn` returns a `Future[Map[Key, Map[Name, Column[Name, Value]]]]` whichmaps row keys to
column names to columns.
`multigetColumn` returns a `Future[Map[Key, Map[Name, Column[Name, Value]]]]` which maps row keys to column names to columns.


Asynchronous Iteration Through Rows and Columns
-----------------------------------------------

NOTE: This is new/experimental and likely to change in the future.

Cassie provides functionality for iterating through the rows of a column family and columns in a
row. This works with both the random partitioner and the order-preserving partitioner, though
iterating through rows in the random partitioner had undefined order.
Cassie provides functionality for iterating through the rows of a column family and columns in a row. This works with both the random partitioner and the order-preserving partitioner, though iterating through rows in the random partitioner had undefined order.

You can iterate over every column of every row:

val finished = cf.rowsIteratee(100).foreach { case(key, columns) =>
println(key) //this function is executed async for each row
println(cols)
}
finished() //this is a Future[Unit]. wait on it to know when the iteration is done
val finished = cf.rowsIteratee(100).foreach { case(key, columns) =>
println(key) //this function is executed async for each row
println(cols)
}
finished() //this is a Future[Unit]. wait on it to know when the iteration is done

This gets 100 rows at a time and calls the above partial function on each one.

Expand Down Expand Up @@ -210,29 +213,16 @@ Or even a row:
Generating Unique IDs
---------------------

If you're going to be storing data in Cassandra and don't have a naturally unique piece of data to
use as a key, you've probably looked into UUIDs. The only problem with UUIDs is that they're mental,
requiring access to MAC addresses or Gregorian calendars or POSIX ids. In general, people want UUIDs
which are:
If you're going to be storing data in Cassandra and don't have a naturally unique piece of data to use as a key, you've probably looked into UUIDs. The only problem with UUIDs is that they're mental, requiring access to MAC addresses or Gregorian calendars or POSIX ids. In general, people want UUIDs which are:

* Unique across a large set of workers without requiring coordination.
* Partially ordered by time.

Cassie's `LexicalUUID`s meet these criteria. They're 128 bits long. The most significant 64 bits are
a timestamp value (from Cassie's strictly-increasing `Clock` implementation). The least significant
64 bits are a worker ID, with the default value being a hash of the machine's hostname.

When sorted using Cassandra's `LexicalUUIDType`, `LexicalUUID`s will be partially ordered by time --
that is, UUIDs generated in order on a single process will be totally ordered by time; UUIDs
generated simultaneously (i.e., within the same clock tick, given clock skew) will not have a
deterministic order; UUIDs generated in order between single processes (i.e., in different clock
ticks, given clock skew) will be totally ordered by time.

See *Lamport. Time, clocks, and the ordering of events in a distributed system. Communications of
the ACM (1978) vol. 21 (7) pp. 565* and *Mattern. Virtual time and global states of distributed
systems. Parallel and Distributed Algorithms (1989) pp. 215–226* for a more thorough discussion.
Cassie's `LexicalUUID`s meet these criteria. They're 128 bits long. The most significant 64 bits are a timestamp value (from Cassie's strictly-increasing `Clock` implementation). The least significant 64 bits are a worker ID, with the default value being a hash of the machine's hostname.

When sorted using Cassandra's `LexicalUUIDType`, `LexicalUUID`s will be partially ordered by time--that is, UUIDs generated in order on a single process will be totally ordered by time; UUIDs generated simultaneously (i.e., within the same clock tick, given clock skew) will not have a deterministic order; UUIDs generated in order between single processes (i.e., in different clock ticks, given clock skew) will be totally ordered by time.

See *Lamport. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM (1978) vol. 21 (7) pp. 565* and *Mattern. Virtual time and global states of distributed systems. Parallel and Distributed Algorithms (1989) pp. 215–226* for a more thorough discussion.

Things What Ain't Done Yet
==========================
Expand All @@ -252,7 +242,7 @@ Many thanks to (pre twitter fork):
License
-------

Copyright (c) 2010 Coda Hale
Copyright (c) 2011-2012 Twitter, Inc.
* Copyright (c) 2010 Coda Hale
* Copyright (c) 2011-2012 Twitter, Inc.

Published under The Apache 2.0 License, see LICENSE.
Published under The Apache 2.0 License, see LICENSE.