Database Session Service

Purpose

The Database Session Service provides a robust, persistent implementation of the session management functionality within the broader Session Management topic. Unlike the in-memory alternative, this subtopic addresses the need for durable storage of user-agent interaction sessions by leveraging a SQL database through the GORM ORM library. It ensures session data—including state, event history, and metadata—is reliably saved and retrieved across application restarts and distributed deployments.

This service is specifically designed to handle transactional consistency and concurrent updates to session state, supporting complex multi-turn conversations with durable state persistence. It also manages separate namespaces of state (application-level, per-user, and per-session) with clear transactional boundaries, enabling scalable and consistent session management.

Functionality

Key Features

Core Workflows

Session Creation

When a new session is created, the service:

  1. Generates or uses the provided session ID.

  2. In a transaction, fetches or initializes app and user state records.

  3. Applies any initial state deltas to app and user state.

  4. Creates a new session record with the merged initial state.

  5. Returns a session object with combined namespaces visible.

func (s *databaseService) Create(ctx context.Context, req *session.CreateRequest) (*session.CreateResponse, error) {
    // Transactionally create session and update app/user state
    err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
        // fetch app and user states
        storageApp, err := fetchStorageAppState(tx, req.AppName)
        storageUser, err := fetchStorageUserState(tx, req.AppName, req.UserID)

        // extract and apply state deltas
        appDelta, userDelta, sessionState := extractStateDeltas(req.State)
        maps.Copy(storageApp.State, appDelta)
        maps.Copy(storageUser.State, userDelta)

        // save app and user states
        tx.Save(&storageApp)
        tx.Save(&storageUser)

        // create session with sessionState
        tx.Create(createdSession)
        return nil
    })
}

Event Appending and State Updates

Appending events involves:

  1. Ignoring partial events (not persisted).

  2. Trimming temporary state keys from event deltas to avoid persisting ephemeral state.

  3. In a transaction:

    • Fetching the latest session record.

    • Checking for stale session updates via UpdateTime.

    • Fetching and updating app, user, and session states with the event's state delta.

    • Persisting the event as a new record.

    • Updating the session's update timestamp and state.

  4. Updating the in-memory session object to reflect new state and event history.

func (s *databaseService) AppendEvent(ctx context.Context, curSession session.Session, event *session.Event) error {
    if event.Partial {
        return nil
    }
    err := s.applyEvent(ctx, curSession.(*localSession), event)
    if err != nil {
        return err
    }
    return curSession.(*localSession).appendEvent(event)
}

Session Retrieval

Fetching a session retrieves:

func (s *databaseService) Get(ctx context.Context, req *session.GetRequest) (*session.GetResponse, error) {
    // Query session record
    s.db.Where(...).First(&foundSession)

    // Query events with filters
    s.db.Where(...).Order("timestamp DESC").Limit(...).Find(&storageEvents)

    // Fetch app and user state
    storageApp, _ := fetchStorageAppState(...)
    storageUser, _ := fetchStorageUserState(...)

    // Merge states and reorder events
    mergedState := mergeStates(storageApp.State, storageUser.State, sessionState)
}

State Delta Extraction and Merging

The service handles state delta maps by splitting keys based on prefixes to isolate app, user, and session state updates, ignoring temporary keys starting with temp:.

Merging re-applies prefixes to app and user state keys when returning the combined state map to clients.

func extractStateDeltas(delta map[string]any) (appDelta, userDelta, sessionDelta map[string]any) {
    for key, value := range delta {
        if cleanKey, found := strings.CutPrefix(key, session.KeyPrefixApp); found {
            appDelta[cleanKey] = value
        } else if cleanKey, found := strings.CutPrefix(key, session.KeyPrefixUser); found {
            userDelta[cleanKey] = value
        } else if !strings.HasPrefix(key, session.KeyPrefixTemp) {
            sessionDelta[key] = value
        }
    }
    return
}

func mergeStates(appState, userState, sessionState map[string]any) map[string]any {
    merged := make(map[string]any)
    maps.Copy(merged, sessionState)
    for k, v := range appState {
        merged[session.KeyPrefixApp+k] = v
    }
    for k, v := range userState {
        merged[session.KeyPrefixUser+k] = v
    }
    return merged
}

Integration

The Database Session Service is a concrete implementation of the session management interface defined by the parent Session Management topic. It fits into the architecture as a pluggable backend alongside the in-memory alternative. This allows the system to support both ephemeral session storage (for fast prototyping or testing) and durable persistence (for production scenarios requiring reliability).

It is utilized by the Agent Execution Runner and other components that interact with session state and event history to maintain conversational context. The session data managed here can be augmented by artifacts from Artifact Management and can be accessed or extended by workflow agents in Agent Workflow Management.

The service also supports telemetry and observability through consistent event recording, enabling tracing of session state changes over time as seen in Telemetry and Observability.

Diagram

sequenceDiagram
participant Client
participant DBService as DatabaseSessionService
participant DB as SQL Database
Client->>DBService: CreateSession(request)
DBService->>DB: Begin Transaction
DBService->>DB: Fetch or Init AppState, UserState
DBService->>DB: Apply Initial State Deltas
DBService->>DB: Save AppState, UserState, Session
DBService->>DB: Commit Transaction
DBService->>Client: Return Created Session
Client->>DBService: AppendEvent(session, event)
DBService->>DB: Begin Transaction
DBService->>DB: Fetch Session Record
DBService->>DB: Check for Stale Session
DBService->>DB: Fetch AppState, UserState
DBService->>DB: Apply Event State Deltas
DBService->>DB: Save Event Record
DBService->>DB: Update Session Timestamp and State
DBService->>DB: Commit Transaction
DBService->>Client: Acknowledge Event Append
Client->>DBService: GetSession(sessionKey, filters)
DBService->>DB: Query Session Record
DBService->>DB: Query Events (with filters)
DBService->>DB: Fetch AppState, UserState
DBService->>DBService: Merge States with Prefixes
DBService->>Client: Return Session with Events and Merged State

This sequence diagram visualizes the core transactional workflows of the Database Session Service, highlighting the interaction between client calls, the service logic, and the underlying SQL database operations. It captures how session creation, event appending, and session retrieval are handled atomically to maintain data consistency and integrity.