or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-commands.mdconfiguration.mdindex.mdnode-management.mdrequest-routing.mdsecurity.mdsession-distribution.mdsession-queuing.mdsession-storage.md

session-storage.mddocs/

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

```