CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-seleniumhq-selenium--selenium-grid

Selenium Grid is a distributed testing infrastructure that allows running WebDriver tests in parallel across multiple machines and browsers.

Pending
Overview
Eval results
Files

session-storage.mddocs/

Session Storage

The session storage system provides persistent mapping between session IDs and their locations across the grid, enabling request routing and session lifecycle management.

Capabilities

Core SessionMap Interface

The main abstract class for storing and retrieving session location information.

/**
 * Abstract class for mapping session IDs to their locations and metadata
 */
abstract class SessionMap implements HasReadyState, Routable {
    /** Protected constructor with tracer dependency */
    protected SessionMap(Tracer tracer);
    
    /** Add a new session to the map */
    abstract boolean add(Session session);
    
    /** Retrieve session information by ID */
    abstract Session get(SessionId id) throws NoSuchSessionException;
    
    /** Remove a session from the map */
    abstract void remove(SessionId id);
    
    /** Get the URI for a session by ID */
    URI getUri(SessionId id) throws NoSuchSessionException;
}

Local SessionMap Implementation

In-memory session map implementation for single-process deployments.

/**
 * In-memory session map implementation
 */
class LocalSessionMap extends SessionMap {
    /** Create a local session map with event bus integration */
    LocalSessionMap(Tracer tracer, EventBus eventBus);
    
    /** Factory method to create from configuration */
    static SessionMap create(Config config);
    
    boolean isReady();
    boolean add(Session session);
    Session get(SessionId id);
    void remove(SessionId id);
    Set<Session> getSessions();
}

Usage Example:

// Create local session map
EventBus eventBus = new GuavaEventBus();
SessionMap sessionMap = new LocalSessionMap(tracer, eventBus);

// Add a session
Session session = new Session(
    new SessionId(UUID.randomUUID()),
    URI.create("http://node1:5555"),
    new ImmutableCapabilities("browserName", "chrome"),
    new ImmutableCapabilities("browserName", "chrome", "version", "91.0"),
    Instant.now()
);

boolean added = sessionMap.add(session);
if (added) {
    System.out.println("Session stored: " + session.getId());
}

// Retrieve session
Session retrieved = sessionMap.get(session.getId());
if (retrieved != null) {
    System.out.println("Session found at: " + retrieved.getUri());
}

// Remove session when done
sessionMap.remove(session.getId());

Remote SessionMap Client

Client for accessing session maps running in remote processes.

/**
 * Remote session map client for distributed deployments
 */
class RemoteSessionMap extends SessionMap {
    RemoteSessionMap(HttpClient.Factory httpClientFactory, URI sessionMapUri);
    
    // All operations implemented via HTTP calls
    boolean isReady();
    boolean add(Session session);
    Session get(SessionId id);
    void remove(SessionId id);
    Set<Session> getSessions();
}

JDBC SessionMap Implementation

Database-backed session map for persistent storage across restarts.

/**
 * JDBC-based session map implementation for database persistence
 */
class JdbcSessionMap extends SessionMap {
    JdbcSessionMap(Tracer tracer, DataSource dataSource);
    
    /** Create from configuration with database settings */
    static SessionMap create(Config config);
    
    boolean isReady();
    boolean add(Session session);
    Session get(SessionId id);
    void remove(SessionId id);
    Set<Session> getSessions();
    
    /** Clean up expired sessions */
    void cleanupExpiredSessions(Duration maxAge);
}

Configuration Example:

[sessionmap]
implementation = "org.openqa.selenium.grid.sessionmap.jdbc.JdbcSessionMap"

[sessionmap.jdbc]
url = "jdbc:postgresql://localhost:5432/selenium_grid"
username = "grid_user"
password = "grid_password"
max-pool-size = 10

Redis SessionMap Implementation

Redis-backed session map for high-performance distributed storage.

/**
 * Redis-based session map implementation for distributed caching
 */
class RedisSessionMap extends SessionMap {
    RedisSessionMap(Tracer tracer, RedisClient redisClient);
    
    /** Create from configuration with Redis settings */
    static SessionMap create(Config config);
    
    boolean isReady();
    boolean add(Session session);
    Session get(SessionId id);
    void remove(SessionId id);
    Set<Session> getSessions();
    
    /** Set session expiration time */
    void setSessionExpiration(SessionId id, Duration ttl);
}

Configuration Example:

[sessionmap]
implementation = "org.openqa.selenium.grid.sessionmap.redis.RedisSessionMap"

