Skip to main content
CQL provides two UUID types: uuid for general-purpose universally unique identifiers, and timeuuid for time-based UUIDs (version 1).

UUID

The uuid type stores a 128-bit universally unique identifier of any version. The driver uses the uuid crate for UUID support.

Basic Usage

use uuid::Uuid;
use scylla::Session;

// Generate a random UUID (v4)
let id = Uuid::new_v4();

session.query_unpaged(
    "INSERT INTO table (id, name) VALUES (?, ?)",
    (id, "Alice"),
).await?;

// Read back
let result = session
    .query_unpaged("SELECT id FROM table WHERE id = ?", (id,))
    .await?;

if let Some(row) = result.rows()?.first() {
    let uuid: Uuid = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_uuid())
        .unwrap();
    println!("UUID: {}", uuid);
}

Creating UUIDs

The uuid crate provides several ways to create UUIDs:
use uuid::Uuid;

// Random UUID (version 4)
let v4 = Uuid::new_v4();

// From a byte array
let bytes = [
    0xa1, 0xa2, 0xa3, 0xa4, 0xb1, 0xb2, 0xc1, 0xc2,
    0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
];
let from_bytes = Uuid::from_bytes(bytes);

// From a string
let from_string = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8").unwrap();

// Nil UUID (all zeros)
let nil = Uuid::nil();

UUID as Primary Key

UUIDs are commonly used as primary keys in ScyllaDB/Cassandra:
use uuid::Uuid;

// Create table with UUID primary key
session.query_unpaged(
    "CREATE TABLE IF NOT EXISTS users (
        id uuid PRIMARY KEY,
        name text,
        email text
    )",
    &[],
).await?;

// Insert with generated UUID
let user_id = Uuid::new_v4();
session.query_unpaged(
    "INSERT INTO users (id, name, email) VALUES (?, ?, ?)",
    (user_id, "Alice", "[email protected]"),
).await?;

UUID Conversion

use uuid::Uuid;

let uuid = Uuid::new_v4();

// To string
let uuid_str = uuid.to_string();
println!("UUID string: {}", uuid_str); // "550e8400-e29b-41d4-a716-446655440000"

// To bytes
let uuid_bytes = uuid.as_bytes();
println!("UUID bytes: {:?}", uuid_bytes);

// To u128
let uuid_u128 = uuid.as_u128();
println!("UUID as u128: {}", uuid_u128);

// From u128
let from_u128 = Uuid::from_u128(uuid_u128);

Timeuuid

The timeuuid type stores a version 1 UUID, which includes a timestamp component. This makes it useful for time-ordered data.

Basic Usage

use scylla::value::CqlTimeuuid;
use uuid::Uuid;

// Create a timeuuid from a v1 UUID
// Note: The uuid crate's v1 generation requires the `v1` feature
let timestamp = uuid::Timestamp::now(uuid::NoContext);
let node_id = [1, 2, 3, 4, 5, 6]; // MAC address or random bytes
let v1_uuid = Uuid::new_v1(timestamp, &node_id);
let timeuuid = CqlTimeuuid::from(v1_uuid);

session.query_unpaged(
    "INSERT INTO events (id, event_time, data) VALUES (?, ?, ?)",
    (timeuuid, "event data"),
).await?;

Special Ordering

CqlTimeuuid has a special ordering that differs from standard UUID ordering. It orders by timestamp first (the time component of the UUID), then by the other components:
use scylla::value::CqlTimeuuid;
use uuid::Uuid;

// CqlTimeuuid implements Ord with timestamp-first ordering
let timeuuid1 = CqlTimeuuid::from(Uuid::new_v1(/* earlier timestamp */, &node_id));
let timeuuid2 = CqlTimeuuid::from(Uuid::new_v1(/* later timestamp */, &node_id));

assert!(timeuuid1 < timeuuid2); // Orders by time
This ordering matches ScyllaDB/Cassandra’s timeuuid comparison semantics.

Converting Between UUID and Timeuuid

use scylla::value::CqlTimeuuid;
use uuid::Uuid;

// From Uuid to CqlTimeuuid
let uuid = Uuid::new_v4();
let timeuuid = CqlTimeuuid::from(uuid);

// From CqlTimeuuid to Uuid
let timeuuid = CqlTimeuuid::from(Uuid::new_v1(timestamp, &node_id));
let uuid: Uuid = timeuuid.into();

// Or as a reference
let uuid_ref: &Uuid = timeuuid.as_ref();

Timeuuid as Clustering Key

