NAME

Concierge::Sessions - Session manager with factory pattern and multiple backend support

VERSION

v0.8.5

SYNOPSIS

use Concierge::Sessions;

# Create session manager
my $sessions = Concierge::Sessions->new(
    backend     => 'database',
    storage_dir => '/var/app/sessions',
);

# Create a new user session
my $result = $sessions->new_session(
    user_id         => 'user123',
    session_timeout => 3600,         # 1 hour (optional, default: 3600)
    data            => {
        username    => 'alice',
        cart        => [],
        preferences => { theme => 'dark' },
    },
);

unless ($result->{success}) {
    die "Failed to create session: $result->{message}";
}

my $session = $result->{session};
my $session_id = $session->session_id();

# Retrieve an existing session
my $retrieved = $sessions->get_session($session_id);

if ($retrieved->{success}) {
    my $s = $retrieved->{session};
    # Use the session...
}

# Delete a session
$sessions->delete_session($session_id);

# Delete all sessions for a user
$sessions->delete_user_session('user123');

# Clean up expired sessions
my $cleanup = $sessions->cleanup_sessions();
print "Removed $cleanup->{deleted_count} expired sessions\n";

DESCRIPTION

Concierge::Sessions is a session management system that provides a factory pattern for creating and managing session objects. It supports multiple storage backends and implements a service layer pattern with consistent return values.

The manager handles session lifecycle operations including creation, retrieval, deletion, and cleanup. Individual session data operations are handled by the Concierge::Sessions::Session objects returned by this manager.

FEATURES

  • **Application-controlled data storage** - Store any serializable data structure in sessions

  • **In-memory performance** - Fast access to state and configuration

  • **Optional persistence** - Session tracks changes, saves when App tells it to

  • **Single-session enforcement** - Enforces one active session per user

  • **Sliding window expiration** - Sessions auto-extend when users are active

  • **Indefinite sessions** - Application-wide sessions that never expire

  • **Multiple backends** - Database/SQLite (production), File (testing/small user population)

  • **Modern Perl** - v5.36+ with contemporary best practices

  • **Service layer pattern** - Non-fatal errors with descriptive messages

METHODS

All methods return hashrefs with the following structure:

{ success => 1, session => $session_object }  # For successful operations
{ success => 0, message => "Error description" }  # For failures

new

Creates a new session manager instance.

my $sessions = Concierge::Sessions->new(
    backend     => 'database',     # Optional: 'database' or 'file' (default: 'database')
    storage_dir => '/path/to/dir', # Required: directory for session storage
);

Parameters:

  • backend - Backend type to use. Either 'database' (default) or 'file'. Case-insensitive.

  • storage_dir - Directory where session data will be stored. Required. The directory will be created if it doesn't exist.

Returns a blessed Concierge::Sessions object.

Dies if the backend cannot be initialized.

new_session

Creates a new session for the specified user.

my $result = $sessions->new_session(
    user_id         => 'user123',              # Required
    session_timeout => 3600,                   # Optional: seconds or 'indefinite'
    data            => \%initial_data,         # Optional: hashref of session data
);

Parameters:

  • user_id - Required. Unique identifier for the user. Creating a new session for a user who already has an active session will cause the old session to be deleted.

  • session_timeout - Optional. Timeout in seconds, or the string 'indefinite' for a session that never expires. Default: 3600 (1 hour).

  • data - Optional. Initial session data as a hashref. This can be any data structure that can be represented as JSON.

Returns:

{
    success => 1,
    session => $session_object,  # Concierge::Sessions::Session object
}

Or on error:

{
    success => 0,
    message => "Error description",
}

get_session

Retrieves an existing session by session ID.

my $result = $sessions->get_session($session_id);

Parameters:

  • session_id - Required. The session ID returned when the session was created.

Returns:

{
    success => 1,
    session => $session_object,  # Concierge::Sessions::Session object
}

Or on error:

{
    success => 0,
    message => "Session not found or expired",
}

Note: Expired sessions are filtered by the backend and cannot be retrieved.

delete_session

Deletes a session by session ID.

my $result = $sessions->delete_session($session_id);

Parameters:

  • session_id - Required. The session ID to delete.

Returns:

{
    success => 1,
    message => "Session deleted",
}

If the session doesn't exist, the operation is still considered successful (no error is returned).

delete_user_session

Deletes all sessions for a specific user.

my $result = $sessions->delete_user_session($user_id);

Parameters:

  • user_id - Required. The user ID whose sessions should be deleted.

