TechnologyJanuary 17, 2017

Promise Support in the DataStax Node.js Drivers

Jorge Bay Gondra
Jorge Bay Gondra
Promise Support in the DataStax Node.js Drivers

'm excited to announce that we added support for promises in the latest version of the DataStax drivers, while continuing to support callback-based execution.

Alternate promise-based API

The use of promises has been spreading fast in the Node.js ecosystem in recent years. There are several reasons to use promises as opposed to a callback-based execution most notably:

  • Straightforward error handling
  • Simplified chaining of multiple async methods
  • Control-flow methods are included in the Promise API

A promise can be chained using the built-in then() method. In modern versions of JavaScript, promises can awaited upon with the yield keyword in conjunction with generator functions, aka coroutines, or using the await keyword in async functions.

Note that we will continue to support callback-based execution and this new alternate API was added without introducing breaking changes to the existing functionality. Asynchronous methods in the driver expect a callback as the last parameter, according to the callback-style convention. If no callback is provided, the methods will return a Promise.

Let's look at how to use the Node.js drivers with this new API:

const query = 'SELECT name, email FROM users WHERE key = ?';
client.execute(query, [ 'someone' ], { prepare: true })
  .then(result => console.log('User with email %s', result.rows[0].email));

Using async functions:

const result = await client.execute(query);
console.log('User with email %s', result.rows[0].email);

Besides CQL query executions, the drivers also support promises for metadata fetching methods, for example:

const tableMetadata = await client.metadata.getTable('ks1', 'table1');

Promises are created in the drivers using the Promise constructor by default. Alternatively, you can use your Promise library of choice by overriding promiseFactory when creating a Client instance. For example, using bluebird:

const Promise = require('bluebird');
const client = new Client({
  contactPoints: [ 'host1', 'host2' ],
  promiseFactory: Promise.fromCallback
});

Key things to remember:

  • We will continue to support callback based execution.
  • Asynchronous methods in the driver accept a callback as the last parameter, according to the callback-style convention.
  • When a callback is not provided, then the asynchronous methods will return a Promise.
  • The new alternative API for promises does not break any existing functionality.

Other new features

Along with Promise support, there are other noteworthy features in the version 1.2 of the DataStax Enterprise Node.js Driver and version 3.2 of the DataStax Node.js Driver for Apache Cassandra.

Timestamp Generator

When using Apache Cassandra 2.1+ or DataStax Enterprise 4.7+, it's possible to send the operation timestamp in the request, as opposed to embedded in the query. The drivers now use MonotonicTimestampGenerator by default to generate the request timestamps.

You can provide a different generator when creating the Client instance:

const client = new Client({
  contactPoints: [ 'host1', 'host2' ],
  policies: {
    timestampGeneration: new MyCustomTimestampGenerator()
  }
});

As defined by ECMAScript, the Date object has millisecond resolution. The MononoticTimestampGenerator uses an incremental counter to generate the sub-millisecond part of the timestamp until the next clock tick. The implementation also guarantees that the returned timestamps will always be monotonically increasing, even if multiple updates happen under the same millisecond. To guarantee such monotonicity, if more than one thousand timestamps are generated within the same millisecond, or in the event of a system clock skew, the implementation might return timestamps that drift out into the future. When this happens, the built-in generator logs a periodic warning message.

Query Idempotence Option

We added a query option that can be used to define whether it's safe for the driver to apply the query multiple times without changing the result beyond the initial application.

Initially, this value can be retrieved at Retry policy level to determine if the execution can be retried in case of a request error or write timeout. In future versions, the drivers will avoid the retry logic and directly rethrow the error in case of non-idempotent queries.

ResultSet sync iterator,

We added a bit of syntactic sugar on top of the ResultSet prototype to allow synchronous iteration of the rows using the new for...of statement in ES2015 by exposing a Symbol.iterator method.

const result = await client.execute(query);
for (let row of result) {
  console.log(row.email);
}

Wrapping up

The new versions of the DataStax drivers are available on npm: dse-driver and cassandra-driver.

We would love to hear your comments, feedback, and questions:

Share

One-stop Data API for Production GenAI

Astra DB gives JavaScript developers a complete data API and out-of-the-box integrations that make it easier to build production RAG apps with high relevancy and low latency.