Skip to main content
Session is the main object used in the driver. It manages all connections to the cluster and allows executing CQL requests.

Overview

The Session type provides methods for:
  • Executing unprepared queries
  • Preparing and executing prepared statements
  • Batch operations
  • Schema management
  • Cluster metadata access

Creating a Session

Use SessionBuilder to create a new session:
use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;

let session: Session = SessionBuilder::new()
    .known_node("127.0.0.1:9042")
    .build()
    .await?;
Alternatively, use Session::connect() with a SessionConfig:
use scylla::client::session::{Session, SessionConfig};

let mut config = SessionConfig::new();
config.add_known_node("127.0.0.1:9042");

let session: Session = Session::connect(config).await?;

Unprepared Queries

query_unpaged

query_unpaged
async fn
Executes an unprepared CQL statement without paging.
This method receives all results in a single response, which can cause huge memory footprint and latency for large result sets. For SELECT queries, prefer query_iter() or query_single_page().
Using this method with non-empty values is discouraged. It requires preparing the statement on a single connection (2 round trips instead of 1). Use execute_unpaged() with a prepared statement instead.
Parameters:
  • statement: impl Into<Statement> - The CQL statement to execute
  • values: impl SerializeRow - Bound values for the statement
Returns: Result<QueryResult, ExecutionError>Example:
// Insert data
session
    .query_unpaged(
        "INSERT INTO ks.tab (a, b) VALUES(?, ?)",
        (2_i32, "some text")
    )
    .await?;

// Read data (not recommended for large result sets)
let query_rows = session
    .query_unpaged("SELECT a, b FROM ks.tab", &[])
    .await?
    .into_rows_result()?;

for row in query_rows.rows::<(i32, &str)>()? {
    let (int_val, text_val) = row?;
    println!("{}, {}", int_val, text_val);
}

query_single_page

query_single_page
async fn
Queries a single page from the database, optionally continuing from a saved point.
Using this method with non-empty values is discouraged. Use execute_single_page() instead.
Parameters:
  • statement: impl Into<Statement> - The CQL statement
  • values: impl SerializeRow - Bound values
  • paging_state: PagingState - Previously received paging state or PagingState::start()
Returns: Result<(QueryResult, PagingStateResponse), ExecutionError>Example:
use std::ops::ControlFlow;
use scylla::response::PagingState;

// Manual paging in a loop
let mut paging_state = PagingState::start();
loop {
   let (res, paging_state_response) = session
       .query_single_page("SELECT a, b, c FROM ks.tbl", &[], paging_state)
       .await?;

   // Process a single page of results
   for row in res.into_rows_result()?.rows::<(i32, &str)>()? {
       let (a, b) = row?;
       println!("{}, {}", a, b);
   }

   match paging_state_response.into_paging_control_flow() {
       ControlFlow::Break(()) => break,
       ControlFlow::Continue(new_paging_state) => {
           paging_state = new_paging_state;
       }
   }
}

query_iter

query_iter
async fn
Executes an unprepared CQL statement with automatic paging, returning a stream over all results.
Using this method with non-empty values is discouraged. Use execute_iter() instead.
Parameters:
  • statement: impl Into<Statement> - The CQL statement
  • values: impl SerializeRow - Bound values
Returns: Result<QueryPager, PagerExecutionError>Example:
use futures::stream::StreamExt;

let mut rows_stream = session
   .query_iter("SELECT a, b FROM ks.t", &[])
   .await?
   .rows_stream::<(i32, i32)>()?;

while let Some(next_row_res) = rows_stream.next().await {
    let (a, b): (i32, i32) = next_row_res?;
    println!("{}, {}", a, b);
}

Prepared Statements

prepare

prepare
async fn
Prepares a statement on all nodes in the cluster.Prepared statements are much faster than unprepared statements:
  • Database doesn’t need to parse the statement upon each execution
  • They enable proper load balancing using token-aware routing
Prepare a statement once (e.g., store it in a variable), then execute it multiple times with different values. Do NOT call prepare() repeatedly before each execution - this defeats the purpose and degrades performance.
For token/shard aware load balancing to work properly, all partition key values must be sent as bound values.
Parameters:
  • statement: impl Into<Statement> - Statement to prepare