Returns:

{
    success => 1,
    deleted_count => 3,  # Number of sessions deleted
}

Useful for logging out a user from all devices/sessions.

cleanup_sessions

Removes all expired sessions from the backend storage.

my $result = $sessions->cleanup_sessions();

Parameters:

None.

Returns:

{
    success => 1,
    deleted_count => 15,  # Number of expired sessions removed
}

Returns a count of 0 if no expired sessions were found.

This method should be called periodically (e.g., via cron) to clean up expired sessions and reclaim storage space.

BACKENDS

Concierge::Sessions supports multiple storage backends:

database

The default and recommended backend for production use. Uses SQLite for storage.

my $sessions = Concierge::Sessions->new(
    backend     => 'database',
    storage_dir => '/var/app/sessions',
);

Features:

  • High performance (4,000-5,000 operations per second)

  • ACID-compliant storage

  • Automatic cleanup of expired sessions during retrieval

  • Single-session enforcement at database level

Requires: DBI and DBD::SQLite

file

Simple file-based backend using JSON format. Useful for testing and development.

my $sessions = Concierge::Sessions->new(
    backend     => 'file',
    storage_dir => '/tmp/sessions',
);

Features:

  • Human-readable JSON files

  • Simple debugging (view session data directly)

  • No database dependencies

  • Lower performance (~1,000 operations per second)

The File backend stores each session as a separate JSON file named after the session ID.

RETURN VALUES

All methods return hashrefs with consistent structure:

Successful operations:

# For new_session and get_session
{
    success => 1,
    session => $session_object,
}

# For delete_session
{
    success => 1,
    message => "Session deleted",
}

# For delete_user_session and cleanup_sessions
{
    success => 1,
    deleted_count => 5,
}

Failed operations:

{
    success => 0,
    message => "Description of what went wrong",
}

Always check $result-{success}> before accessing other fields.

EXAMPLES

Basic Session Lifecycle

use Concierge::Sessions;

my $sessions = Concierge::Sessions->new(
    backend     => 'database',
    storage_dir => '/var/app/sessions',
);

# Create session
my $result = $sessions->new_session(
    user_id => 'user123',
    data    => { cart => [] },
);

my $session = $result->{session};

# Retrieve session later
my $retrieved = $sessions->get_session($session->session_id());
if ($retrieved->{success}) {
    my $data = $retrieved->{session}->get_data()->{value};
    # Use session data...
}

# Delete session
$sessions->delete_session($session->session_id());

Application-Wide Indefinite Session

my $app_session = $sessions->new_session(
    user_id         => 'application_state',
    session_timeout => 'indefinite',
    data            => {
        metrics    => { requests_processed => 0 },
        subsystems => { database => 'connected' },
    },
)->{session};

# This session never expires
# Use it for application-wide state tracking

Logout from All Devices

# Delete all sessions for a user
my $result = $sessions->delete_user_session('user123');
print "Logged out from $result->{deleted_count} devices\n";

Periodic Cleanup

# Run this periodically (e.g., from cron)
my $cleanup = $sessions->cleanup_sessions();
print "Cleaned up $cleanup->{deleted_count} expired sessions\n";

SINGLE-SESSION ENFORCEMENT

The system enforces one active session per user. When you create a new session for a user who already has an active session, the old session is automatically deleted.

my $session1 = $sessions->new_session(user_id => 'user123')->{session};
my $id1 = $session1->session_id();

# Create another session for same user
my $session2 = $sessions->new_session(user_id => 'user123')->{session};

# $session1 has been deleted
my $check = $sessions->get_session($id1);
# $check->{success} is false - session no longer exists

This enforcement is implemented at the backend level for consistency and to ensure database integrity.

SLIDING WINDOW EXPIRATION

Sessions automatically extend when you call save() on the session object:

my $session = $sessions->new_session(
    user_id         => 'user123',
    session_timeout => 3600,  # 1 hour
)->{session};

# User is active - extend the session
$session->save();  # Session now expires 1 hour from now

This "sliding window" approach keeps active users logged in while allowing inactive sessions to expire naturally.

SEE ALSO

Concierge::Sessions::Session - Session object methods for data access

Concierge::Sessions::SQLite - SQLite backend implementation

Concierge::Sessions::File - File backend implementation

DBI - Database interface

JSON::PP - JSON handling

AUTHOR

Bruce Van Allen <bva@cruzio.com>

LICENSE

Artistic License 2.0