0
# Session Storage
1
2
The session storage system provides persistent mapping between session IDs and their locations across the grid, enabling request routing and session lifecycle management.
3
4
## Capabilities
5
6
### Core SessionMap Interface
7
8
The main abstract class for storing and retrieving session location information.
9
10
```java { .api }
11
/**
12
* Abstract class for mapping session IDs to their locations and metadata
13
*/
14
abstract class SessionMap implements HasReadyState, Routable {
15
/** Protected constructor with tracer dependency */
16
protected SessionMap(Tracer tracer);
17
18
/** Add a new session to the map */
19
abstract boolean add(Session session);
20
21
/** Retrieve session information by ID */
22
abstract Session get(SessionId id) throws NoSuchSessionException;
23
24
/** Remove a session from the map */
25
abstract void remove(SessionId id);
26
27
/** Get the URI for a session by ID */
28
URI getUri(SessionId id) throws NoSuchSessionException;
29
}
30
```
31
32
### Local SessionMap Implementation
33
34
In-memory session map implementation for single-process deployments.
35
36
```java { .api }
37
/**
38
* In-memory session map implementation
39
*/
40
class LocalSessionMap extends SessionMap {
41
/** Create a local session map with event bus integration */
42
LocalSessionMap(Tracer tracer, EventBus eventBus);
43
44
/** Factory method to create from configuration */
45
static SessionMap create(Config config);
46
47
boolean isReady();
48
boolean add(Session session);
49
Session get(SessionId id);
50
void remove(SessionId id);
51
Set<Session> getSessions();
52
}
53
```
54
55
**Usage Example:**
56
57
```java
58
// Create local session map
59
EventBus eventBus = new GuavaEventBus();
60
SessionMap sessionMap = new LocalSessionMap(tracer, eventBus);
61
62
// Add a session
63
Session session = new Session(
64
new SessionId(UUID.randomUUID()),
65
URI.create("http://node1:5555"),
66
new ImmutableCapabilities("browserName", "chrome"),
67
new ImmutableCapabilities("browserName", "chrome", "version", "91.0"),
68
Instant.now()
69
);
70
71
boolean added = sessionMap.add(session);
72
if (added) {
73
System.out.println("Session stored: " + session.getId());
74
}
75
76
// Retrieve session
77
Session retrieved = sessionMap.get(session.getId());
78
if (retrieved != null) {
79
System.out.println("Session found at: " + retrieved.getUri());
80
}
81
82
// Remove session when done
83
sessionMap.remove(session.getId());
84
```
85
86
### Remote SessionMap Client
87
88
Client for accessing session maps running in remote processes.
89
90
```java { .api }
91
/**
92
* Remote session map client for distributed deployments
93
*/
94
class RemoteSessionMap extends SessionMap {
95
RemoteSessionMap(HttpClient.Factory httpClientFactory, URI sessionMapUri);
96
97
// All operations implemented via HTTP calls
98
boolean isReady();
99
boolean add(Session session);
100
Session get(SessionId id);
101
void remove(SessionId id);
102
Set<Session> getSessions();
103
}
104
```
105
106
### JDBC SessionMap Implementation
107
108
Database-backed session map for persistent storage across restarts.
109
110
```java { .api }
111
/**
112
* JDBC-based session map implementation for database persistence
113
*/
114
class JdbcSessionMap extends SessionMap {
115
JdbcSessionMap(Tracer tracer, DataSource dataSource);
116
117
/** Create from configuration with database settings */
118
static SessionMap create(Config config);
119
120
boolean isReady();
121
boolean add(Session session);
122
Session get(SessionId id);
123
void remove(SessionId id);
124
Set<Session> getSessions();
125
126
/** Clean up expired sessions */
127
void cleanupExpiredSessions(Duration maxAge);
128
}
129
```
130
131
**Configuration Example:**
132
133
```toml
134
[sessionmap]
135
implementation = "org.openqa.selenium.grid.sessionmap.jdbc.JdbcSessionMap"
136
137
[sessionmap.jdbc]
138
url = "jdbc:postgresql://localhost:5432/selenium_grid"
139
username = "grid_user"
140
password = "grid_password"
141
max-pool-size = 10
142
```
143
144
### Redis SessionMap Implementation
145
146
Redis-backed session map for high-performance distributed storage.
147
148
```java { .api }
149
/**
150
* Redis-based session map implementation for distributed caching
151
*/
152
class RedisSessionMap extends SessionMap {
153
RedisSessionMap(Tracer tracer, RedisClient redisClient);
154
155
/** Create from configuration with Redis settings */
156
static SessionMap create(Config config);
157
158
boolean isReady();
159
boolean add(Session session);
160
Session get(SessionId id);
161
void remove(SessionId id);
162
Set<Session> getSessions();
163
164
/** Set session expiration time */
165
void setSessionExpiration(SessionId id, Duration ttl);
166
}
167
```
168
169
**Configuration Example:**
170
171
```toml
172
[sessionmap]
173
implementation = "org.openqa.selenium.grid.sessionmap.redis.RedisSessionMap"
174
175
[sessionmap.redis]
176
host = "redis.example.com"
177
port = 6379
178
password = "redis_password"
179
database = 0
180
connection-pool-size = 20
181
```
182
183
### Configuration Options
184
185
SessionMap-specific configuration settings.
186
187
```java { .api }
188
/**
189
* Configuration options for session map behavior
190
*/
191
class SessionMapOptions {
192
static final String SESSION_MAP_SECTION = "sessionmap";
193
194
/** Get session map implementation class */
195
String getSessionMapImplementation(Config config);
196
197
/** Get session cleanup interval */
198
Duration getCleanupInterval(Config config);
199
200
/** Get maximum session age before cleanup */
201
Duration getMaxSessionAge(Config config);
202
203
/** Get database connection settings for JDBC implementation */
204
JdbcConfig getJdbcConfig(Config config);
205
206
/** Get Redis connection settings for Redis implementation */
207
RedisConfig getRedisConfig(Config config);
208
}
209
```
210
211
## Session Lifecycle Management
212
213
### Adding Sessions
214
215
```java
216
// Session created by node, added to map by distributor
217
Session newSession = new Session(
218
sessionId,
219
nodeUri,
220
requestedCapabilities,
221
actualCapabilities,
222
Instant.now()
223
);
224
225
boolean success = sessionMap.add(newSession);
226
if (!success) {
227
// Handle duplicate session ID or storage failure
228
throw new GridException("Failed to store session: " + sessionId);
229
}
230
```
231
232
### Session Lookup for Request Routing
233
234
```java
235
// Router uses session map to find session location
236
public HttpResponse routeWebDriverCommand(HttpRequest request) {
237
SessionId sessionId = extractSessionId(request.getUri());
238
239
Session session = sessionMap.get(sessionId);
240
if (session == null) {
241
return new HttpResponse()
242
.setStatus(HTTP_NOT_FOUND)
243
.setContent(asJson(Map.of("error", "Session not found: " + sessionId)));
244
}
245
246
// Proxy request to the node owning the session
247
URI nodeUri = session.getUri();
248
return httpClient.execute(nodeUri, request);
249
}
250
```
251
252
### Session Cleanup
253
254
```java
255
// Clean up session when it ends
256
public void endSession(SessionId sessionId) {
257
sessionMap.remove(sessionId);
258
259
// Optionally fire session ended event
260
eventBus.fire(new SessionClosedEvent(sessionId));
261
}
262
263
// Periodic cleanup of expired sessions (for persistent implementations)
264
@Scheduled(fixedRate = 300000) // Every 5 minutes
265
public void cleanupExpiredSessions() {
266
if (sessionMap instanceof JdbcSessionMap) {
267
((JdbcSessionMap) sessionMap).cleanupExpiredSessions(Duration.ofHours(2));
268
}
269
}
270
```
271
272
## High Availability Patterns
273
274
### Replication
275
276
```java
277
// Use multiple session maps for redundancy
278
public class ReplicatedSessionMap implements SessionMap {
279
private final List<SessionMap> replicas;
280
281
public boolean add(Session session) {
282
boolean success = true;
283
for (SessionMap replica : replicas) {
284
success &= replica.add(session);
285
}
286
return success;
287
}
288
289
public Session get(SessionId id) {
290
// Try replicas in order, return first success
291
for (SessionMap replica : replicas) {
292
try {
293
Session session = replica.get(id);
294
if (session != null) return session;
295
} catch (Exception e) {
296
// Try next replica
297
}
298
}
299
return null;
300
}
301
}
302
```
303
304
### Backup and Recovery
305
306
```java
307
// Backup sessions to persistent storage
308
@Scheduled(fixedRate = 60000) // Every minute
309
public void backupSessions() {
310
Set<Session> activeSessions = sessionMap.getSessions();
311
312
// Write to backup storage
313
try (FileWriter backup = new FileWriter("sessions-backup.json")) {
314
backup.write(gson.toJson(activeSessions));
315
}
316
}
317
318
// Restore sessions on startup
319
public void restoreSessions() {
320
try (FileReader backup = new FileReader("sessions-backup.json")) {
321
Type setType = new TypeToken<Set<Session>>(){}.getType();
322
Set<Session> sessions = gson.fromJson(backup, setType);
323
324
for (Session session : sessions) {
325
sessionMap.add(session);
326
}
327
}
328
}
329
```
330
331
## Monitoring and Metrics
332
333
```java
334
// Session map metrics
335
public class SessionMapMetrics {
336
public int getActiveSessionCount() {
337
return sessionMap.getSessions().size();
338
}
339
340
public Map<String, Integer> getSessionsByBrowser() {
341
return sessionMap.getSessions().stream()
342
.collect(Collectors.groupingBy(
343
session -> session.getCapabilities().getBrowserName(),
344
Collectors.summingInt(session -> 1)
345
));
346
}
347
348
public Duration getAverageSessionAge() {
349
Instant now = Instant.now();
350
return sessionMap.getSessions().stream()
351
.map(session -> Duration.between(session.getStartTime(), now))
352
.reduce(Duration.ZERO, Duration::plus);
353
}
354
}
355
```