Returns: Result<PreparedStatement, PrepareError>Example:
use scylla::statement::prepared::PreparedStatement;

// Prepare the statement ONCE
let prepared: PreparedStatement = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;

// Execute it multiple times with different values
session.execute_unpaged(&prepared, (12345_i32,)).await?;
session.execute_unpaged(&prepared, (67890_i32,)).await?;

execute_unpaged

execute_unpaged
async fn
Executes a prepared statement without paging.
For SELECT queries, this receives all results in one response. Prefer execute_iter() or execute_single_page() for large result sets.
Parameters:
  • prepared: &PreparedStatement - The prepared statement
  • values: impl SerializeRow - Bound values
Returns: Result<QueryResult, ExecutionError>Example:
let prepared = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;

session.execute_unpaged(&prepared, (12345_i32,)).await?;

execute_single_page

execute_single_page
async fn
Executes a prepared statement, restricting results to a single page.Parameters:
  • prepared: &PreparedStatement - The prepared statement
  • values: impl SerializeRow - Bound values
  • paging_state: PagingState - Paging continuation or PagingState::start()
Returns: Result<(QueryResult, PagingStateResponse), ExecutionError>Example:
use std::ops::ControlFlow;
use scylla::statement::unprepared::Statement;
use scylla::response::PagingState;

let paged_prepared = session
    .prepare(
        Statement::new("SELECT a, b FROM ks.tbl")
            .with_page_size(100.try_into().unwrap()),
    )
    .await?;

let mut paging_state = PagingState::start();
loop {
    let (res, paging_state_response) = session
        .execute_single_page(&paged_prepared, &[], paging_state)
        .await?;

   // Process results
   for row in res.into_rows_result()?.rows::<(i32, &str)>()? {
       let (a, b) = row?;
   }

    match paging_state_response.into_paging_control_flow() {
        ControlFlow::Break(()) => break,
        ControlFlow::Continue(new_paging_state) => {
            paging_state = new_paging_state;
        }
    }
}

execute_iter

execute_iter
async fn
Executes a prepared statement with automatic paging, returning a stream over all results.Parameters:
  • prepared: impl Into<PreparedStatement> - The prepared statement
  • values: impl SerializeRow - Bound values
Returns: Result<QueryPager, PagerExecutionError>Example:
use futures::StreamExt;

let prepared = session
    .prepare("SELECT a, b FROM ks.t")
    .await?;

let mut rows_stream = session
   .execute_iter(prepared, &[])
   .await?
   .rows_stream::<(i32, i32)>()?;

while let Some(next_row_res) = rows_stream.next().await {
    let (a, b): (i32, i32) = next_row_res?;
    println!("{}, {}", a, b);
}

Batch Operations

batch

batch
async fn
Executes a batch statement containing multiple queries.
Avoid using non-empty values for unprepared statements in the batch, as this requires preparing them first, sending multiple requests instead of one.
Parameters:
  • batch: &Batch - The batch to execute
  • values: impl BatchValues - Values for each statement
Returns: Result<QueryResult, ExecutionError>Example:
use scylla::statement::batch::Batch;

let mut batch: Batch = Default::default();
batch.append_statement("INSERT INTO ks.tab(a, b) VALUES(?, ?)");
batch.append_statement("INSERT INTO ks.tab(a, b) VALUES(3, ?)");
batch.append_statement("INSERT INTO ks.tab(a, b) VALUES(5, 6)");

let batch_values = (
    (1_i32, 2_i32),  // Values for first statement
    (4_i32,),        // Values for second statement
    ()               // No values for third statement
);

session.batch(&batch, batch_values).await?;

prepare_batch

prepare_batch
async fn
Prepares all statements within a batch and returns a new batch where every statement is prepared.Parameters:
  • batch: &Batch - The batch with unprepared statements
Returns: Result<Batch, PrepareError>Example:
use scylla::statement::batch::Batch;

let mut batch: Batch = Default::default();
batch.append_statement("INSERT INTO ks.simple_unprepared1 VALUES(?, ?)");
batch.append_statement("INSERT INTO ks.simple_unprepared2 VALUES(?, ?)");

