Query timestamps

Timestamps determine the order of precedence for operations on the same column value from different queries.

Timestamps determine the order of precedence for operations on the same column value from different queries. In DataStax Enterprise and Cassandra, each mutation—update, insert, delete—is assigned a microsecond-precision timestamp to order operations relative to each other. The order of precedence for operations on the same column value is:

  1. Data with the latest timestamp.
  2. If the operations have the same timestamp, deletes have priority over inserts and updates.
  3. Otherwise, the lexically larger value of data has priority. For example, 2 is chosen over 1.

Timestamps can be assigned by the driver client or the server-side node coordinating the request. All recent versions of the DataStax drivers use client-generated timestamps by default for DataStax Enterprise versions 4.7 and later. Older versions of DSE (and versions of Apache Cassandra older than 2.1) do not support client timestamps, as they were introduced in the CQL native protocol version 3.

Client-side timestamp generation is the default to keep order of operations predictable from the perspective of a single client. Through monotonically increasing client-side timestamps, the driver ensures that all operations are written in the sequential order that they were executed within the scope of that instance.

Without client timestamps, the client is at the whim of timestamps assigned by coordinating nodes. Coordinating nodes assign timestamps based on their internal system clock. It is difficult to keep the different nodes system clock synchronized in a distributed system. Each node is subject to clock drifts ranging from tens of milliseconds to seconds, even when the nodes use NTP or other clock synchronization software.

For example, consider the following scenario where server timestamps are used.

  1. A client executes the following query:
    DELETE FROM tbl_a WHERE key = 0
    The query is sent to Node A, which creates a delete mutation with timestamp 10.
  2. The client then executes:
    UPDATE tbl_a SET x = ‘hello’ where key = 0
    The query is sent to Node B, which creates an update mutation with timestamp 9.
  3. The client executes:
    SELECT x from tbl_a where key = 0
    and receives a result set with 0 rows.

It should be surprising that no rows were returned from the SELECT query in step 3. Even though the DELETE operation in step 1 was executed before the UPDATE operation in step 2, it takes precedence because the largest timestamp (10) was assigned to it. This scenario is avoided completely by using client timestamps.

Configuring timestamp generation in the drivers

Client timestamp generation can be configured or disabled in each of the drivers. See the individual driver documentation for more information on each driver:

Table 1. Client timestamp generation for drivers
C/C++ C# Java Node.js PHP Python Ruby

When to use server timestamps

One possible downside to using client timestamps is that the number of client application servers often outnumber DataStax Enterprise nodes in production environments. It’s not unusual for different applications using the same DSE cluster to be managed by different teams. In these cases, it may be operationally challenging to keep the clocks synchronized between many different client application servers.

Out-of-sync client application server clocks is an issue only when there are clients making updates to the same partition values as other clients within a window that would be smaller than the expected clock drift between client nodes. Even in this case, it may not be important that updates made in this window be properly ordered in the sequence in which they were executed. It is possible that these updates were made by different parties who are not aware of one another. If it is important, consider using lightweight transactions.

Lightweight transactions and client timestamps

When executing lightweight transactions (LWTs), any client timestamp assigned to those operations is discarded. This is because DSE maintains a separate timestamp generator that ensures the timestamp assigned is monotonically increased across all LWTs.

One common mistake users make is mixing the use of LWTs and other mutation operations on a single table. This is not recommended, especially since the timestamp mechanism used for normal operations is different than the one used by LWTs, even when using server timestamps.

Keeping clocks in sync across servers

No matter the timestamp strategy, DataStax strongly recommends using a service like NTP to keep the system clocks synchronized across all machines in the data ecosystem. DataStax also recommends organizations measure and understand the degree of clock drift among all the servers in their production environment to understand the time windows that may exist between nodes. Use utilities and commands, such as clockdiff, ntpdate -q, and ntp -q, to measure clock differences between servers.