[sessionmap.redis]
host = "redis.example.com"
port = 6379
password = "redis_password"
database = 0
connection-pool-size = 20

Configuration Options

SessionMap-specific configuration settings.

/**
 * Configuration options for session map behavior
 */
class SessionMapOptions {
    static final String SESSION_MAP_SECTION = "sessionmap";
    
    /** Get session map implementation class */
    String getSessionMapImplementation(Config config);
    
    /** Get session cleanup interval */
    Duration getCleanupInterval(Config config);
    
    /** Get maximum session age before cleanup */
    Duration getMaxSessionAge(Config config);
    
    /** Get database connection settings for JDBC implementation */
    JdbcConfig getJdbcConfig(Config config);
    
    /** Get Redis connection settings for Redis implementation */
    RedisConfig getRedisConfig(Config config);
}

Session Lifecycle Management

Adding Sessions

// Session created by node, added to map by distributor
Session newSession = new Session(
    sessionId,
    nodeUri,
    requestedCapabilities,
    actualCapabilities,
    Instant.now()
);

boolean success = sessionMap.add(newSession);
if (!success) {
    // Handle duplicate session ID or storage failure
    throw new GridException("Failed to store session: " + sessionId);
}

Session Lookup for Request Routing

// Router uses session map to find session location
public HttpResponse routeWebDriverCommand(HttpRequest request) {
    SessionId sessionId = extractSessionId(request.getUri());
    
    Session session = sessionMap.get(sessionId);
    if (session == null) {
        return new HttpResponse()
            .setStatus(HTTP_NOT_FOUND)
            .setContent(asJson(Map.of("error", "Session not found: " + sessionId)));
    }
    
    // Proxy request to the node owning the session
    URI nodeUri = session.getUri();
    return httpClient.execute(nodeUri, request);
}

Session Cleanup

// Clean up session when it ends
public void endSession(SessionId sessionId) {
    sessionMap.remove(sessionId);
    
    // Optionally fire session ended event
    eventBus.fire(new SessionClosedEvent(sessionId));
}

// Periodic cleanup of expired sessions (for persistent implementations)
@Scheduled(fixedRate = 300000) // Every 5 minutes
public void cleanupExpiredSessions() {
    if (sessionMap instanceof JdbcSessionMap) {
        ((JdbcSessionMap) sessionMap).cleanupExpiredSessions(Duration.ofHours(2));
    }
}

High Availability Patterns

Replication

// Use multiple session maps for redundancy
public class ReplicatedSessionMap implements SessionMap {
    private final List<SessionMap> replicas;
    
    public boolean add(Session session) {
        boolean success = true;
        for (SessionMap replica : replicas) {
            success &= replica.add(session);
        }
        return success;
    }
    
    public Session get(SessionId id) {
        // Try replicas in order, return first success
        for (SessionMap replica : replicas) {
            try {
                Session session = replica.get(id);
                if (session != null) return session;
            } catch (Exception e) {
                // Try next replica
            }
        }
        return null;
    }
}

Backup and Recovery

// Backup sessions to persistent storage
@Scheduled(fixedRate = 60000) // Every minute
public void backupSessions() {
    Set<Session> activeSessions = sessionMap.getSessions();
    
    // Write to backup storage
    try (FileWriter backup = new FileWriter("sessions-backup.json")) {
        backup.write(gson.toJson(activeSessions));
    }
}

// Restore sessions on startup
public void restoreSessions() {
    try (FileReader backup = new FileReader("sessions-backup.json")) {
        Type setType = new TypeToken<Set<Session>>(){}.getType();
        Set<Session> sessions = gson.fromJson(backup, setType);
        
        for (Session session : sessions) {
            sessionMap.add(session);
        }
    }
}

Monitoring and Metrics

// Session map metrics
public class SessionMapMetrics {
    public int getActiveSessionCount() {
        return sessionMap.getSessions().size();
    }
    
    public Map<String, Integer> getSessionsByBrowser() {
        return sessionMap.getSessions().stream()
            .collect(Collectors.groupingBy(
                session -> session.getCapabilities().getBrowserName(),
                Collectors.summingInt(session -> 1)
            ));
    }
    
    public Duration getAverageSessionAge() {
        Instant now = Instant.now();
        return sessionMap.getSessions().stream()
            .map(session -> Duration.between(session.getStartTime(), now))
            .reduce(Duration.ZERO, Duration::plus);
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-grid

docs

cli-commands.md

configuration.md

index.md

node-management.md

request-routing.md

security.md

session-distribution.md

session-queuing.md

session-storage.md

tile.json