let prepared_batch: Batch = session.prepare_batch(&batch).await?;

let batch_values = ((1_i32, 2_i32), (3_i32, 4_i32));
session.batch(&prepared_batch, batch_values).await?;

Schema Management

use_keyspace

use_keyspace
async fn
Sends USE <keyspace_name> on all connections, allowing queries without fully-qualified table names.
Call only one use_keyspace at a time. Simultaneous calls with different names can result in inconsistent keyspace state across connections.
Parameters:
  • keyspace_name: impl Into<String> - Keyspace name (up to 48 alphanumeric characters and underscores)
  • case_sensitive: bool - If true, puts keyspace name in quotes
Returns: Result<(), UseKeyspaceError>Example:
session
    .query_unpaged("INSERT INTO my_keyspace.tab (a) VALUES ('test1')", &[])
    .await?;

session.use_keyspace("my_keyspace", false).await?;

// Now we can omit keyspace name
session
    .query_unpaged("INSERT INTO tab (a) VALUES ('test2')", &[])
    .await?;

get_keyspace

get_keyspace
fn
Gets the name of the currently set keyspace, or None if no keyspace was set.Returns: Option<Arc<String>>Example:
if let Some(ks) = session.get_keyspace() {
    println!("Current keyspace: {}", ks);
}

await_schema_agreement

await_schema_agreement
async fn
Waits until all nodes in the cluster agree on the same schema version.Returns: Result<Uuid, SchemaAgreementError> - The agreed upon schema versionExample:
session
    .query_unpaged("CREATE TABLE ks.tab (a int primary key)", &[])
    .await?;

let schema_version = session.await_schema_agreement().await?;
println!("Schema agreement reached: {}", schema_version);

check_schema_agreement

check_schema_agreement
async fn
Checks if all reachable nodes have the same schema version without waiting.Returns: Result<Option<Uuid>, SchemaAgreementError> - Some(version) if in agreement, None if notExample:
match session.check_schema_agreement().await? {
    Some(version) => println!("Schema agreement: {}", version),
    None => println!("Schema not in agreement"),
}

refresh_metadata

refresh_metadata
async fn
Manually triggers a metadata refresh, fetching current nodes in the cluster.Normally this is not needed - the driver automatically detects metadata changes.Returns: Result<(), MetadataError>Example:
session.refresh_metadata().await?;

Cluster Information

get_cluster_state

get_cluster_state
fn
Access cluster state visible by the driver, including network topology and schema information.Returns: Arc<ClusterState>Example:
let state = session.get_cluster_state();
println!("Number of nodes: {}", state.get_nodes_info().len());

get_metrics

get_metrics
fn
Access metrics collected by the driver (requires metrics feature).Returns: Arc<Metrics>Example:
#[cfg(feature = "metrics")]
{
    let metrics = session.get_metrics();
    println!("Total queries: {}", metrics.get_queries_num());
}

get_default_execution_profile_handle

get_default_execution_profile_handle
fn
Retrieves the handle to the execution profile used by default.Returns: &ExecutionProfileHandleExample:
let profile = session.get_default_execution_profile_handle();
let settings = profile.access();
println!("Default consistency: {:?}", settings.consistency);

Tracing

get_tracing_info

get_tracing_info
async fn
Retrieves tracing information for a traced query performed earlier.The driver will make multiple attempts to fetch tracing info, as it might not be available immediately.Parameters:
  • tracing_id: &Uuid - Tracing ID from a traced query result
Returns: Result<TracingInfo, TracingError>Example:
use scylla::statement::unprepared::Statement;

let mut stmt = Statement::new("SELECT * FROM ks.tab");
stmt.set_tracing(true);

let result = session.query_unpaged(stmt, &[]).await?;
if let Some(tracing_id) = result.tracing_id {
    let tracing_info = session.get_tracing_info(&tracing_id).await?;
    println!("Tracing duration: {:?}", tracing_info.duration);
}
  • SessionBuilder - Builder for creating sessions
  • CachingSession - Wrapper that automatically caches prepared statements
  • PreparedStatement - Prepared statement for efficient execution
  • QueryResult - Result from query execution
  • Batch - Container for batch operations

Build docs developers (and LLMs) love