The Online Works of Chris Anderton

Node.js Cassandra Driver: Ports, Pools and Constant Reconnects

While working with the Node.js Cassandra Driver, I was confused to see every query was slow - each query took a couple of seconds to complete.

I initially thought the issue would be the hosted Cassandra service I was using - but testing over cqlsh worked just fine.

To debug I increased the verbosity of logging to include all but the most chatty log messages (the code fragment below shows one way to customise logging, where client is the Cassandra Client):

javascript client.on('log', function(level, className, message, furtherInfo) { if (level != 'verbose') { console.log('cassandra: %s -- %s', level, message); } });

The logs showed a new connection attempt on every query and for it seemed that a Connection Pool was not being maintained.

cassandra: info -- Connecting to xxx.xxx.xxx.xxx
cassandra: warning -- There was an error when trying to connect to the host xxx.xxx.xxx.xxx
cassandra: warning -- Setting host xxx.xxx.xxx.xxx:9042 as DOWN
cassandra: info -- Connecting to xxx.xxx.xxx.xxx:9042
cassandra: warning -- There was an error when trying to connect to the host xxx.xxx.xxx.xxx
cassandra: warning -- Setting host xxx.xxx.xxx.xxx:9042 as DOWN

It turned out it wasn't an issue with the driver - the answer was right there in the log:

  1. My Cassandra cluster runs on a non-standard port;
  2. I provided Contact Points in host:port format - this enabled a successful control connection;
  3. Discovery of the other hosts failed due to the non-standard port:
    • the Cassandra cluster specified the rpc_address but not the rpc_port;
    • the driver was attempting to connect to the rpc_address on the standard port - and failing.

The solution was simple and should have been obvious all along - specify the port in the Client Options when initialising the Client.

Once I had correctly specified the Client Options, the Connection Pool was created and maintained and queries performed as expected.

Specifying Client Options

The client constructor uses client options - a sample is below:

options = { 
    contactPoints : contactPoints,
    authProvider: authProvider,
    sslOptions: {
        ca: [process.env.CASSANDRA_TRUSTED_CERT]
    },
    protocolOptions: {
        port: 30041
    }
};

Specify Port in ClientOptions

The port is specified in the protocolOptions property of the Client Options - as shown in the example.

Contact Points

To connect to Cassandra you provide one or more Contact Points - these are used to establish the control connection - it is best practice to specify more than one:

 Only one contact point is required (the driver will retrieve the address of the other nodes automatically), but it is usually a good idea to provide more than one contact point, because if that single contact point is unavailable, the driver will not be able to initialize correctly.     

It is possible to specify host:port strings for each contact point - the driver will correctly parse and connect to the contact points - but you probably want to specify the port using the port option instead.

The Cassandra service i'm using provides contact points in a connection string format delimited by commas - I split the contact points as follows:

var contactPoints = process.env.CASSANDRA_URL.split(',').map(function(uri){
    var parsedUri = url.parse(uri);
    
    return (parsedUri.host);
});

Authentication

Authentication is possible using a standard or custom authentication provider.

Using the PlainTextAuthProvider is as simple as passing in the username and password to the constructor:

var authProvider = new cassandra.auth.PlainTextAuthProvider(username, password);

SSL

The sslOptions property of Client Options uses the same format as tls.connect.

My Cassandra service uses a self-signed certificate - the Client needs the CA certificate.

In my environment, the CA certificate is available as an environment variable and can be passed in as shown in the above example.