Struct SqliteSessionBackend
pub struct SqliteSessionBackend { /* private fields */ }Expand description
SQLite-backed session store with FTS5 and WAL mode.
Implementations§
Trait Implementations§
Source§impl SessionBackend for SqliteSessionBackend
impl SessionBackend for SqliteSessionBackend
Source§fn replace_conversation_state(
&self,
session_key: &str,
messages: &[ChatMessage],
breadcrumb_present: bool,
) -> Result<(), Error>
fn replace_conversation_state( &self, session_key: &str, messages: &[ChatMessage], breadcrumb_present: bool, ) -> Result<(), Error>
Replace the transcript and the breadcrumb flag in one transaction, so
the pair can never commit as a partial write: a crash or error
between the two rolls the whole transaction back rather than leaving
a new transcript paired with a stale flag (or vice versa). The
default SessionBackend::replace_conversation_state performs these
as two separate committed operations; this override replaces it for
SQLite where a single transaction can make the pair atomic.
Source§fn replace_conversation_state_if_exists(
&self,
session_key: &str,
messages: &[ChatMessage],
breadcrumb_present: bool,
) -> Result<bool, Error>
fn replace_conversation_state_if_exists( &self, session_key: &str, messages: &[ChatMessage], breadcrumb_present: bool, ) -> Result<bool, Error>
Existence-guarded replacement in one transaction: when the metadata
row is gone (a concurrent delete won), return Ok(false) without
writing anything, so a completed turn can never recreate a session
the user just deleted. The plain replace_conversation_state
breadcrumb upsert would INSERT a fresh metadata row here.
delete_session holds the same connection mutex, so the existence
probe and both writes are atomic with respect to it. A genuine query
failure (anything other than “no such row”) is still an error, not a
silent skip: only a missing row means “deleted”.
Source§fn update_last(
&self,
session_key: &str,
message: &ChatMessage,
) -> Result<bool, Error>
fn update_last( &self, session_key: &str, message: &ChatMessage, ) -> Result<bool, Error>
Efficiently update the last message in-place (single UPDATE instead of DELETE + INSERT). Used for incremental persistence during streaming.
Source§fn load(&self, session_key: &str) -> Vec<ChatMessage>
fn load(&self, session_key: &str) -> Vec<ChatMessage>
Source§fn load_with_timestamps(&self, session_key: &str) -> Vec<TimestampedMessage>
fn load_with_timestamps(&self, session_key: &str) -> Vec<TimestampedMessage>
load, but each row carries its persisted created_at
when the backend has one. Default impl falls back to load
without timestamps so non-SQLite backends keep working.Source§fn append(&self, session_key: &str, message: &ChatMessage) -> Result<(), Error>
fn append(&self, session_key: &str, message: &ChatMessage) -> Result<(), Error>
Source§fn rewrite_messages(
&self,
session_key: &str,
messages: &[ChatMessage],
) -> Result<(), Error>
fn rewrite_messages( &self, session_key: &str, messages: &[ChatMessage], ) -> Result<(), Error>
messages. Used to
keep the canonical stored history in sync with a caller-owned buffer
that trimmed or rewrote turns in place (e.g. history-budget
enforcement), rather than appending on top of a now-stale transcript.
The default clears the session (clear_messages) and re-appends
every message through the required append primitive, so every
backend gets real replacement semantics — a backend that never
overrides this does not silently drop the caller’s authoritative
trimmed history. It is O(n) calls and not atomic; backends with a
native batch/transactional rewrite (JSONL, SQLite) override this.Source§fn remove_last(&self, session_key: &str) -> Result<bool, Error>
fn remove_last(&self, session_key: &str) -> Result<bool, Error>
true if a message was removed.Source§fn list_sessions(&self) -> Vec<String>
fn list_sessions(&self) -> Vec<String>
Source§fn list_sessions_with_metadata(&self) -> Vec<SessionMetadata>
fn list_sessions_with_metadata(&self) -> Vec<SessionMetadata>
Source§fn cleanup_stale(&self, ttl_hours: u32) -> Result<usize, Error>
fn cleanup_stale(&self, ttl_hours: u32) -> Result<usize, Error>
fn clear_messages(&self, session_key: &str) -> Result<usize, Error>
Source§fn delete_session(&self, session_key: &str) -> Result<bool, Error>
fn delete_session(&self, session_key: &str) -> Result<bool, Error>
true if the session existed.Source§fn delete_session_owned(
&self,
session_key: &str,
owner_principal_id: &str,
) -> Result<bool, Error>
fn delete_session_owned( &self, session_key: &str, owner_principal_id: &str, ) -> Result<bool, Error>
owner_principal_id matches the stored
owner: the ownership check and the destruction of the ownership row
are one SQL statement (the RFC 7141 atomic storage predicate), so a
concurrent re-stamp cannot race them apart. Backends without
ownership support fail closed: Ok(false), nothing deleted.fn clear_agent_attribution(&self, agent_alias: &str) -> Result<usize, Error>
fn rename_agent_attribution(&self, from: &str, to: &str) -> Result<usize, Error>
fn count_agent_attribution(&self, agent_alias: &str) -> Result<usize, Error>
fn session_exists(&self, session_key: &str) -> bool
Source§fn set_session_name(&self, session_key: &str, name: &str) -> Result<(), Error>
fn set_session_name(&self, session_key: &str, name: &str) -> Result<(), Error>
Source§fn get_session_name(&self, session_key: &str) -> Result<Option<String>, Error>
fn get_session_name(&self, session_key: &str) -> Result<Option<String>, Error>
fn get_session_metadata(&self, session_key: &str) -> Option<SessionMetadata>
Source§fn set_session_state(
&self,
session_key: &str,
state: &str,
turn_id: Option<&str>,
) -> Result<(), Error>
fn set_session_state( &self, session_key: &str, state: &str, turn_id: Option<&str>, ) -> Result<(), Error>
turn_id identifies the current turn (set when running, cleared on idle).Source§fn get_session_state(
&self,
session_key: &str,
) -> Result<Option<SessionState>, Error>
fn get_session_state( &self, session_key: &str, ) -> Result<Option<SessionState>, Error>
None if the backend doesn’t track state.Source§fn list_running_sessions(&self) -> Vec<SessionMetadata>
fn list_running_sessions(&self) -> Vec<SessionMetadata>
Source§fn list_stuck_sessions(&self, threshold_secs: u64) -> Vec<SessionMetadata>
fn list_stuck_sessions(&self, threshold_secs: u64) -> Vec<SessionMetadata>
threshold_secs.Source§fn search(&self, query: &SessionQuery) -> Vec<SessionMetadata>
fn search(&self, query: &SessionQuery) -> Vec<SessionMetadata>
Source§fn set_session_agent_alias(
&self,
session_key: &str,
agent_alias: &str,
) -> Result<(), Error>
fn set_session_agent_alias( &self, session_key: &str, agent_alias: &str, ) -> Result<(), Error>
Source§fn set_session_principal(
&self,
session_key: &str,
principal_id: &str,
) -> Result<(), Error>
fn set_session_principal( &self, session_key: &str, principal_id: &str, ) -> Result<(), Error>
session/new when the dispatcher has a real (non-sentinel)
principal bound. No-op for backends without the column.Source§fn get_session_agent_alias(
&self,
session_key: &str,
) -> Result<Option<String>, Error>
fn get_session_agent_alias( &self, session_key: &str, ) -> Result<Option<String>, Error>
None means
the backend doesn’t track it or no session-level state exists yet
(callers should assume false, not attempt text inference).fn set_session_context( &self, session_key: &str, context: SessionContext<'_>, ) -> Result<(), Error>
Source§fn try_load(&self, session_key: &str) -> Result<Vec<ChatMessage>, Error>
fn try_load(&self, session_key: &str) -> Result<Vec<ChatMessage>, Error>
load, but distinguishes an unreadable transcript from a genuinely
missing/empty session. Ok(vec) is the verified durable transcript
(empty means no session), Err means the transcript could not be read
or parsed and must not be treated as empty. The default impl
delegates to load and never errors; backends that can detect read
failures (JSONL) override this so callers can fail closed instead of
seeding a new-message-only cache over an existing transcript.Auto Trait Implementations§
impl !Freeze for SqliteSessionBackend
impl !RefUnwindSafe for SqliteSessionBackend
impl !UnwindSafe for SqliteSessionBackend
impl Send for SqliteSessionBackend
impl Sync for SqliteSessionBackend
impl Unpin for SqliteSessionBackend
impl UnsafeUnpin for SqliteSessionBackend
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more