In-Memory Session Service

Purpose

The In-Memory Session Service provides a thread-safe, volatile implementation of the session management backend described in the parent topic Session Management. It addresses the need for a lightweight, fast, and concurrency-safe session storage mechanism without relying on external databases or disk persistence.

This subtopic specifically caters to scenarios such as:

Unlike the database-backed session service, this in-memory service maintains session state and event history entirely within process memory, ensuring rapid access and modification while supporting complex features like event-driven state updates and multi-level state merging.

Functionality

Core Responsibilities

Key Workflows and Methods

Session Creation (Create)

Session Retrieval (Get)

Session Listing (List)

Event Appending (AppendEvent)

State Management

The service maintains three state layers:

  1. Application State: Shared across all sessions and users for an application.

  2. User State: Specific to a user within an application.

  3. Session State: Specific to an individual session.

State deltas from events are extracted and merged appropriately into these layers, enabling flexible and reusable state management.

Internal Data Structures

Example Code Snippet

Appending an event updates session and global states:

func (s *inMemoryService) AppendEvent(ctx context.Context, curSession Session, event *Event) error {
    if event.Partial {
        return nil // partial events not persisted
    }
    s.mu.Lock()
    defer s.mu.Unlock()

    storedSession, ok := s.sessions.Get(curSession.(*session).id.Encode())
    if !ok {
        return fmt.Errorf("session not found")
    }

    if err := curSession.(*session).appendEvent(event); err != nil {
        return err
    }

    storedSession.events = append(storedSession.events, event)
    storedSession.updatedAt = event.Timestamp

    if len(event.Actions.StateDelta) > 0 {
        appDelta, userDelta, sessionDelta := sessionutils.ExtractStateDeltas(event.Actions.StateDelta)
        s.updateAppState(appDelta, curSession.AppName())
        s.updateUserState(userDelta, curSession.AppName(), curSession.UserID())
        // merge session delta into session state
        for k, v := range sessionDelta {
            storedSession.state[k] = v
        }
    }
    return nil
}

This method ensures that state changes propagate correctly across layers and that the session event history remains consistent.

Integration with Session Management

The In-Memory Session Service is one of the pluggable storage backends under the parent topic Session Management. It implements the same core interface as the database-backed session service, allowing seamless substitution depending on deployment needs.

Diagram: Class Structure of In-Memory Session Service

classDiagram
class inMemoryService {
- mu: sync.RWMutex
- sessions: omap.Map[string,*session]
- userState: map[string]map[string]stateMap
- appState: map[string]stateMap
+ Create()
+ Get()
+ List()
+ Delete()
+ AppendEvent()
+ updateAppState()
+ updateUserState()
+ mergeStates()
}
class session {
- id: id
- mu: sync.RWMutex
- events: []*Event
- state: map[string]any
- updatedAt: time.Time
+ ID()
+ AppName()
+ UserID()
+ State()
+ Events()
+ LastUpdateTime()
+ appendEvent()
}
class id {
- appName: string
- userID: string
- sessionID: string
+ Encode()
+ Decode()
}
inMemoryService "1" o-- "*" session : manages
session "1" *-- "1" id : identifies

This diagram illustrates the core structural relationships of the in-memory service, highlighting how sessions are identified, stored, and protected for concurrent access.