02 Jun 2010

Your First Fluent Cassandra Application

23 Comments FluentCassandra

As your are probably aware by now if you follow my Twitter status or have looked in to some of my recent posts.  I am developing a library called FluentCassandra which is a .NET library for using the Cassandra database in a .NETty way.  The project has progressed quite nicely in the last couple of months and I am finally ready to start talking about it and giving examples on how it can be used in your applications.  So lets gets started…

Step 1)

The first thing we need to do is make sure that your machine is properly setup to run Cassandra.  Back in March I put together a jump start for Windows developers to do just that.  So if you don’t have it running on your machine already, start there.

Step 2)

The next thing we need to do is to locate and configure the database storage-conf.xml file, which was referenced in the previous steps instructions. 

  1. Open the storage-conf.xml in your favorite text editor.
  2. Add the following to the <Keyspaces /> tag in the file:
    <Keyspace Name="Blog">
        <ColumnFamily Name="Posts"
            ColumnType="Super"
            CompareWith="UTF8Type"
            CompareSubcolumnsWith="UTF8Type" />
    
        <ReplicaPlacementStrategy>org.apache.cassandra.locator.RackUnawareStrategy</ReplicaPlacementStrategy>
        <ReplicationFactor>1</ReplicationFactor>
        <EndPointSnitch>org.apache.cassandra.locator.EndPointSnitch</EndPointSnitch>
    </Keyspace>
  3. Save it.

The above configuration creates one Column Family (or table in RDBMS speak) called Posts in a Keyspace (or database in RDBMS speak) called Blog.  We are going to use this column family in our code below.

Step 3)

Next grab a copy of FluentCassandra from http://github.com/managedfusion/fluentcassandra

image

Create your your own console app or use FluentCassandra.Sandbox console app provided in the source downloaded.

Step 4)

Now for the fun part, the coding. 

The first thing we need to do is create a context for the database entities that we are going to save.  This is done with the CassandraContext.

using (var db = new CassandraContext(keyspace: "Blog", host: "localhost"))
{

The above code creates a Cassandra Context for the Blog Keyspace on our local Cassandra database.  After we have done this we want to get a reference to the family that we are going to execute our saves against.  This is done by getting the column family with the CompareWith and CompareSubcolumnWith types we specified in the above storage-conf.xml.

var family = db.GetColumnFamily<UTF8Type, UTF8Type>("Posts");

In the above code the first generic parameter is the CompareWith parameter and the second generic parameter is the CompareSubcolumnsWith parameter.  This creates the family repository that can be used to execute CRUD commands against this column family. 

Now that we have all this setup lets actually create a post record, with a key called “first-blog-post”.

// create post
dynamic post = family.CreateRecord(key: "first-blog-post");

The easiest way to accomplish this is to use the method provided in the family object for creating the properly typed record for use. This object will be used in a little while but first we need to create two super columns with the details of our blog post and the tags associated with the blog post.  This is done by using the CreateSuperColumn method on the post object we just created.

// create post details
dynamic postDetails = post.CreateSuperColumn();
postDetails.Title = "My First Cassandra Post";
postDetails.Body = "Blah. Blah. Blah. about my first post on how great Cassandra is to work with.";
postDetails.Author = "Nick Berardi";
postDetails.PostedOn = DateTimeOffset.Now;

// create post tags
dynamic tags = post.CreateSuperColumn();
tags[0] = "Cassandra";
tags[1] = ".NET";
tags[2] = "Database";
tags[3] = "NoSQL";

This creates two super column objects postDetails and tags that each contain their own set of columns.  In the case of the post details it contains information about the posts title, content body, author, and when it was posted on.  In the case of the tags it contains an array where each item in the array is a new column.  We will talk about why this works in a future post, but accept for now that it does work, even though one is used as an object with a bunch of properties and one is used as an array with a bunch of elements.

Lets now add the details and tags to our post record that we created above.

// add properties to post
post.Details = postDetails;
post.Tags = tags;

Just like the details above we are going to treat the post record as an object with properties.  This will complete our entire record that we want to save to the database.  Now lets attach it and save our record to the database.

// attach the post to the database
Console.WriteLine("attaching record");
db.Attach(post);

// save the changes
Console.WriteLine("saving changes");
db.SaveChanges();

So we have now done our first Cassandra database insert.  But that is only half the fun, lets read it back out of the database.  As with the write, we are going to use the same family object to do the read from the database.  The first thing we need to do is get the record out of the database using the same key, “first-blog-post”.

// get the post back from the database
Console.WriteLine("getting 'first-blog-post'");
dynamic getPost = family.Get("first-blog-post").FirstOrDefault();

The above code uses the LINQ-like syntax to retrieve the record.  This LINQ-like syntax can be started using the method Get on the family object.  And it then can be executed with any LINQ operation, in our case above we are using FirstOrDetault method.  The next thing we want to see is the details of the post, which can be easily retrieved using the same object structure that we put them in the database as.

// show details
dynamic getPostDetails = getPost.Details;
Console.WriteLine(
    String.Format("=={0} by {1}==\n{2}", 
        getPostDetails.Title, 
        getPostDetails.Author, 
        getPostDetails.Body
    ));

And now for the tags, which we are going to query in a way more suitable for an array.

// show tags
Console.Write("tags:");
foreach (var tag in getPost.Tags)
    Console.Write(String.Format("{0}:{1},", tag.Name, tag.Value));

Finish it off with this code, and we will be ready to run our first Cassandra application.

}

