Friday, April 27, 2018

Designing a Polyphonic Music Keyboard Algorithm.


-->
After several weeks of development, I’m discovering quite how hard programming a polyphonic keyboard for Eurorack is!  There are several well-known algorithms for dealing with voice stealing, that is, when your system is 4 note polyphonic and someone presses 5 notes,  these can be dealt with buy top note priority, bottom note priority or last note priority (http://electronicmusic.wikia.com/wiki/Note_priority).  But a polyphonic keyboard for Eurorack produces some unique problems.

First of all, lets describe the problem, our controller has 4 voltage outputs coupled with 4 gates.  A simple algorithm will assign the first note pressed to the first output, the second note to the second and so on.  This works pretty well, but has a major problem, voice changing.    Suppose we press two keys,  a low C (C0) and a  high A (A3),  C0 will go to output 1 and A3 to output 2. If you now release C0, the A3 will change to output 0.  The problem is the patch connected to output 0 might be very different to the patch on output 1.  When the A3 moves from output 1 to output 0 the sound produced will be very different.

To solve this, the algorithm needs to make the notes stick to a output.  In this situation note A3 once assigned to output 1 will stick to it that output until it is released, other new notes are assigned to free outputs, if no notes are free the new note is ignored!  This works pretty well but introduces a new problem, it is impossible to play the keyboard in legato mode!

Suppose you press C0 and then A3.  You want to move the A3 to G3 smoothly so that A3 moves to G3 on output 1.  What happens with the “Sticky” algorithm is that G3 is assigned to output 2 because for a short while both A3 and G3 are pressed together:

Ouput
Key
Keys
Keys
Keys
0
C0
C0
C0
C0
1
A3
A3
2
G3
G3
3

The problem here is that the algorithm needs to distinguish between a new note being pressed and held and the player playing in legato mode.  The difference is in legato mode a new key is pressed and then an older one released within a certain time, so simply delay the new note for a short period of time to see if this new note is part of a legato mode. This of course does mean that adding the new note will be delayed slowing down the playing mode.

This is the current state of play, I’m experimenting with the delay and seeing what effect it has on the playability of the keyboard. I can’t help feeling there is a better way, let me know if you know of one !

The source code is at:


Friday, July 18, 2014

Solving Eclipse and Maven problems:

"Failed to read artifact descriptor for"


I've spent most of today trying to add a dependency for the Cassandra Java driver to a maven pom in eclipse with little luck at all.  Worst when I tried it on another machine it worked fine so it was something wrong with my mac laptop.  Nothign I did would work, I kept on getting a error " Failed to read artifact descriptor for com.datastax.cassandra" etc. Looking at:

Stackoverflow: Maven: Failed to read artifact descriptor 

It suggested Maven -> Update Project and click on the force option.  No joy there !  It was only when I tried a manual mvn -U clean install command that I got the full error:
 "Failed to execute goal on project testmaven: Could not resolve dependencies for project uk.ac.dundee.computing.aec:testmaven:jar:0.0.1-SNAPSHOT: Failed to collect dependencies at com.datastax.cassandra:cassandra-driver-core:jar:2.0.3: Failed to read artifact descriptor for com.datastax.cassandra:cassandra-driver-core:jar:2.0.3: Could not transfer artifact com.datastax.cassandra:cassandra-driver-core:pom:2.0.3 from/to central (http://repo.maven.apache.org/maven2): Specified destination directory cannot be created: /Users/Administrator/.m2/repository/com/datastax/cassandra/cassandra-driver-core/2.0.3"
Looking at the permissions on /Users/Administrator/.m2/repository/com/datastax/cassandra/ did I see that the sub directories where owned by root. I must have used sudo at some point to manually build the cassandra java driver from a git repo (in fact I knew I did).

The answer then was to delete  /Administrator/.m2/repository/com/datastax and then run the force maven project update in eclipse.

All now works well !

Update to Java 1.8

I also ran into a problem changing the java version of a project from 1.5 to 1.7 or 1.8.  Yes you can change the project facet, but you'll find that a maven update will change it back to 1.5.  This stakoverflow has the correct answer:

Java. Warning - Build path specifies execution environment J2SE-1.4

Open the pom.xml file and add the following to the section
      <pluginManagement>  
           <plugins>  
                <plugin>  
                     <groupId>org.apache.maven.plugins</groupId>  
                     <artifactId>maven-compiler-plugin</artifactId>  
                     <configuration>  
                          <source>1.8</source>  
                          <target>1.8</target>  
                     </configuration>  
                </plugin>  
           </plugins>  
      </pluginManagement>  


You will need to do a maven update from eclipse after that.

Change Web app facet to 3.0

If you create a dynamic webapp from the file->new->maven project and select maven-archetype-webapp you may find that it is "stuck" at version 2.3.  If you try and change it to 2.4 or higher (3.1 for instance) you'll be prevented. Deep inside this thread is on stackoverflow is the correct answer (for me):

Cannot change version of project facet Dynamic Web Module to 3.0?


In eclipse, go to window->show vie -> navigator.  Now in the navigator window you should see the .settings folder.  Open the folder and open the file org.eclipse.wst.common.project.facet.core.xml  Inside that file you should see jst.web and you can change the webapp facet version there.  Again do a maven update from eclipse after that.

Wednesday, March 19, 2014

Saving an image in Cassandra BLOB field

We had an occasion today to be able to store images in a blob field of a Cassandra tables.  More to the point I needed to extract it and send it from a java servlet to a web browser as an image.   The code for storing the image is quit easy but there is a small gotcha when retrieving it.   So, suppose we have a table that looks something like:

String CreateTweetTable = "CREATE TABLE if not exists Messages ("+
                "user varchar,"+
                " interaction_time timeuuid,"+
                " tweet varchar,"+
                " image blob," +
                " imagelength int,"+
                " PRIMARY KEY (user,interaction_time)"+
                ") WITH CLUSTERING ORDER BY (interaction_time DESC);";

Our image will be in the blob field and we also store the size of the image for reference. We can load a picture from a file on the local machines hard disk like this:

FileInputStream fis=new FileInputStream("/Users/Administrator/Desktop/mystery.png");
byte[] b= new byte[fis.available()+1];
int length=b.length;
fis.read(b);

We now need to convert the byte array into a bytebuffer:

ByteBuffer buffer =ByteBuffer.wrap(b);

Writing the record becomes simply:

 PreparedStatement ps = session.prepare("insert into Messages ( image, user, interaction_time,imagelength) values(?,?,?,?)");
BoundStatement boundStatement = new BoundStatement(ps);
session.execute(  boundStatement.bind( buffer, "Andy",  convertor.getTimeUUID(),length));


Getting the image back is simple.  Use a Select to get the result set:

PreparedStatement ps = session.prepare("select user,image,imagelength from Messages where user =?");
BoundStatement boundStatement = new BoundStatement(ps);
ResultSet rs =session.execute ( boundStatement.bind("Andy"));

We can now loop through the result set (here we are assuming only one image comes back)

ByteBuffer bImage=null;
for (Row row : rs) {
 bImage = row.getBytes("image") ;
 length=row.getInt("imagelength");
}

However to display the image we will need it as a byte array.  We can’t use bImage.get() as this reaches down in to the raw buffer (see: https://groups.google.com/a/lists.datastax.com/forum/#!searchin/java-driver-user/blob$20ByteBuffer/java-driver-user/4_KegVX0teo/2OOZ8YOwtBcJ for details )  Instead we can use :

byte image[]= new byte[length];
image=Bytes.getArray(bImage);

In the servlet we can return this image in one of 2 ways:

OutputStream out = response.getOutputStream();
response.setContentType("image/png");
response.setContentLength(image.length);
out.write(Image);

Writes the image as a single lump which may use too much memory.  You might be better using a bufferedinput stream (http://stackoverflow.com/questions/2979758/writing-image-to-servlet-response-with-best-performance

InputStream is = new ByteArrayInputStream(Image);
BufferedInputStream input = new BufferedInputStream(is);
byte[] buffer = new byte[8192];
for (int length = 0; (length = input.read(buffer)) > 0;) {
    out.write(buffer, 0, length);
}
out.close();

Wednesday, January 22, 2014

Running Cassandra 2.x.x on Windows 7 and 8

This blog post describes how to get the Cassandra 2.x.x family running on a windows machine.  It's clear that Cassandra should not be run for production on Windows (except perhaps on Azure), but if you're a student learning to use C* it may well be you have no choice to run it on Windows 7 or 8 on your laptop.  Lets get started : 

Install JRE 7

Open a command prompt and type java -version to see if it is installed properly.  If not find a jre from oracle and install it.  Make sure it's a version 7 at the least (version 6 will not work).

You'll need to set JAVA_HOME. Find the control panel (on windows 8 search for it).  Go to "system and security" and then "system". Click on "Advanced Settings" and then the "Environment Variables" button. Click on the new button an in the Variable name box type JAVA_HOME
under the value you'll need to put in the path to the java you are using.  Mine is

c:\program files\java\jre7

but yours may be different, especially if you have a jdk.  If you are going to program java clients for C* you will need a JDK but that's a different post

 

Install Cassandra

Download Cassandra from http://cassandra.apache.org/ probably a file like
apache-cassandra-2.0.4-bin.tar.gz
You'll need to unpack this file and that will depend on which flavor of windows you have.  At this point I'll assume you have a legal copy of winzip or similar.  Unpack the downloaded file to the root of the c: or d: drive on your machine

You can now change to the Cassandra install directory in your command prompt, change to the bin directory to start Cassandra, type Cassandra to start it.  The window will print a lot of information but you are looking for a line like:

 INFO 19:00:31,031 Listening for thrift clients...

to make sure it's working.

CQLSH

So now we have C* running, we need to check we can connect to it.  Start by opening another command prompt and type cqlsh. Sadly it won't start, cqlsh now needs an installation of python, so lets get one installed. Download one from  http://www.python.org/  and go to downloads then "individual release". click on the 2.x stable release and then
scroll down to the download section.  Your looking for the Windows MSI installer.  I used:

http://www.python.org/ftp/python/2.7.6/python-2.7.6.msi

Download it and run it to install Python, you'll need a version 2 of python, NOTE THIS WELL, version 3 will not work! This installs a nice windows version of python but does not install
a path to the executable.  You'll need to set it by hand I guess.  Once again Find the control panel (on windows 8 search for it).  Go to "system and security" and then "system"
Click on "Advanced Settings" and then the "Environment Variables" button.  Under the system variables find PATH.  Highlight it and click edit.

Careful!  We don't want to wipe the current contents (if you do hit cancel) go to the end of the current path and enter

;c:\python27

Note the ; at the beginning.  Again this will depend on the current version of python you've installed and should mirror the path to your python installation. Click OK to close the
dialog boxes and open a command prompt again.

Now you should be able to change to the cassandra directory and then the bin directory and type cqlsh.  With luck you should get the
cassandra cqlsh prompt:

Connected to Test Cluster at localhost:9160.
[cqlsh 4.1.0 | Cassandra 2.0.4 | CQL spec 3.1.1 | Thrift protocol 19.39.0]
Use HELP for help.
cqlsh>

type "use system;" followed by "describe  keyspaces;", cqlsh should reply:

system  system_traces

You're now connected and ready to start work.

BTW, folks at datastax and apache Cassandra, why is this so hard ?  Would Datastax Devcenter work easier ?

Setting up a Cassandra cluster on Windows with a vagrant virtualbox

Setting up Cassandra on windows can now be a pain with all it's dependancies, but it's something I'll cover in a later post.  One simpler way is to get C* running in a virtual box and perhaps even run it as a mini cluster.  This can be helped a lot by using Vagrant, but even that isn't quite straight forward.

The following has worked for me and is based heavily on the work done by calebgroom  and his github contribution vagrant-cassandra.  I've altered it a bit for use with the latest C* which adds virtual nodes etc.  Using these instructions you should be able to provisiona 3 node C* cluster with vnodes.

1: Install oracle Vm Virtualbox from https://www.virtualbox.org/ the latest version should do.
2: Install git for windows http://msysgit.github.io/ ensure you select the option to run git from the command line.
3: Install ruby for windows http://rubyinstaller.org/ V2.x.x Select all options
4: Download devkit DevKit-mingw64-32-4.7.2-20130224-1151-sfx.exe
4.1: Extract it to a permeant location
4.2 Start "commandline" with ruby from
4.3 Change to devkit location and run
    ruby dk.rb init
    ruby dk.rb install

5 At any location run gem install librarian-chef (This may take sometime)
6 Download vagrant (http://www.vagrantup.com/ ) and install it
7: Open a command prompt and git clone https://github.com/acobley/vagrant-cassandra.git
8:  change to the directory vagrant-cassandra\vagrant and run
   librarian-chef install
9: Open vagrant/cookbooks/java/attributes and edit default.rb so that
    default['java']['jdk_version'] = '7'
   
10: If you want comment, out the “DL is deprecated, please use Fiddle” warning at C:\HashiCorp\Vagrant\embedded\lib\ruby\2.0.0\dl.rb
11: change to the vagrant-cassandra and run
    Vagrant up
This could take some time, but once it's finished you should be able to ssh to the virtual machine if you have a ssh installed.

   vagrant ssh node1

If you don't have ssh installed the the git installation comes with a ssh client so add c:\program files\git\bin to your path

 set PATH=%PATH%;c:\program files\git\bin

 Or set the path environment variable from the control panel.

 You can then ssh to the virtual host
  
  ssh vagrant@127.0.0.1 -p 2222 -i c:/users/*username*/.vagrant.d/insecure_private_key
 
Once inside the virtual machines you can test and see if  it works by getting the c* status by typing

/usr/local/cassandra/bin/nodetool -h 192.168.2.10 status





You can bring down the cluster with vagrant halt  and remove it with vagrant destroy (but then you'll need to start again!)

Vagrant can also be run on a mac.  Make sure you have vitualbox installed, clone the https://github.com/acobley/vagrant-cassandra.git and follow the instructions in the readme.

Saturday, November 2, 2013

Hadoop 2.x : jar file location for wordcount example

The Jar files  for Hadoop 2.x have moved location from Hadoop 1.x.  I found the following command

javac -classpath $HADOOP_HOME/share/hadoop/common/hadoop-common-2.2.0.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.2.0.jar:$HADOOP_HOME/share/hadoop/common/lib/commons-cli-1.2.jar -d wordcount_classes myWordCount.java

will allow you to compile the standard wordcount example code.  You can see that the common files are in /share/hadoop/common/ and the mapreduce files are in /share/hadoop/mapreduce/.  Finally the common lib file are in /share/hadoop/common/lib

This post is in answer to this stackoverflow question:

http://stackoverflow.com/questions/19488894/compile-hadoop-2-2-0-job

(or set your classpath lke this

 export CLASSPATH=$HADOOP_HOME/share/hadoop/common/hadoop-common-2.2.0.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.2.0.jar:$HADOOP_HOME/share/hadoop/common/lib/commons-cli-1.2.jar

and compile like this:

javac -classpath $CLASSPATH -d myWordCountClasses myWordCount.java

)

Wednesday, October 30, 2013

Hadoop 2 on Ubuntu on Azure.

This is to be read in conjunction with http://ac31004.blogspot.co.uk/2013/10/installing-hadoop-2-on-mac_29.html

Fire up a Azure Ubuntu server and ssh to it

Install a Java JDK:
apt-get install default-jdk

On you home machine, download a copy of Hadoop and secure copy it to the Azure machine (your username and machine will be different)
scp hadoop-2.2.0.tar.gz user@Hadoopmachine.cloudapp.net:

Unzip it and untar it
gunzip hadoop-2.2.0.tar.gz
tar xvf  hadoop-2.2.0.tar

You'll still need to set up the env variables
export JAVA_HOME=/usr/lib/jvm/default-java
export HADOOP_INSTALL=/home/user/hadoop-2.2.0
export PATH=$PATH:$HADOOP_INSTALL/bin:$HADOOP_INSTALL/sbin


Also add JAVA_HOME, add Hadoop_INSTALL and change path in /etc/environment, see http://trentrichardson.com/2010/02/10/how-to-set-java_home-in-ubuntu/ for details

After setting up core-site.xml and hdfs-site.xml  you'll make the datanode and name nodename directories

mkdir -p /home/hadoop/yarn/namenode
mkdir /home/hadoop/yarn/datanode

Everything else should be the same.

Tuesday, October 29, 2013

Installing Hadoop 2 on a Mac

I've had a lot of trouble getting Hadoop 2 and yarn 2 running on my MAC.  There are some tutorials out there but they are often for
beta and alpha versions of the hadoop 2.0 family.  These are the steps I used to get Hadoop 2.2.0 working on my MAC running OSX 10.9

Note:  watch for version differences in this blog.  It was written for Hadoop 2.2.0, we are currently on 2.6.2 so that will need to be changed throughout.

Get hadoop from http://www.apache.org/dyn/closer.cgi/hadoop/common/

make sure JAVA_HOME is set (if you have Java 6 on your machine):
export JAVA_HOME=`/usr/libexec/java_home -v1.6`
(Note your Java version should be 1.7 or 1.8)

point HADOOP_INSTALL to the hadoop installation directory
export HADOOP_INSTALL=/Applications/hadoop-2.2.0

And set the path
export PATH=$PATH:$HADOOP_INSTALL/bin:$HADOOP_INSTALL/sbin

You can test hadoop is found with
hadoop -version

make sure ssh is set up on your machine:
system preferences -> sharing -> remote login is ticked

try:
ssh @localhost

where is the name you used to logon.

in $HADOOP_INSTALL/etc these are the conf files I changed.

core-site.xml

 <configuration>  
 <property>  
   <name>fs.default.name</name>  
   <value>hdfs://localhost:9000</value>  
  </property>  
 </configuration>  


hdfs-site.xml

 <configuration>  
 <property>  
   <name>dfs.replication</name>  
   <value>1</value>  
  </property>  
  <property>  
   <name>dfs.namenode.name.dir</name>  
   <value>file:/Users/Administrator/hadoop/namenode</value>  
  </property>  
  <property>  
   <name>dfs.datanode.data.dir</name>  
   <value>file:/Users/Administrator/hadoop/datanode</value>  
  </property>  
 </configuration>  


Make the directories for the namenode and datanode data (note the file above and the mkdir below will need to reflect where you  want to store the files, I've stored mine in the home directory of the Administrator user on my Mac).

mkdir -p /Users/Administrator/hadoop/namenode
mkdir -p /Users/Administrator/hadoop/datanode

hadoop namenode -format

yarn-site.xml
 <configuration>  
 <!-- Site specific YARN configuration properties -->  
 <property>  
 <name>yarn.resourcemanager.address</name>  
 <value>localhost:8032</value>  
 </property>  
 <property>  
 <name>yarn.nodemanager-aux-services</name>  
 <value>madpreduce.shuffle</value>  
 </property>  
 </configuration>  


start-dfs.sh
start-yarn.sh
jps

should give
9430 ResourceManager
9325 SecondaryNameNode
9513 NodeManager
9225 DataNode
9916 Jps
9140 NameNode

if not check log files.  If data node is not started and  you get incompatible id's error, stop everything delete datanode directory and recreate
datanode directory

try  a ls
hadoop fs -ls

if you get

ls: `.': No such file or directory

then there is no home directory in the hadoop file system.  So

hadoop fs -mkdir /user
hadoop fs -mkdir /user/<username>
where is the name you are logged onto the machine with.

now change to $HADOOP_INSTALL directory and upload a file

hadoop fs -put LICENSE.txt


finally try a mapreduce job:

cd share/hadoop/mapreduce
hadoop jar ./hadoop-mapreduce-examples-2.2.0.jar wordcount LICENSE.txt out

Friday, October 11, 2013

Mapping CQL's sets and maps to column families

In this post we are going to explore how CQL implements sets and maps in Cassandra’s column store.

(in a bizarre twist of fate, John Berryman. created this post http://www.planetcassandra.org/blog/post/understanding-how-cql3-maps-to-cassandras-internal-data--structure yesterday on the same subject, I swear I hadn't seen it when I started working on this post, yesterday as well !  It's just how it goes sometimes,  Johns post is great it has to be said !. )

In CQL version 3 wide tables have been supported through the use of sets, maps and lists.  These features have been supported since Cassandra 1.2 (http://www.datastax.com/dev/blog/cql3_collections) and should now be the de facto way of creating “wide tables”  the canonical example of sets is the use of multiple email addresses for a user .  In the relational world you might create a email address table with a foreign key pointing to the user id for each address.   This is going to cause a join just for any request that needs details of the user and their valid addresses. 

Suppose we create a simple keyspace in the usual fashion:

create keyspace Keyspace3 WITH replication = {'class':'SimpleStrategy', 'replication_factor':1};

In a  Cassandra (from 1.2) you would create a table like this:

CREATE TABLE Users (   
    id uuid Primary Key,
    name text,    
    email_addresses set) ;

(This is similar to  Sylvain Lebresne’s example here http://www.datastax.com/dev/blog/cql3_collections)

We can insert data into the table (a user with 2 email addresses like this):

insert into users(id,name,email_addresses) values (88b8fd18-b1ed-4e96-bf79-4280797cba81,'tim',{'tim@example.org','timothy@example.org'});

This user has a UUID, a name and two email addresses.   You can of course get the email addresses with a select command:

select email_addresses from Users;

which will return the addresses as a set:

email_addresses
-------------------------------------------------------
            {'tim@example.org', 'timothy@example.org'}

However, how is this implemented in the column store ?  If you had used a thrift based interface (such as Hector) you may have created the column family and had the following structure:

Id: 88b8fd18-b1ed-4e96-bf79-4280797cba81 (Key)
    name: tim
  email_address: 'tim@example.org'
  email_address: 'timothy@example.org'

but how is it implemented in CQL3 ?  If you fire up Cassandra-cli you can use the list command to see what is stored in the column family:

LifeintheAirAge:bin Administrator$ ./cassandra-cli
Connected to: "Test Cluster" on 127.0.0.1/9160
Welcome to Cassandra CLI version 2.0.0

Please consider using the more convenient cqlsh instead of CLI
CQL3 is fully backwards compatible with Thrift data; see http://www.datastax.com/dev/blog/thrift-to-cql3

Type 'help;' or '?' for help.
Type 'quit;' or 'exit;' to quit.

[default@unknown] use keyspace3;
Authenticated to keyspace: keyspace3
[default@keyspace3] list users;
Using default limit of 100
Using default cell limit of 100
-------------------
RowKey: 88b8fd18-b1ed-4e96-bf79-4280797cba81
=> (name=, value=, timestamp=1381497810072000)
=> (name=email_addresses:74696d406578616d706c652e6f7267, value=, timestamp=1381497810072000)
=> (name=email_addresses:74696d6f746879406578616d706c652e6f7267, value=, timestamp=1381497810072000)
=> (name=name, value=74696d, timestamp=1381497810072000)

So we can see the rowkey as expected and the name of the user as a name value pair (the value is in ASCII in hex in this case 746976d is tim).

But for the email_addresses the values are not set.  The values of the email addresses is encoded into the name along with the “column” schema name.  name=email_addresses:74696d406578616d706c652e6f7267  is the column name email_addresses followed by tim@example.org in ASCII hex) .  Why do this ? Why not have the name as email_addresses and the value as the the hex email address ?  One reason perhaps is because this allows us to implement maps ina similar way with out needing special cases.   Suppose we alter table to include a map, we want to store details about our user, but we don’t yet know which details the user will provide (A contrived example I’ll grant you).  You can alter the table as follows:

alter table users add details map;

and insert some details as follows:

update users set details= {'tel' : '555 232341', 'twitter' : '@andycobley'} where id =88b8fd18-b1ed-4e96-bf79-4280797cba81;

What does our column now look like? Using the list command we get :

RowKey: 88b8fd18-b1ed-4e96-bf79-4280797cba81
=> (name=, value=, timestamp=1381498805511000)
=> (name=details:74656c, value=3031333832333435303738, timestamp=1381498805511000)
=> (name=details:74776974746572, value=40616e6479636f626c6579, timestamp=1381498805511000)

You can see the map key is stored with the column name  in the name part of the column family name value  pair. So  name=details:74656c contains ‘tel’ as a ASCII hex value.  The map value is simply stored in the value part of the column family name value pair.

So, we’ve seen how CQL3’s  maps and sets map on to the column family name value pairs by storing the CQL table’s column name in the name part of the column family name value pair.  It’s quite simple and elegant really.

(as ever I’m more than happy to receive corrections or further explanations !)

Monday, September 2, 2013

Setting default java on a mac

If you have multiple versions of Java on your Mac, you can set the current working version from the command line as follows:

export JAVA_HOME=`/usr/libexec/java_home -v1.6`

This will set the version to 1.6.  You can of course see the current version with

java -version

You can see all java versions installed on your mac with:

/usr/libexec/java_home -V

(tested with macos 10.8.4 )

Friday, March 22, 2013

Minor bug with C*1.2.2 and jdk8 on a pi


This is just a quick note on getting Cassandra to run on a Raspberry pi with jdk8.  It looks like a little bug has crept into the starup script in 1.2.2  that will stop C* from running on the pi with jdk1.8   To fix this locate  conf/cassandra-env.sh and comment out the following lines

#if [ "$JVM_VERSION" \> "1.7" ] ; then                                                                      
#    JVM_OPTS="$JVM_OPTS -XX:+UseCondCardMark"                                                              
#fi

It looks like jdk8 on the pi doesn't support the UseCondCardMark option, but it can be safely removed from the startup script. I'm told that the option was added "for  better lock handling especially on hotspot with multicore processor" (see https://issues.apache.org/jira/browse/CASSANDRA-4366 )  I’ll report this as a bug and hopefully it can fixed soon in a more elegant manor.

Friday, August 31, 2012

Calculating Pi to 5 Digits

Yesterday I was reading 20 controversial programming opinions which has some interesting debates going on in it.   However I was drawn to the following programming question that one responder likes to ask potential new hires in job interviews:

Given that Pi can be estimated using the function 4 * (1 – 1/3 + 1/5 – 1/7 + …) with more terms giving greater accuracy, write a function that calculates Pi to an accuracy of 5 decimal places.

The first part of this is easy, depending on the language you use.  In Java it's something simple like:


float Pi=(float)1.0;
int mult=-1;
for (int dem=3;dem <1000 dem="dem+2){<br">    float Add=(float)(1.0/(float)dem);
    Pi=Pi+(float)(mult*Add);
    System.out.println("Dem "+dem+"  "+Add+ "  Pi "+4.0*Pi);
    mult=-1*mult;
}



The problem is getting the answer to 5 digits accuracy. How can we know it's accurate unless we know the value of Pi. My solution (which does have it's problems is to iterate until the calculated value of Pi doesn't change to the accuracy we require. In the examples below I've used string formatting to track the old value and the new value until they are the same. Both versions give the same answer 3.14159

In Java

import java.text.*;
public class Pi {
   public static void main(String[] args) {
      double Pi=(double)1.0;
      //http://www.javaprogrammingforums.com/java-programming-tutorials/297-how-format-double-value-2-decimal-places.html
      DecimalFormat df = new DecimalFormat("#.#####");
      String oldPi;
      String newPi;
      long dem=3;
      oldPi =df.format((double)4.0);
      newPi =df.format(Pi);
      int mult=-1;
      while (oldPi.compareTo(newPi)!=0){
        oldPi=df.format((double)4.0*Pi);
        double Add=(double)(1.0/(double)dem);
        Pi=Pi+(double)(mult*Add);
        newPi=df.format((double)4.0*Pi);
        System.out.println("Dem "+dem+"  "+Add+ "  Pi "+df.format((double)4.0*Pi)+ "  Pi "+4.0*Pi+" : "+oldPi+" : "+newPi);
      mult=-1*mult;
     dem+=2;
     }
 }
}

And in C
#include
#include
main(){
   double Pi=(double)1.0;
   char oldPi[100];
   char newPi[100];
   long dem=3;
   sprintf(oldPi,"%.5f",(double)4.0);
   sprintf(newPi,"%.5f",(double)Pi);
   int mult=-1;
   while (strcmp(oldPi,newPi)!=0){
      sprintf(oldPi,"%.5f",(double)4.0*Pi);
      double Add=(double)(1.0/(double)dem);
      Pi=Pi+(double)(mult*Add);
      sprintf(newPi,"%.5f",(double)4.0*Pi);
      printf(" %ld Pi %.5f %s %s \n",dem,4.0*Pi,oldPi,newPi);
      mult=-1*mult;
      dem+=2;
   }
}
The problem with both these versions is they don't work if you try and increase the accuracy.  If you want 6 decimal places then the value doesn't settle down, in fact it oscillates between two values and never stays the same.

So two questions:


  1. What does the code look like in other languages (particularly something like Erlang)
  2. How to deal with the oscillation problem ?

Tuesday, July 24, 2012

Cassandra on a Raspberry Pi, 5 and 6 node insert stress tests:

Here's a quick update for the performance graphs for cassandra on Raspberry pi.  Here's the results for 5 and 6 node inserts on a stress test



I'm getting to the point in this project where I can start to build pseudo data centers and start to test performance there.

Thursday, July 19, 2012

Java performance on Raspbian vs Debian

Over the past few weeks I’ve been blogging about my experience of running Apache Cassandra on the Raspberry Pi.  I plan to use the Pi as an educational resource in the University I work in, hopefully giving students the chance to play with large clusters and experiment with configurations, database models and practices in a nosql environment.  Of course performance isn’t great but  for me, it’s a  cheap way of getting lots of nodes and do real network configuration problems. 

A couple of days ago a new Debian based distro for the pi was released called Raspbian “wheezy” was released and is now the official Raspberry Pi Debian distro (I believe).   This is the first OS release for the Pi to take advantage of the Pi’s floating point hardware, which is going to make the OS a lot faster for general use.  I downloaded it for testing in my rig, sadly this is a tale of woe.

Apache Cassandra is a Java application and needs a JRE in order to run.    I’ve always used a  Oracle supplied JVM “Oracle’s java SE for  embedded “

http://www.oracle.com/technetwork/java/embedded/downloads/javase/index.html


Sadly, it seems this can’t be used on Raspbian.  Trying to run it gives :

Java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory

It seems that this version of Java uses the “soft float ABI (armel) which is incompatible with Raspbian”: (thanks to mpthompson on the Raspberry Pi forum for the information) so it’s looking like it can’t run.  Back to openjdk ?

But wait !  Why did I not use openjdk in the first place ?

That’s simple, performance.  In my experience (and perhaps this is a configuration problem I’m not aware of)  Open JDK is a lot slower than the Oracle version.  And  I mean a lot slower!  I set up a single node Cassandra server image, one with the old Debian image and Oracle Java the other with Raspbian and Open JDk.  I then ran stress tests from a Apple Air (something I’ve done many times !) .  Here’s the results.  The second column is interval_op_rate, you want this to be as high as possible, the third column  is  avg_latency, you want this to be as low as possible.

Raspbian and OpenJDK


>Lifeintheairage:bin Administrator$ ./stress -d 192.168.1.12 -o insert -I DeflateCompressor
Unable to create stress keyspace: Keyspace names must be case-insensitively unique ("Keyspace1" conflicts with "Keyspace1")
total,interval_op_rate,interval_key_rate,avg_latency,elapsed_time
485,48,48,0.8705896907216495,10
1042,55,55,0.9123070017953321,20
1436,39,39,1.2947030456852793,30
2010,57,57,0.9009128919860627,40
2510,50,50,0.961294,51
2743,23,23,1.922206008583691,61
3306,56,56,1.0665861456483126,71
3863,55,55,0.9055601436265709,81
4118,25,25,2.0272901960784315,91
4659,54,54,0.9333364140480591,102
5031,37,37,0.916733870967742,112
5498,46,46,1.480710920770878,122

 

Debian Squeze and Java SE for embedded


>lifeintheairage:bin Administrator$ ./stress -d 192.168.1.10 -o insert -I DeflateCompressor
Unable to create stress keyspace: Keyspace names must be case-insensitively unique ("Keyspace1" conflicts with "Keyspace1")
total,interval_op_rate,interval_key_rate,avg_latency,elapsed_time
2565,256,256,0.18891695906432748,10
4604,203,203,0.2503182932810201,20
7093,248,248,0.20536078746484532,30
9289,219,219,0.23249635701275045,40
11516,222,222,0.22830354737314773,51
14107,259,259,0.19691161713624084,61
16297,219,219,0.22646849315068493,71
18092,179,179,0.29083064066852365,81
19756,166,166,0.30374939903846154,91
21689,193,193,0.2648339368856699,102
23404,171,171,0.18766355685131195,112
25395,199,199,0.3459779005524862,122
27646,225,225,0.2330964015992892,132
29684,203,203,0.24136898920510305,142


And a graph of interval_op_rate:



(red is Java SE for embedded, blue is OpenJDK)

Java SE for embedded really is a lot faster for Apache Cassandra (and I wouldn’t be surprised for other java apps such as Arduino IDE).  For now I need to stick with the Debian release, I hope it doesn’t become unsupported.  Hopefully someone can  get in touch with Oracle and encourage them to support a official port of Java SE for embedded onto the raspberry which supports the correct Raspbian libraries.

Saturday, June 16, 2012

3 Node / 4 Node Cassandra Stress test on a Raspberry Pi cluster




One of the things I’m interested in is using  tiny Raspberry Pi computers for teaching database and network admin to Undergraduate and MSc students.  In the first instance I’ve been looking at building a large cluster of these devices for to run a cluster of apache Cassandra database servers.  I’m in no way expecting these to get any where near the performance of  real servers or even VM installations but, for me at least, they give a feeling of working with real hardware.   The first thing I’m doing is conducting stress tests  with various configurations,  but I’m limited by availability of the  devices.  I started out with a cluster of 3 and have just managed to add another node.    The stress test is using the stress command Cassandra provides in the tools directory of a standard installation (some distributions missed the directory so  you may need to get the source and build the stress tool yourself).   After we’ve looked at the chart, I’ll look a little at the process of adding a new node to a Cassandra cluster.  For the record the commands I used to stress the cluster are as follows:

 Insert:
./stress -d 192.168.1.10,192.168.1.11,192.168.1.12 -o insert -I DeflateCompressor

Read:

./stress -d 192.168.1.10,192.168.1.11,192.168.1.12 -o read

For a 4 node test I added the new node into the list of hosts.  Note also I’m using DeflateCompressor as I’ve not yet managed to get snappy compressor compiled for the Pi.  I used a Mac book air to drive the stress test over a wifi connection to the cluster which is connected via a Netgear 10Meg switch which should handle the data rates form a Pi

Here then  is a graph combining inserts and reads for 3 and 4 node clusters:




One thing I do want to note here, for both the 3 and 4 node clusters the insert performance drops suddenly towards the end of the run.  I’m not sure why that happens.  The clusters where in both case balanced with each node running 90% CPU.  Here’s the ring information for the cluster arrangements (optained from the nodetool command ./nodetool -h 192.168.1.10 ring)

Address         DC          Rack        Status State   Load            Effective-Owership  Token                                       
                                                                                           113427455640312821154458202477256070485     
192.168.1.11    datacenter1 rack1       Up     Normal  14.67 MB        33.33%              0                                           
192.168.1.10    datacenter1 rack1       Up     Normal  14.42 MB        33.33%              56713727820156410577229101238628035242      
192.168.1.12    datacenter1 rack1       Up     Normal  14.51 MB        33.33%              113427455640312821154458202477256070485     


pi@raspberrypi:/home/space/apache-cassandra-1.1.0/bin$ ./nodetool -h 192.168.1.12 ring
Address         DC          Rack        Status State   Load            Effective-Owership  Token                                       
                                                                                           127605887595351923798765477786913079296     
192.168.1.11    datacenter1 rack1       Up     Normal  11.24 MB        25.00%              0                                           
192.168.1.10    datacenter1 rack1       Up     Normal  11.24 MB        25.00%              42535295865117307932921825928971026432      
192.168.1.12    datacenter1 rack1       Up     Normal  11.38 MB        25.00%              85070591730234615865843651857942052864      
192.168.1.13    datacenter1 rack1       Up     Normal  11.1 MB         25.00%              127605887595351923798765477786913079296     

Moving from 3 to 4 nodes.

Here’s the procedure I used to move from 3 to 4 nodes. Providing your  cluster is already balanced with the initial_token correctly set in the Cassandra.yaml file you can add the new node with it’s correct key.  Once it’s bootstrapped on each of the other nodes you can use nodetool move to change that nodes token, something like:

sudo ./nodetool -h 192.168.1.10 move 42535295865117307932921825928971026432

Does this on each node that needs to be moved, so not the first node with a token of 0 and the new node you've just added with the correct initial token.  After the node is moved you will need to run cleanup to delete any data that the node doesn’t need:

./nodetool -h 192.168.1.10 cleanup

There’s a simple python code you can use to calculate the keys (this version courtesy of a good friend  on twitter)

import sys
if (len(sys.argv) > 1):
   num = int(sys.argv[1])
else:
   num = int(raw_input("How many nodes? :"))
for i in range(0,num):
   print 'node %d: %d' % (i, (i*(2**127)/num))

I’m looking forward to going beyond 4 nodes soon !

Getting more memory on the Pi

The Pi is a little short on memory for this type of server.  The situation isn’t helped by some of the memory being shared by the GPU, the default being 64M.  You can move this down to 32 M by changing the start.elf file.

Change to /boot  on the pi
Copy start.elf to start.elf.old  (sudo cp start.elf start.elf.old)
Copy arm224_start.elf to start.elf (sudo cp arm224_start.elf to start.elf)

Reboot.  You can use the top command to see the performance of your Pi and how much memory it has.   See http://elinux.org/RPi_Advanced_Setup for more information on the elf files available and how much memory the GPU uses for each.

A Pic of the setup
Just for completeness, here's a pic of 4 Raspberry Pi running apache cassandra