Skip to main content
CachingSession provides a convenient wrapper over Session that automatically caches prepared statements and reuses them when possible.

Overview

The CachingSession type:
  • Automatically prepares statements on first use
  • Caches prepared statements for reuse
  • Provides the same API as Session for queries and execution
  • Manages cache size with LRU eviction
When the cache reaches its maximum capacity, the oldest prepared statement is removed to make room for new ones.

Creating a CachingSession

Using CachingSessionBuilder

use scylla::client::session_builder::SessionBuilder;
use scylla::client::caching_session::{CachingSession, CachingSessionBuilder};

let session = SessionBuilder::new()
    .known_node("127.0.0.1:9042")
    .build()
    .await?;

let caching_session: CachingSession = CachingSessionBuilder::new(session)
    .max_capacity(2137)
    .build();

Using from()

use scylla::client::caching_session::CachingSession;

let caching_session = CachingSession::from(session, 128);

Using with_hasher()

use scylla::client::caching_session::CachingSession;
use std::collections::hash_map::RandomState;

let hasher = RandomState::new();
let caching_session = CachingSession::with_hasher(session, 128, hasher);

CachingSessionBuilder

new

new
fn
Creates a new CachingSessionBuilder from a Session.Parameters:
  • session: Session - The session to wrap
Returns: CachingSessionBuilderExample:
let builder = CachingSessionBuilder::new(session);

new_shared

new_shared
fn
Creates a new CachingSessionBuilder from an Arc<Session>.Parameters:
  • session: Arc<Session> - The shared session to wrap
Returns: CachingSessionBuilderExample:
use std::sync::Arc;

let session_arc = Arc::new(session);
let builder = CachingSessionBuilder::new_shared(session_arc);

max_capacity

max_capacity
fn
Configures the maximum capacity of the prepared statements cache.The default capacity is 128. If you expect to run many different prepared statements, consider increasing this value.Parameters:
  • max_capacity: usize - Maximum number of cached prepared statements
Returns: SelfExample:
let caching_session = CachingSessionBuilder::new(session)
    .max_capacity(2137)
    .build();

use_cached_result_metadata

use_cached_result_metadata
fn
Configures whether to use cached metadata for result deserialization.When enabled, the driver requests the server not to attach result metadata in responses. The driver caches metadata received during preparation and uses it for deserialization.This option is false by default.Parameters:
  • use_cached_metadata: bool - Whether to use cached metadata
Returns: SelfExample:
let caching_session = CachingSessionBuilder::new(session)
    .use_cached_result_metadata(true)
    .build();

hasher

hasher
fn
Provides a custom hasher for the prepared statement cache.Parameters:
  • hasher: S2 - Custom hasher implementing BuildHasher
Returns: CachingSessionBuilder<S2>Example:
#[derive(Default, Clone)]
struct CustomBuildHasher;

impl std::hash::BuildHasher for CustomBuildHasher {
    type Hasher = CustomHasher;
    fn build_hasher(&self) -> Self::Hasher {
        CustomHasher::new()
    }
}

let caching_session = CachingSessionBuilder::new(session)
    .hasher(CustomBuildHasher::default())
    .build();

build

build
fn
Finishes configuration and creates a CachingSession.Returns: CachingSession<S>Example:
let caching_session = CachingSessionBuilder::new(session)
    .max_capacity(256)
    .build();

Query Execution Methods

execute_unpaged

execute_unpaged
async fn
Does the same as Session::execute_unpaged but uses the prepared statement cache.On first execution with a given query string, the statement is prepared and cached. Subsequent executions with the same query reuse the cached prepared statement.Parameters:
  • query: impl Into<Statement> - The query to execute
  • values: impl SerializeRow - Bound values
Returns: Result<QueryResult, ExecutionError>Example:
// First execution prepares and caches the statement
caching_session
    .execute_unpaged("INSERT INTO ks.tab (a, b) VALUES (?, ?)", (1, 2))
    .await?;

// Second execution reuses the cached statement
caching_session
    .execute_unpaged("INSERT INTO ks.tab (a, b) VALUES (?, ?)", (3, 4))
    .await?;

execute_iter