Console.Read();

Step 5)

The first thing we need to do to run our application is to make sure the database is running.  This may sound like a no-duh moment, but if you are use to SQL Server development, you really never have to make sure the database is running, so I just like to mention it.  If you don’t remember how to do this, go back to Step 1 and look at the instructions for starting the database.

Now lets run the application and see what results.  If everything ran correctly you will receive the following output.

attaching record
saving changes
getting 'first-blog-post'
==My First Cassandra Post by Nick Berardi==
Blah. Blah. Blah. about my first post on how great Cassandra is to work with.
tags:0:Cassandra,1:.NET,2:Database,3:NoSQL,

Step 6)

As a follow up exercise, see if you can add comments.  Hint you will need a new super column family as defined here:

<ColumnFamily Name="Comments"
    ColumnType="Super"
    CompareWith="TimeUUIDType"
    CompareSubcolumnsWith="UTF8Type" />

Hope this was an interesting exercise, and if you see any way to improve the interface or want to help out on the project please start by going to http://github.com/managedfusion/fluentcassandra.

Don’t forget to check out part 2 of this series.

Tags: , , ,
written by
Nick Berardi
subscribe
If you found this post valuable and would like to see more like it you can follow me.

23 Responses to “Your First Fluent Cassandra Application”

  1. Reply Juande says:

    Hi!
    I installed Cassandra 0.6.8 successfully on Windows using the default values except the paths, but when I tried to run the FluentCassandra.Sandbox example, the execution gets stuck on “db.SaveChanges();” (specifically into the TStreamTransport.cs -> method “read” -> “return inputStream.Read(buf, off, len);”)

    I previously added the keyspace “Blog” to the keyspaces tag, but this is all I get:

    attaching record
    saving changes

    When I close the app window (FluentCassandra.Sandbox), and Cassandra is running in DEBUG, I get the message “DEBUG 19:45:49,947 logout complete” from Cassandra.

    What could be going wrong? Any suggestions?

    Thanks!

  2. Reply Juande says:

    I got confused when looking for the Cassandra XML config file (as the tutorial says) I found a YAML config file in the Cassandra 0.7 RC3.

    The Fluent Cassandra branch 0.6 for Cassandra 0.6.X works great!
    https://github.com/managedfusion/fluentcassandra/tree/0.6

    Thank you very much for your time!
    Using your library is like magic, and I like that kind of things!

    • Reply Steve says:

      Would you post the yaml version of step 2 for this? I am trying to get it right but running into a Cassandra exception complaining that the Blog keyspace does not exist. I have to be close because Cassandra loads the yaml with my changes, but there’s something that is screwing up the works.

      Thanks-

  3. Reply James says:

    i’m currently working through this example and i ran into the same thing.

    What i did is import this post’s schema in just like Cassandra’s example in conf/schema-sample.txt

    They give an example right there of how to load the schema into Cassandra by passing that file into the client as an argument.

    My file looks like:

    /* James Sconfitto

    Trying to go through the Fluent .net interface for Cassandra example on the website.

    cassandra-cli command line interface as follows:

    bin/cassandra-cli -host localhost –file conf/Keyspace1.txt

    The cassandra-cli includes online help that explains the statements below. You can
    accessed the help without connecting to a running cassandra instance by starting the
    client and typing “help;”
    */

    create keyspace Blog
    with replication_factor = 1
    and placement_strategy = ‘org.apache.cassandra.locator.SimpleStrategy’;

    use Blog;

    create column family Posts
    with column_type = Super
    and comparator = UTF8Type
    and subcomparator = BytesType;

  4. Reply James says:

    Also, thanks for Fluent and this post. It’s great working through these. They’re well done :)

  5. Reply Tony says:

    Hi – using your API – if I have a SuperColumn with subcolumnscomparewith being of type LongType then the dynamic setting of columns therein breaks completely. Any suggestions on how to actually save a [CompareWith == UTFType] supercolumn and a bunch of [CompareWith == LongType] columns via the API?

    I tried not using the dynamic keyword and setting a FluentColumn on the resultant SuperColumn ref I got out ofthe CreateSuperColumn() method on a Record – but when I call SaveChanges on the CassandraContext there are no mutations registered on the record and thus nothing happens at all

    example below:

    public Persister(string cassandraHost, string keySpace, string busName)
    {
    _cassandraContext = new CassandraContext(keyspace: keySpace, host: cassandraHost);
    var todaysfamily1 = _cassandraContext.GetColumnFamily(“PRD”);
    _record = todaysfamily1.CreateRecord(DateTime.Today.ToString(“yyyyMMdd”));
    _superCol = _record.CreateSuperColumn(busName);
    }

    public bool Persist(byte[] payload, int seqnum)
    {
    _superCol.SeqNo = seqnum;
    _superCol.Payload = payload;
    _cassandraContext.Attach(_record);
    _cassandraContext.SaveChanges(_record);
    return true;
    }

  6. Reply Tony says:

    Sorted after reading the second sample…….. ;)

  7. Reply Tony says:

    I think there may be a problem with the TimeUUID generation code. Check out the below code:

    TimeUUIDType t1 = DateTime.Today;
    TimeUUIDType t2 = DateTime.Today;

    DateTime dt1 = t1;
    DateTime dt2 = t2;

    Assert.AreEqual(dt1, dt2);

    Console.WriteLine(dt1 + ” ” + dt2);

    If you run it you will see the dt1 does not equate to ‘Today’

    Any ideas?

    • Reply Nick Berardi says:

      Hi Tony,

      As mentioned in my email, TimeUUID has no concept of time zones, so they need to be stored in UTC time. So when they are casted back to a DateTIme they should be in UTC time. To get them back to the original you need to call:

      dt1.ToLocal()

      Thanks,
      Nick

  8. Reply kkdhf says:

    hi,
    i use cassandra-0.7.6-2 in windows xp.
    and use the last fluentcassandra to connect it.
    when i execute ‘save’ method.
    i got thrift error:
    ‘Cannot read, Remote side has closed’

    then i tried cassandra-cli.bat , it’s works fine..
    so , can u tell me which step i missed?

    • Reply Nick Berardi says:

      This would seem to indicate that something is blocking the connection or you are using a different version of Cassandra. Please make sure that you are using Cassandra 0.7 with FluentCassandra 0.7. There have been many changes in the Thrift protocol since I wrote this article.

  9. Reply Steve L says:

    I’m having trouble getting connected to a Cassandra (v0.8.2) server using the fluentcassandra (v0.8.2) library obtained via nuget. When trying any of the sample code I receive an

    “Cannot read, Remote side has closed” error.

    Any pointers?

  10. Reply Steve L says:

    I meant to say… in the log file, all I see are these messages (it looks like it’s not calling login correctly)

    DEBUG 15:19:11,117 logged out: null

    • Reply Nick Berardi says:

      That might be true, “login” is one of the places that hasn’t seen much love in FluentCassandra mostly because I don’t use it on a day-to-day basis. I am accepting contributions if you are interested in updating it.

  11. Reply Sam Beskur says:

    Hi Nick,
    I’ve been working through your latest code and examples on GitHub and I’m really liking what you have put together. Just curious as to why you provided a PostController but no Views? Am I missing something?

  12. Reply Gareth says:

    tag.Name and tag.Value are now
    tag.ColumnName and tag.ColumnValue respectively

  13. Reply JLindborg says:

    var keyspace = new CassandraKeyspace(new CassandraKeyspaceSchema
    {
    Name = KeyspaceName,
    Strategy = CassandraKeyspaceSchema.ReplicaPlacementStrategySimple,
    ReplicationFactor = 1
    }, db);

    Throw and error:

    FluentCassandra.Operations.CassandraOperationException: SimpleStrategy requires a replication_factor strategy option. —> Apache.Cassandra.InvalidRequestException: Exception of type ‘Apache.Cassandra.InvalidRequestException’ was thrown.

    I’m using the 1.1.0 release of Cassandra – presumably something changed and it’s not happy with the replication factor value there?

    • Reply Nick Berardi says:

      Please report an issue, and if you have time to dig into it, please fork the code and send a pull request back. It should be rather easy to hook up with the new schema objects I have implemented.

  14. Reply jim says:

    Hi Nick,
    Wasn’t sure whether this was an appropriate place to ask my question(s) –
    Being more of a .NET developer than a java … I started playing with FluentCassandra – it’s great so don’t get me wrong–
    I’ve also played with Hector and it’s great as well. HOWEVER — my use of hector is in the form of converted dll’s from java to .net via IKVM. btw – the reason I tried out hector was because of HOM and POJO — and I did see that you were starting an ADO impl too?

    Anyway — Because I would GREATLY value your opinion …
    I just wanted to ask you if you had any experience with IKVM.NET and whether you knew any reason(s) why NOT to use it – along with Hector – in production?
    And do you feel FluentCassandra is production worthy?
    Thanks.

  15. Reply Leonardo says:

    Hello

    Maybe I am asking nonsense, but when you add the columns in the supercolumn:

    postDetails.Title = “My First Cassandra Post”;
    postDetails.Body = “Blah. Blah. Blah. about my first post on how great Cassandra is to work with.”;
    postDetails.Author = “Nick Berardi”
    ;postDetails.PostedOn = DateTimeOffset.Now;

    Is it possible to have the column name as a variable?

    example:

    string mycolumnname = “Title”;

    so:

    something like:

    postDetails.AddColumn(mycolumname, “My First Cassandra Post”)

    Thank you.

    • Reply Nick Berardi says:

      You can do something like this.

      postDetails.TrySetColumn(mycolumnname, “My First Cassandra Post”);

      Or you can do

      postDetails[mycolumnname] = “My First Cassandra Post”;

      By the way the FluentCassandra group is a better place to ask this than my blog.

  16. Reply Leonardo says:

    Thank you very much, I will use your group from now on

Leave a Reply