Timeuuid is commonly used as a clustering key for time-series data:
// Create table with timeuuid clustering key
session.query_unpaged(
    "CREATE TABLE IF NOT EXISTS sensor_data (
        sensor_id int,
        event_time timeuuid,
        temperature double,
        PRIMARY KEY (sensor_id, event_time)
    ) WITH CLUSTERING ORDER BY (event_time DESC)",
    &[],
).await?;

// Insert events
let sensor_id = 1;
for _ in 0..10 {
    let timestamp = uuid::Timestamp::now(uuid::NoContext);
    let event_time = CqlTimeuuid::from(Uuid::new_v1(timestamp, &node_id));
    let temperature = 20.0 + rand::random::<f64>() * 10.0;
    
    session.query_unpaged(
        "INSERT INTO sensor_data (sensor_id, event_time, temperature) VALUES (?, ?, ?)",
        (sensor_id, event_time, temperature),
    ).await?;
    
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}

Timeuuid Functions

CQL provides several functions for working with timeuuid:
// Get current timeuuid
let result = session
    .query_unpaged("SELECT now() FROM system.local", &[])
    .await?;

// Get minimum/maximum timeuuid for a timestamp
let result = session
    .query_unpaged(
        "SELECT event_time FROM events WHERE event_time > minTimeuuid('2024-01-01 00:00:00+0000')",
        &[],
    )
    .await?;

Extracting Timestamp from Timeuuid

use scylla::value::CqlTimeuuid;
use uuid::Uuid;

let timestamp = uuid::Timestamp::now(uuid::NoContext);
let timeuuid = CqlTimeuuid::from(Uuid::new_v1(timestamp, &node_id));

// Access the underlying UUID
let uuid: &Uuid = timeuuid.as_ref();

// Get fields (includes timestamp)
let (time_low, time_mid, time_hi_and_version, clock_seq_and_node) = uuid.as_fields();

// For proper timestamp extraction, use CQL functions
let result = session
    .query_unpaged(
        "SELECT dateOf(event_time), unixTimestampOf(event_time) FROM events WHERE id = ?",
        (timeuuid,),
    )
    .await?;

Nullable UUIDs

Both UUID types can be nullable:
use uuid::Uuid;
use scylla::value::CqlTimeuuid;

// Nullable UUID
let optional_id: Option<Uuid> = Some(Uuid::new_v4());
session.query_unpaged(
    "INSERT INTO table (id, optional_id) VALUES (?, ?)",
    (1, optional_id),
).await?;

// Nullable Timeuuid
let optional_time: Option<CqlTimeuuid> = None;
session.query_unpaged(
    "INSERT INTO events (id, event_time) VALUES (?, ?)",
    (1, optional_time),
).await?;

Best Practices

Using UUID for Primary Keys

// Good: Random UUIDs distribute data evenly across nodes
let user_id = Uuid::new_v4();

// Avoid: Sequential IDs can cause hotspots
// Don't use auto-incrementing integers as partition keys

Using Timeuuid for Time-Series Data

// Good: Timeuuid as clustering key for time-ordered data
session.query_unpaged(
    "CREATE TABLE events (
        partition_key int,
        event_time timeuuid,
        data text,
        PRIMARY KEY (partition_key, event_time)
    )",
    &[],
).await?;

Generating Timeuuids

use uuid::{Uuid, Timestamp, NoContext};
use scylla::value::CqlTimeuuid;

// Generate a timeuuid for the current time
let timestamp = Timestamp::now(NoContext);
let node_id = [1, 2, 3, 4, 5, 6]; // Use a consistent node ID or random bytes
let v1_uuid = Uuid::new_v1(timestamp, &node_id);
let timeuuid = CqlTimeuuid::from(v1_uuid);

Common Patterns

Pagination with Timeuuid

use scylla::value::CqlTimeuuid;

// Query with time range
let start_time = CqlTimeuuid::from(/* earlier timeuuid */);
let end_time = CqlTimeuuid::from(/* later timeuuid */);

let result = session
    .query_unpaged(
        "SELECT * FROM events WHERE sensor_id = ? AND event_time >= ? AND event_time < ? LIMIT 100",
        (sensor_id, start_time, end_time),
    )
    .await?;

UUID String Formatting

use uuid::Uuid;

let uuid = Uuid::new_v4();

// Standard format (lowercase with hyphens)
let standard = uuid.to_string();
println!("{}", standard); // "550e8400-e29b-41d4-a716-446655440000"

// Uppercase
let uppercase = format!("{:X}", uuid);
println!("{}", uppercase); // "550E8400-E29B-41D4-A716-446655440000"

// Without hyphens
let simple = uuid.simple().to_string();
println!("{}", simple); // "550e8400e29b41d4a716446655440000"

See Also

Build docs developers (and LLMs) love