execute_iter
async fn
Does the same as Session::execute_iter but uses the prepared statement cache.Parameters:
  • query: impl Into<Statement> - The query to execute
  • values: impl SerializeRow - Bound values
Returns: Result<QueryPager, PagerExecutionError>Example:
use futures::stream::StreamExt;

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

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

execute_single_page

execute_single_page
async fn
Does the same as Session::execute_single_page but uses the prepared statement cache.Parameters:
  • query: impl Into<Statement> - The query to execute
  • values: impl SerializeRow - Bound values
  • paging_state: PagingState - Paging state
Returns: Result<(QueryResult, PagingStateResponse), ExecutionError>Example:
use scylla::response::PagingState;
use std::ops::ControlFlow;

let mut paging_state = PagingState::start();
loop {
    let (result, paging_state_response) = caching_session
        .execute_single_page(
            "SELECT a, b FROM ks.tab",
            &[],
            paging_state
        )
        .await?;

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

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

Batch Operations

batch

batch
async fn
Does the same as Session::batch but uses the prepared statement cache.Automatically prepares unprepared statements in the batch before executing.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();
batch.append_statement("INSERT INTO ks.tab (a, b) VALUES (?, ?)");
batch.append_statement("INSERT INTO ks.tab (a, b) VALUES (?, ?)");

caching_session
    .batch(&batch, ((1, 2), (3, 4)))
    .await?;

prepare_batch

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

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

let prepared_batch = caching_session.prepare_batch(&batch).await?;

caching_session
    .batch(&prepared_batch, ((1, 2), (3, 4)))
    .await?;

Cache Management

add_prepared_statement

add_prepared_statement
async fn
Adds a prepared statement to the cache manually.If the statement is already in the cache, returns the cached version. Otherwise, prepares it and adds it to the cache.Parameters:
  • query: impl Into<&Statement> - The query to prepare
Returns: Result<PreparedStatement, PrepareError>Example:
let prepared = caching_session
    .add_prepared_statement(&"SELECT * FROM ks.tab WHERE id = ?".into())
    .await?;

get_max_capacity

get_max_capacity
fn
Retrieves the maximum capacity of the prepared statements cache.Returns: usizeExample:
let capacity = caching_session.get_max_capacity();
println!("Cache capacity: {}", capacity);

get_session

get_session
fn
Retrieves the underlying Session instance.Returns: &SessionExample:
let session = caching_session.get_session();
let keyspace = session.get_keyspace();

Usage Patterns

Simple Caching

use scylla::client::caching_session::CachingSession;
use scylla::client::session_builder::SessionBuilder;

// Create session and caching wrapper
let session = SessionBuilder::new()
    .known_node("127.0.0.1:9042")
    .build()
    .await?;

let caching_session = CachingSession::from(session, 128);

// Execute queries - they're automatically prepared and cached
for i in 0..100 {
    caching_session
        .execute_unpaged(
            "INSERT INTO ks.tab (id, value) VALUES (?, ?)",
            (i, format!("value_{}", i))
        )
        .await?;
}

Large Statement Pool

use scylla::client::caching_session::CachingSessionBuilder;

// If you have many different prepared statements
let caching_session = CachingSessionBuilder::new(session)
    .max_capacity(1000)  // Increase cache size
    .build();

With Custom Configuration

use scylla::client::caching_session::CachingSessionBuilder;

let caching_session = CachingSessionBuilder::new(session)
    .max_capacity(500)
    .use_cached_result_metadata(true)
    .build();

Performance Considerations

The CachingSession is most beneficial when:
  • You execute the same queries repeatedly with different values
  • You don’t want to manually manage prepared statement lifecycle
  • Your application has a limited set of unique queries
Be mindful of cache size:
  • If the cache is too small, statements will be frequently evicted and re-prepared
  • If the cache is too large, you may waste memory on rarely-used statements
  • The default capacity of 128 is suitable for applications with a moderate number of unique queries
  • Session - The underlying session that handles queries
  • SessionBuilder - Builder for creating sessions
  • PreparedStatement - Prepared statement returned by the cache
  • Batch - Container for batch operations

Build docs developers (and LLMs) love