Heroku and Cassandra
Join the DZone community and get the full member experience.
Join For Freeintroduction
last time i wrote about
hadoop on heroku
which is on add-on from treasure data - this time i am going to cover nosql on heroku.
there are various datastore services – add-ons in heroku terms – available from mongodb (mongohq) to couchdb (cloudant) to cassandra (cassandra.io). this post is devoted to cassandra.io.
cassandra.io
cassandra.io is a hosted and managed cassandra ring based on apache cassandra and makes it accessible via restful api. as of writing this article, the cassandra.io client helper libraries are available in java, ruby and php, and there is also a objective-c version in private beta. the libraries can be downloaded from github. i use the java library in my tests.
heroku – and cassandra.io add-on, too – is built on amazon elastic compute cloud (ec2) and it is supported in all amazon’s locations. note: cassandra.io add-on is in public beta now that means you have only one option called test available - this is free.
installing cassandra.io add-on
to install cassandra.io add-on you just need to follow the standard way of adding an add-on to an application:
$ heroku login enter your heroku credentials. email: address@example.com password (typing will be hidden): authentication successful. $ heroku create creating glacial-badlands-1234... done, stack is cedar http://glacial-badlands-1234.herokuapp.com/ | git@heroku.com:glacial-badlands-1234.git $ heroku addons:add cassandraio:test --app glacial-badlands-1234 adding cassandraio:test on glacial-badlands-1234... done, v2 (free) use `heroku addons:docs cassandraio:test` to view documentation.
you can check the configuration via heroku admin console:
then you need to clone the client helper libraries from github:
$ git clone https://github.com/m2mio/cassandraio-client-libraries.git
in case of java client library, you need google gson library (gson-2.0.jar), too.
writing cassandra.io application
the java restful api library has one simple configuration file called sdk.properties. it has very few parameters stored in it – the api url and the version. the original sdk.properties file that is cloned from github has the version wrong (v0.1), it needs to be changed to 1. you can verify the required configuration parameters using heroku config command.
$ heroku config --app glacial-badlands-1234 === glacial-badlands-1234 config vars cassandraio_url: https://token:accountid@api.cassandra.io/1/welcome
you can check the same config parameters from heroku admin console, thought the url is misleading:
the sdk.properties file should look like this:
apiurl = https://api.cassandra.io version = 1
the java code – cassandraiotest.java – is like this:
package io.cassandra.tests; import java.util.arraylist; import java.util.list; import io.cassandra.sdk.statusmessagemodel; import io.cassandra.sdk.column.columnapi; import io.cassandra.sdk.columnfamily.columnfamilyapi; import io.cassandra.sdk.constants.apiconstants; import io.cassandra.sdk.data.dataapi; import io.cassandra.sdk.data.databulkmodel; import io.cassandra.sdk.data.datacolumn; import io.cassandra.sdk.data.datamapmodel; import io.cassandra.sdk.data.datarowkey; import io.cassandra.sdk.keyspace.keyspaceapi; public class cassandraiotest { // credentials private static string token = "<token>"; private static string accountid = "<accountid>"; // data private static string ks = "aapl"; private static string cf = "marketdata"; private static string col1 = "open"; private static string col2 = "close"; private static string col3 = "high"; private static string col4 = "low"; private static string col5 = "volume"; private static string col6 = "adjclose"; private static string rk = "18-05-2012"; public static void main(string[] args) { try { statusmessagemodel sm; // create keyspace keyspaceapi keyspaceapi = new keyspaceapi(apiconstants.api_url, token, accountid); sm = keyspaceapi.createkeyspace(ks); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); // create columnfamily columnfamilyapi columnfamilyapi = new columnfamilyapi(apiconstants.api_url, token, accountid); sm = columnfamilyapi.createcolumnfamily(ks, cf, apiconstants.comparator_utf8); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); // add columns (high, low, open, close, volume, adjclose) columnapi columnapi = new columnapi(apiconstants.api_url, token, accountid); sm = columnapi.upsertcolumn(ks, cf, col1, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); sm = columnapi.upsertcolumn(ks, cf, col2, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); sm = columnapi.upsertcolumn(ks, cf, col3, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); sm = columnapi.upsertcolumn(ks, cf, col4, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); sm = columnapi.upsertcolumn(ks, cf, col5, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); sm = columnapi.upsertcolumn(ks, cf, col6, apiconstants.comparator_utf8, true); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); //add bulk data dataapi dataapi = new dataapi(apiconstants.api_url, token, accountid); list columns = new arraylist(); datacolumn dc = new datacolumn(col1, "533.96"); columns.add(dc); dc = new datacolumn(col2, "530.38", 12000); columns.add(dc); dc = new datacolumn(col3, "543.41", 12000); columns.add(dc); dc = new datacolumn(col4, "522.18", 12000); columns.add(dc); dc = new datacolumn(col5, "26125200", 12000); columns.add(dc); dc = new datacolumn(col6, "530.12", 12000); columns.add(dc); list rows = new arraylist(); datarowkey row = new datarowkey(rk, columns); rows.add(row); databulkmodel databulk = new databulkmodel(rows); sm = dataapi.postbulkdata(ks, cf, databulk); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); // get data datamapmodel dm = dataapi.getdata(ks, cf, rk, 0, null); system.out.println(dm.tostring()); // delete keyspace sm = keyspaceapi.deletekeyspace(ks); system.out.println(sm.getmessage() + " | " + sm.getdetail() + " | " + sm.geterror()); } catch(exception e) { system.out.println(e.getmessage()); } } }
the runtime result is:
09-sep-2012 22:59:18 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/keyspace/aapl/ success | keyspace added successfully. | null 09-sep-2012 22:59:21 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/columnfamily/aapl/marketdata/utf8type/ success | marketdata columnfamily created successfully | null 09-sep-2012 22:59:24 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/open/utf8type/?isindex=true failed | unable to create column: open | cassandra encountered an internal error processing this request: tapplicationerror type: 6 message:internal error processing system_update_column_family 09-sep-2012 22:59:24 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/close/utf8type/?isindex=true success | close column upserted successfully | null 09-sep-2012 22:59:26 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/high/utf8type/?isindex=true success | high column upserted successfully | null 09-sep-2012 22:59:27 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/low/utf8type/?isindex=true success | low column upserted successfully | null09-sep-2012 22:59:29 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/volume/utf8type/?isindex=true success | volume column upserted successfully | null 09-sep-2012 22:59:30 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/column/aapl/marketdata/adjclose/utf8type/?isindex=true success | adjclose column upserted successfully | null posting json: {"rowkeys":[{"rowkey":"18-05-2012","columns":[{"columnname":"open","columnvalue":"533.96","ttl":0},{"columnname":"close","columnvalue":"530.38","ttl":12000},{"columnname":"high","columnvalue":"543.41","ttl":12000},{"columnname":"low","columnvalue":"522.18","ttl":12000},{"columnname":"volume","columnvalue":"26125200","ttl":12000},{"columnname":"adjclose","columnvalue":"530.12","ttl":12000}]}]} 09-sep-2012 22:59:32 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/data/aapl/marketdata/ success | bulk upload successfull. | null 09-sep-2012 22:59:32 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/data/aapl/marketdata/18-05-2012/ {volume=26125200, open=533.96, low=522.18, high=543.41, close=530.38, adjclose=530.12} 09-sep-2012 22:59:32 io.cassandra.sdk.cassandraiosdk constructapiurl info: api url: https://api.cassandra.io/1/keyspace/aapl/ success | keyspace dropped successfully. | null
analysis
step 1./
the code creates a keyspace named aapl using http post, url: https://api.cassandra.io/1/keyspace/aapl/
it uses keyspaceapi class with token and accountid as parameters for the constructor. token is used as username, while accountid is the password. (remember: these attributes can be retrieved using
heroku config
command or via heroku admin console)
step 2./ then the code creates a column family called marketdata.it uses columnfamilyapi – with the credentials mentioned above – and the rest url is https://api.cassandra.io/1/columnfamily/aapl/marketdata/utf8type/.
step 3./ then the code upserts the coumns called open, close, high, low, volume and ajdclose. it uses columnapi – same credentials as we already know – and the rest url is https://api.cassandra.io/1/column/aapl/marketdata/open/utf8type/?isindex=true where aapl is the keyspace, marketdata is the column family and open is the column.
step 4./ then the code prepares the data as name/value pairs (open = “533.96″, close = “530.38″, etc), defines a rowkey (“18-05-2012″) and the uses dataapi postbulkdata method to upload the data into cassandra.io. dataapi credentials are the same as above.
step 5./ the code then fetches the data using http get with url: https://api.cassandra.io/1/data/aapl/marketdata/18-05-2012/. the response is in json format: {volume=26125200, open=533.96, low=522.18, high=543.41, close=530.38, adjclose=530.12}
step 6./ finally the code destroys the keyspace using http delete, url: https://api.cassandra.io/1/keyspace/aapl/.
summary
if you want to try out a robust, highly available casssandra datastore without any upfront infrastructure investment and with an easy to use api, you can certainly have a closer look at cassandra.io on heroku. it takes only a few minutes to start up and the apis offer a simply rest based data management for java, ruby and php developers.
Published at DZone with permission of Istvan Szegedi, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Security Challenges for Microservice Applications in Multi-Cloud Environments
-
Auto-Scaling Kinesis Data Streams Applications on Kubernetes
-
Batch Request Processing With API Gateway
-
Top 10 Pillars of Zero Trust Networks
Comments