Concierge::Sessions - Session Management System

Version: 0.11.2

Concierge::Sessions is a comprehensive session management system for Perl applications, maintaining state across multiple operations — in online services, CLI tools, games, time-billing systems, or any application that needs to track state over time.

Features

Installation

Standard CPAN installation:

cpanm Concierge::Sessions
# OR
perl Makefile.PL
make
make test
make install

Requirements

Quick Start

use Concierge::Sessions;

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

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

unless ($result->{success}) {
	return $result; # { success => 0, message => '...' }
}
my $session = $result->{session};
my $session_id = $session->session_id();

# Read session data
my $data_result = $session->get_data();
my $data = $data_result->{value};

# Update session data
$data->{cart} = ['item1', 'item2'];
$session->set_data($data);

# Save changes (extends session timeout)
$session->save();

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

Usage Patterns

Sliding Window Expiration

save() is the method used to persist session data to the backend (see "Data Access" below); calling it also extends the session automatically:

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

# User activity - save() extends the session
$session->save();  # Session now expires 1 hour from now

Active users stay logged in; inactive users expire automatically.

Indefinite Sessions

For application-wide state that never expires:

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

# This session never expires
$app_session->is_expired();  # Always returns false

Data Access

A session object has its own simple data field in the form of a hashref that may store any serializable Perl construct. Data storage operations by the session object are strictly limited to inserting and retrieving the whole hashref; any modifications to the hashref must be done by the app, with the updated hashref then replacing the previous one.

# Get entire data structure
my $result = $session->get_data();
my $data = $result->{value};

# Modify the data structure
$data->{username} = 'alice';
$data->{preferences}{language} = 'en';
$data->{cart} = [@items];

# Replace entire data field (marks session as dirty)
$session->set_data($data);

# Persist to backend (also extends session timeout)
$session->save();  # Session is clean again (is_dirty() now false)

Session Lifecycle

# Create
my $result = $sessions->new_session(user_id => 'user123');
my $session = $result->{session};

# Check session status
if ($session->is_valid()) {
    # Session is active and not expired
}

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

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

Backend Selection

# SQLite backend (production, high performance)
my $sessions = Concierge::Sessions->new(
    backend_class => 'Concierge::Sessions::SQLite',
    storage_dir   => '/var/app/sessions',
);

# File backend (testing, human-readable)
my $sessions = Concierge::Sessions->new(
    backend_class => 'Concierge::Sessions::File',
    storage_dir   => '/tmp/sessions',
);

API Overview

Concierge::Sessions (Factory)

All factory methods return hashrefs with {success => 1|0, ...}:

$sessions->new_session(user_id => ..., data => ...)
# Returns: {success => 1, session => $session_object}

$sessions->get_session($session_id)
# Returns: {success => 1, session => $session_object}

$sessions->delete_session($session_id)
# Returns: {success => 1, message => '...'}

$sessions->delete_user_session($user_id)
# Returns: {success => 1, deleted_count => 3}

$sessions->cleanup_sessions()
# Returns: {success => 1, deleted_count => 5}

Concierge::Sessions::Session (Session Object)

# Data methods
$session->get_data()           # {success => 1, value => $data}
$session->set_data($data)      # {success => 1}
$session->save()               # {success => 1}

# Status checks
$session->is_valid()           # 1 if active and not expired
$session->is_active()          # 1 if state is 'active'
$session->is_expired()         # 1 if past expiration time
$session->is_dirty()           # 1 if unsaved changes exist

# Accessors
$session->session_id()         # Session ID string
$session->created_at()         # Creation timestamp
$session->expires_at()         # Expiration timestamp
$session->last_updated()       # Last update timestamp
$session->storage_backend()    # Backend class name
$session->status()             # {state => 'active', dirty => 0}

Design Principles

Explicit Persistence

No automatic saving on scope exit. You control when data is persisted:

$session->set_data($new_data);  # Changes in memory only
$session->is_dirty();            # True - changes not saved
$session->save();                # Persists to backend
$session->is_dirty();            # False - saved

Single-Session Enforcement

Only one active session per user. Creating a new session invalidates old ones:

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

# $session1 is now deleted (enforced by backend)

Service Layer Pattern

All methods return consistent result hashrefs. The module only dies/croaks during initialization if the backend cannot be initialized. All other failures are non-fatal:

# Factory methods return hashrefs
my $result = $sessions->new_session(user_id => 'user123');

if ($result->{success}) {
    my $session = $result->{session};
    # Use session
} else {
    warn "Failed to create session: " . $result->{message};
}

# Session object methods also return hashrefs
my $save_result = $session->save();

if ($save_result->{success}) {
    # Session saved successfully
} else {
    warn "Failed to save session: " . $save_result->{message};
}

Default Session Timeout: If not specified, sessions timeout after 3600 seconds (1 hour). Use session_timeout => 'indefinite' for sessions that never expire.

Documentation

Full API documentation is available in the module POD:

perldoc Concierge::Sessions
perldoc Concierge::Sessions::Session

Testing

# Run all tests
prove -lv t/

# Run specific test file
prove -lv t/01-sessions-manager.t

Test coverage: 104 tests across 4 test files, all passing.

Examples

See the examples/ directory for usage examples:

Performance

Benchmarks performed on typical hardware with default settings.

License

Artistic License 2.0 - See LICENSE file for details.

This is the same license as Perl itself.

Author

Bruce Van Allen bva@cruzio.com

See Also