[grid] ensure the NodeRestartedEvent does not kill recently started sessions
diff --git a/java/src/org/openqa/selenium/grid/data/Session.java b/java/src/org/openqa/selenium/grid/data/Session.java
index 1cf20ec..e00a7b1 100644
--- a/java/src/org/openqa/selenium/grid/data/Session.java
+++ b/java/src/org/openqa/selenium/grid/data/Session.java
@@ -39,6 +39,7 @@
 public class Session implements Serializable {
 
   private final SessionId id;
+  private final NodeId nodeId;
   private final URI uri;
   private final Capabilities stereotype;
   private final Capabilities capabilities;
@@ -46,11 +47,13 @@ public class Session implements Serializable {
 
   public Session(
       SessionId id,
+      NodeId nodeId,
       URI uri,
       Capabilities stereotype,
       Capabilities capabilities,
       Instant startTime) {
     this.id = Require.nonNull("Session ID", id);
+    this.nodeId = Require.nonNull("Node ID", nodeId);
     this.uri = Require.nonNull("Where the session is running", uri);
     this.startTime = Require.nonNull("Start time", startTime);
 
@@ -63,6 +66,10 @@ public SessionId getId() {
     return id;
   }
 
+  public NodeId getNodeId() {
+    return nodeId;
+  }
+
   public URI getUri() {
     return uri;
   }
@@ -92,6 +99,7 @@ private Map<String, Object> toJson() {
 
   private static Session fromJson(JsonInput input) {
     SessionId id = null;
+    NodeId nodeId = null;
     URI uri = null;
     Capabilities caps = null;
     Capabilities stereotype = null;
@@ -108,6 +116,10 @@ private static Session fromJson(JsonInput input) {
           id = input.read(SessionId.class);
           break;
 
+        case "nodeId":
+          nodeId = input.read(NodeId.class);
+          break;
+
         case "start":
           start = input.read(Instant.class);
           break;
@@ -127,7 +139,7 @@ private static Session fromJson(JsonInput input) {
     }
     input.endObject();
 
-    return new Session(id, uri, stereotype, caps, start);
+    return new Session(id, nodeId, uri, stereotype, caps, start);
   }
 
   @Override
diff --git a/java/src/org/openqa/selenium/grid/distributor/GridModel.java b/java/src/org/openqa/selenium/grid/distributor/GridModel.java
index 3aa2708..fde1c38 100644
--- a/java/src/org/openqa/selenium/grid/distributor/GridModel.java
+++ b/java/src/org/openqa/selenium/grid/distributor/GridModel.java
@@ -410,6 +410,7 @@ public void reserve(NodeStatus status, Slot slot) {
             now,
             new Session(
                 RESERVED,
+                status.getNodeId(),
                 status.getExternalUri(),
                 slot.getStereotype(),
                 slot.getStereotype(),
diff --git a/java/src/org/openqa/selenium/grid/node/ActiveSession.java b/java/src/org/openqa/selenium/grid/node/ActiveSession.java
index f6476a6..7880964 100644
--- a/java/src/org/openqa/selenium/grid/node/ActiveSession.java
+++ b/java/src/org/openqa/selenium/grid/node/ActiveSession.java
@@ -20,6 +20,7 @@
 import java.net.URI;
 import java.time.Instant;
 import org.openqa.selenium.Capabilities;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.remote.Dialect;
 import org.openqa.selenium.remote.SessionId;
 import org.openqa.selenium.remote.http.HttpHandler;
@@ -28,6 +29,8 @@ public interface ActiveSession extends HttpHandler {
 
   SessionId getId();
 
+  NodeId getNodeId();
+
   Capabilities getStereotype();
 
   Capabilities getCapabilities();
diff --git a/java/src/org/openqa/selenium/grid/node/BaseActiveSession.java b/java/src/org/openqa/selenium/grid/node/BaseActiveSession.java
index 0eb826ea..c72fb7e 100644
--- a/java/src/org/openqa/selenium/grid/node/BaseActiveSession.java
+++ b/java/src/org/openqa/selenium/grid/node/BaseActiveSession.java
@@ -23,6 +23,7 @@
 import java.time.Instant;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.ImmutableCapabilities;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.internal.Require;
 import org.openqa.selenium.remote.Dialect;
@@ -36,6 +37,7 @@ public abstract class BaseActiveSession implements ActiveSession {
 
   protected BaseActiveSession(
       SessionId id,
+      NodeId nodeId,
       URL url,
       Dialect downstream,
       Dialect upstream,
@@ -52,6 +54,7 @@ protected BaseActiveSession(
     this.session =
         new Session(
             Require.nonNull("Session id", id),
+            nodeId,
             uri,
             ImmutableCapabilities.copyOf(Require.nonNull("Stereotype", stereotype)),
             ImmutableCapabilities.copyOf(Require.nonNull("Capabilities", capabilities)),
@@ -67,6 +70,11 @@ public SessionId getId() {
   }
 
   @Override
+  public NodeId getNodeId() {
+    return session.getNodeId();
+  }
+
+  @Override
   public Capabilities getStereotype() {
     return session.getStereotype();
   }
diff --git a/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java b/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java
index 94fc774..e204294 100644
--- a/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java
+++ b/java/src/org/openqa/selenium/grid/node/DefaultActiveSession.java
@@ -23,6 +23,7 @@
 import java.net.URL;
 import java.time.Instant;
 import org.openqa.selenium.Capabilities;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.web.ReverseProxyHandler;
 import org.openqa.selenium.internal.Require;
 import org.openqa.selenium.remote.Dialect;
@@ -42,13 +43,14 @@ protected DefaultActiveSession(
       Tracer tracer,
       HttpClient client,
       SessionId id,
+      NodeId nodeId,
       URL url,
       Dialect downstream,
       Dialect upstream,
       Capabilities stereotype,
       Capabilities capabilities,
       Instant startTime) {
-    super(id, url, downstream, upstream, stereotype, capabilities, startTime);
+    super(id, nodeId, url, downstream, upstream, stereotype, capabilities, startTime);
 
     Require.nonNull("HTTP client", client);
 
diff --git a/java/src/org/openqa/selenium/grid/node/SessionFactory.java b/java/src/org/openqa/selenium/grid/node/SessionFactory.java
index 2d7e486..e783b25 100644
--- a/java/src/org/openqa/selenium/grid/node/SessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/SessionFactory.java
@@ -17,15 +17,16 @@
 
 package org.openqa.selenium.grid.node;
 
-import java.util.function.Function;
+import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.WebDriverException;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.internal.Either;
 
 public interface SessionFactory
-    extends Function<CreateSessionRequest, Either<WebDriverException, ActiveSession>>,
+    extends BiFunction<NodeId, CreateSessionRequest, Either<WebDriverException, ActiveSession>>,
         Predicate<Capabilities> {
 
   Capabilities getStereotype();
diff --git a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
index 5b06243..5415ffb 100644
--- a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java
@@ -45,6 +45,7 @@
 import org.openqa.selenium.WebDriverException;
 import org.openqa.selenium.devtools.CdpEndpointFinder;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.DefaultActiveSession;
 import org.openqa.selenium.grid.node.SessionFactory;
@@ -107,7 +108,8 @@ public boolean test(Capabilities capabilities) {
   }
 
   @Override
-  public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest) {
+  public Either<WebDriverException, ActiveSession> apply(
+      NodeId nodeId, CreateSessionRequest sessionRequest) {
     if (sessionRequest.getDownstreamDialects().isEmpty()) {
       return Either.left(new SessionNotCreatedException("No downstream dialects were found."));
     }
@@ -196,6 +198,7 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
                 tracer,
                 client,
                 new SessionId(response.getSessionId()),
+                nodeId,
                 service.getUrl(),
                 downstream,
                 upstream,
diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
index 3851347..7fd30d9 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
+++ b/java/src/org/openqa/selenium/grid/node/docker/DockerSession.java
@@ -27,6 +27,7 @@
 import java.util.logging.Logger;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.docker.Container;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.DefaultActiveSession;
 import org.openqa.selenium.internal.Require;
 import org.openqa.selenium.remote.Dialect;
@@ -47,6 +48,7 @@ public class DockerSession extends DefaultActiveSession {
       Tracer tracer,
       HttpClient client,
       SessionId id,
+      NodeId nodeId,
       URL url,
       Capabilities stereotype,
       Capabilities capabilities,
@@ -54,7 +56,8 @@ public class DockerSession extends DefaultActiveSession {
       Dialect upstream,
       Instant startTime,
       DockerAssetsPath assetsPath) {
-    super(tracer, client, id, url, downstream, upstream, stereotype, capabilities, startTime);
+    super(
+        tracer, client, id, nodeId, url, downstream, upstream, stereotype, capabilities, startTime);
     this.container = Require.nonNull("Container", container);
     this.videoContainer = videoContainer;
     this.assetsPath = Require.nonNull("Assets path", assetsPath);
diff --git a/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java b/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
index 3cc1d4a..42623ce 100644
--- a/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/docker/DockerSessionFactory.java
@@ -60,6 +60,7 @@
 import org.openqa.selenium.docker.Image;
 import org.openqa.selenium.docker.Port;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.SessionFactory;
 import org.openqa.selenium.internal.Either;
@@ -148,7 +149,8 @@ public boolean test(Capabilities capabilities) {
   }
 
   @Override
-  public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest) {
+  public Either<WebDriverException, ActiveSession> apply(
+      NodeId nodeId, CreateSessionRequest sessionRequest) {
     LOG.info("Starting session for " + sessionRequest.getDesiredCapabilities());
 
     int port = runningInDocker ? 4444 : PortProber.findFreePort();
@@ -262,6 +264,7 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
               tracer,
               client,
               id,
+              nodeId,
               remoteAddress,
               stereotype,
               mergedCapabilities,
diff --git a/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java b/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java
index 77cfbb4..3f8a04c 100644
--- a/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java
+++ b/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java
@@ -325,7 +325,7 @@ public Session getSession(SessionId id) throws NoSuchSessionException {
       throw new NoSuchSessionException("Unable to find session with id: " + id);
     }
 
-    return new Session(sessionId, getUri(), stereotype, capabilities, sessionStart);
+    return new Session(sessionId, getId(), getUri(), stereotype, capabilities, sessionStart);
   }
 
   @Override
@@ -404,7 +404,8 @@ public NodeStatus getStatus() {
                 Instant.EPOCH,
                 driver == null
                     ? null
-                    : new Session(sessionId, getUri(), stereotype, capabilities, Instant.now()))),
+                    : new Session(
+                        sessionId, getId(), getUri(), stereotype, capabilities, Instant.now()))),
         isDraining() ? DRAINING : UP,
         heartbeatPeriod,
         getSessionTimeout(),
diff --git a/java/src/org/openqa/selenium/grid/node/local/LocalNode.java b/java/src/org/openqa/selenium/grid/node/local/LocalNode.java
index 49d5af6..2ed803b 100644
--- a/java/src/org/openqa/selenium/grid/node/local/LocalNode.java
+++ b/java/src/org/openqa/selenium/grid/node/local/LocalNode.java
@@ -500,7 +500,8 @@ public Either<WebDriverException, CreateSessionResponse> newSession(
                 sessionRequest.getDownstreamDialects(), enhanced, sessionRequest.getMetadata());
       }
 
-      Either<WebDriverException, ActiveSession> possibleSession = slotToUse.apply(sessionRequest);
+      Either<WebDriverException, ActiveSession> possibleSession =
+          slotToUse.apply(this.getId(), sessionRequest);
 
       if (possibleSession.isRight()) {
         ActiveSession session = possibleSession.right();
@@ -931,7 +932,8 @@ private Session createExternalSession(
       toUse = new PersistentCapabilities(toUse).setCapability("se:vnc", rewrite(vncPath));
     }
 
-    return new Session(other.getId(), externalUri, other.getStereotype(), toUse, Instant.now());
+    return new Session(
+        other.getId(), this.getId(), externalUri, other.getStereotype(), toUse, Instant.now());
   }
 
   private URI rewrite(String path) {
@@ -960,6 +962,7 @@ public NodeStatus getStatus() {
                       session =
                           new Session(
                               activeSession.getId(),
+                              activeSession.getNodeId(),
                               activeSession.getUri(),
                               slot.getStereotype(),
                               activeSession.getCapabilities(),
diff --git a/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java b/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java
index 3c51b78..babc0b65 100644
--- a/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java
+++ b/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java
@@ -22,7 +22,7 @@
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Function;
+import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -36,6 +36,7 @@
 import org.openqa.selenium.WebDriverInfo;
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.SessionClosedEvent;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.SessionFactory;
@@ -49,7 +50,7 @@
 
 public class SessionSlot
     implements HttpHandler,
-        Function<CreateSessionRequest, Either<WebDriverException, ActiveSession>>,
+        BiFunction<NodeId, CreateSessionRequest, Either<WebDriverException, ActiveSession>>,
         Predicate<Capabilities> {
 
   private static final Logger LOG = Logger.getLogger(SessionSlot.class.getName());
@@ -136,7 +137,8 @@ public boolean test(Capabilities capabilities) {
   }
 
   @Override
-  public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest) {
+  public Either<WebDriverException, ActiveSession> apply(
+      NodeId nodeId, CreateSessionRequest sessionRequest) {
     if (currentSession != null) {
       return Either.left(new RetrySessionRequestException("Slot is busy. Try another slot."));
     }
@@ -148,7 +150,8 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
     }
 
     try {
-      Either<WebDriverException, ActiveSession> possibleSession = factory.apply(sessionRequest);
+      Either<WebDriverException, ActiveSession> possibleSession =
+          factory.apply(nodeId, sessionRequest);
       if (possibleSession.isRight()) {
         ActiveSession session = possibleSession.right();
         currentSession = session;
diff --git a/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java b/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java
index fc0c984..a24565f 100644
--- a/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java
+++ b/java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java
@@ -44,6 +44,7 @@
 import org.openqa.selenium.SessionNotCreatedException;
 import org.openqa.selenium.WebDriverException;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.DefaultActiveSession;
 import org.openqa.selenium.grid.node.SessionFactory;
@@ -141,7 +142,8 @@ public boolean test(Capabilities capabilities) {
   }
 
   @Override
-  public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest) {
+  public Either<WebDriverException, ActiveSession> apply(
+      NodeId nodeId, CreateSessionRequest sessionRequest) {
     Capabilities capabilities = sessionRequest.getDesiredCapabilities();
     if (!test(capabilities)) {
       return Either.left(
@@ -200,6 +202,7 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
                 tracer,
                 client,
                 new SessionId(response.getSessionId()),
+                nodeId,
                 serviceUrl,
                 downstream,
                 upstream,
diff --git a/java/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java b/java/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java
index aeab49a..29b02b9 100644
--- a/java/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java
+++ b/java/src/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMap.java
@@ -31,6 +31,7 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.time.Instant;
+import java.util.UUID;
 import java.util.logging.Logger;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.ImmutableCapabilities;
@@ -38,6 +39,7 @@
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.grid.config.Config;
 import org.openqa.selenium.grid.config.ConfigException;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeRemovedEvent;
 import org.openqa.selenium.grid.data.NodeRestartedEvent;
 import org.openqa.selenium.grid.data.Session;
@@ -60,6 +62,7 @@ public class JdbcBackedSessionMap extends SessionMap implements Closeable {
   private static final Logger LOG = Logger.getLogger(JdbcBackedSessionMap.class.getName());
   private static final String TABLE_NAME = "sessions_map";
   private static final String SESSION_ID_COL = "session_ids";
+  private static final String NODE_ID_COL = "node_id";
   private static final String SESSION_CAPS_COL = "session_caps";
   private static final String SESSION_STEREOTYPE_COL = "session_stereotype";
   private static final String SESSION_URI_COL = "session_uri";
@@ -92,7 +95,8 @@ public JdbcBackedSessionMap(Tracer tracer, Connection jdbcConnection, EventBus b
                     .forEach(this::remove)));
 
     bus.addListener(
-        NodeRestartedEvent.listener(nodeStatus -> this.removeByUri(nodeStatus.getExternalUri())));
+        NodeRestartedEvent.listener(
+            nodeStatus -> this.removeByUri(nodeStatus.getNodeId(), nodeStatus.getExternalUri())));
   }
 
   public static SessionMap create(Config config) {
@@ -145,19 +149,21 @@ public boolean add(Session session) {
       try (PreparedStatement statement =
           connection.prepareStatement(
               String.format(
-                  "insert into %1$s (%2$s, %3$s, %4$s, %5$s, %6$s) values (?, ?, ?, ?, ?)",
+                  "insert into %1$s (%2$s, %3$s, %4$s, %5$s, %6$s, %7$s) values (?, ?, ?, ?, ?, ?)",
                   TABLE_NAME,
                   SESSION_ID_COL,
+                  NODE_ID_COL,
                   SESSION_URI_COL,
                   SESSION_STEREOTYPE_COL,
                   SESSION_CAPS_COL,
                   SESSION_START_COL))) {
 
         statement.setString(1, session.getId().toString());
-        statement.setString(2, session.getUri().toString());
-        statement.setString(3, JSON.toJson(session.getStereotype()));
-        statement.setString(4, JSON.toJson(session.getCapabilities()));
-        statement.setString(5, JSON.toJson(session.getStartTime()));
+        statement.setString(2, session.getNodeId().toString());
+        statement.setString(3, session.getUri().toString());
+        statement.setString(4, JSON.toJson(session.getStereotype()));
+        statement.setString(5, JSON.toJson(session.getCapabilities()));
+        statement.setString(6, JSON.toJson(session.getStartTime()));
 
         String statementStr = statement.toString();
         span.setAttribute(DATABASE_STATEMENT, statementStr);
@@ -188,6 +194,7 @@ public Session get(SessionId id) throws NoSuchSessionException {
     Require.nonNull("Session ID", id);
 
     URI uri = null;
+    NodeId nodeId;
     Capabilities stereotype = null;
     Capabilities caps = null;
     Instant start = null;
@@ -231,6 +238,7 @@ public Session get(SessionId id) throws NoSuchSessionException {
             throw exception;
           }
 
+          nodeId = new NodeId(UUID.fromString(sessions.getString(NODE_ID_COL)));
           rawUri = sessions.getString(SESSION_URI_COL);
 
           String rawStereotype = sessions.getString(SESSION_STEREOTYPE_COL);
@@ -270,7 +278,7 @@ public Session get(SessionId id) throws NoSuchSessionException {
         }
 
         span.addEvent("Retrieved session from the database", attributeMap);
-        return new Session(id, uri, stereotype, caps, start);
+        return new Session(id, nodeId, uri, stereotype, caps, start);
       } catch (SQLException e) {
         span.setAttribute("error", true);
         span.setStatus(Status.CANCELLED);
@@ -323,17 +331,22 @@ public void remove(SessionId id) {
     }
   }
 
-  public void removeByUri(URI sessionUri) {
+  public void removeByUri(NodeId keep, URI sessionUri) {
     Require.nonNull("Session URI", sessionUri);
     try (Span span =
-        tracer.getCurrentContext().createSpan("DELETE from  sessions_map where session_uri = ?")) {
+        tracer
+            .getCurrentContext()
+            .createSpan("DELETE from sessions_map where node_id != ? and session_uri = ?")) {
       AttributeMap attributeMap = tracer.createAttributeMap();
 
       try (PreparedStatement statement =
           connection.prepareStatement(
-              String.format("delete from %1$s where %2$s = ?", TABLE_NAME, SESSION_URI_COL))) {
+              String.format(
+                  "delete from %1$s where %2$s != ? and %3$s = ?",
+                  TABLE_NAME, NODE_ID_COL, SESSION_URI_COL))) {
 
-        statement.setString(1, sessionUri.toString());
+        statement.setString(1, keep.toString());
+        statement.setString(2, sessionUri.toString());
         String statementStr = statement.toString();
         span.setAttribute(DATABASE_STATEMENT, statementStr);
         span.setAttribute(DATABASE_OPERATION, "delete");
diff --git a/java/src/org/openqa/selenium/grid/sessionmap/local/LocalSessionMap.java b/java/src/org/openqa/selenium/grid/sessionmap/local/LocalSessionMap.java
index 6e2ff03..65868fb 100644
--- a/java/src/org/openqa/selenium/grid/sessionmap/local/LocalSessionMap.java
+++ b/java/src/org/openqa/selenium/grid/sessionmap/local/LocalSessionMap.java
@@ -71,6 +71,14 @@ public LocalSessionMap(Tracer tracer, EventBus bus) {
               List<SessionId> toRemove =
                   knownSessions.entrySet().stream()
                       .filter((e) -> e.getValue().getUri().equals(nodeStatus.getExternalUri()))
+                      .filter(
+                          (e) -> {
+                            if (e.getValue().getNodeId().equals(nodeStatus.getNodeId())) {
+                              LOG.warning("BUG BUG BUG");
+                              return false;
+                            }
+                            return true;
+                          })
                       .map(Map.Entry::getKey)
                       .collect(Collectors.toList());
 
diff --git a/java/src/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMap.java b/java/src/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMap.java
index 3e3278c..b46e15a 100644
--- a/java/src/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMap.java
+++ b/java/src/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMap.java
@@ -29,12 +29,14 @@
 import java.net.URISyntaxException;
 import java.time.Instant;
 import java.util.List;
+import java.util.UUID;
 import java.util.logging.Logger;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.ImmutableCapabilities;
 import org.openqa.selenium.NoSuchSessionException;
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.grid.config.Config;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeRemovedEvent;
 import org.openqa.selenium.grid.data.NodeRestartedEvent;
 import org.openqa.selenium.grid.data.Session;
@@ -87,7 +89,7 @@ public RedisBackedSessionMap(Tracer tracer, URI serverUri, EventBus bus) {
                     .forEach(this::remove)));
 
     bus.addListener(
-        NodeRestartedEvent.listener(nodeStatus -> this.removeByUri(nodeStatus.getExternalUri())));
+        NodeRestartedEvent.listener(nodeStatus -> this.removeByUri(nodeStatus.getNodeId(), nodeStatus.getExternalUri())));
   }
 
   public static SessionMap create(Config config) {
@@ -114,6 +116,8 @@ public boolean add(Session session) {
       setCommonSpanAttributes(span);
       setCommonEventAttributes(attributeMap);
 
+      String nodeIdKey = nodeIdKey(session.getId());
+      String nodeIdValue = session.getNodeId().toString();
       String uriKey = uriKey(session.getId());
       String uriValue = session.getUri().toString();
       String stereotypeKey = stereotypeKey(session.getId());
@@ -139,6 +143,7 @@ public boolean add(Session session) {
       span.addEvent("Inserted into the database", attributeMap);
       connection.mset(
           ImmutableMap.of(
+              nodeIdKey, nodeIdValue,
               uriKey, uriValue,
               stereotypeKey, stereotypeJson,
               capabilitiesKey, capabilitiesJson,
@@ -161,6 +166,9 @@ public Session get(SessionId id) throws NoSuchSessionException {
       span.setAttribute(DATABASE_OPERATION, "GET");
       attributeMap.put(DATABASE_OPERATION, "GET");
 
+      String nodeIdKey = nodeIdKey(id);
+      NodeId nodeId = new NodeId(UUID.fromString(connection.get(nodeIdKey)));
+
       URI uri = getUri(id);
 
       attributeMap.put(REDIS_URI_KEY, uriKey(id));
@@ -196,7 +204,7 @@ public Session get(SessionId id) throws NoSuchSessionException {
       CAPABILITIES_EVENT.accept(attributeMap, caps);
 
       span.addEvent("Retrieved session from the database", attributeMap);
-      return new Session(id, uri, stereotype, caps, start);
+      return new Session(id, nodeId, uri, stereotype, caps, start);
     }
   }
 
@@ -286,7 +294,38 @@ public void remove(SessionId id) {
     }
   }
 
-  public void removeByUri(URI uri) {
+  public void removeByUri(NodeId keep, URI uri) {
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+    //TODO handle keep argument
+
     List<String> uriKeys = connection.getKeysByPattern("session:*:uri");
 
     if (uriKeys.isEmpty()) {
@@ -298,7 +337,7 @@ public void removeByUri(URI uri) {
 
     List<KeyValue<String, String>> keyValues = connection.mget(keys);
     keyValues.stream()
-        .filter(entry -> entry.getValue().equals(uri.toString()))
+      .filter(entry -> entry.getValue().equals(uri.toString()))
         .map(KeyValue::getKey)
         .map(
             key -> {
@@ -313,6 +352,12 @@ public boolean isReady() {
     return connection.isOpen();
   }
 
+  private String nodeIdKey(SessionId id) {
+    Require.nonNull("Session ID", id);
+
+    return "session:" + id + ":nodeId";
+  }
+
   private String uriKey(SessionId id) {
     Require.nonNull("Session ID", id);
 
diff --git a/java/test/org/openqa/selenium/grid/data/NodeStatusTest.java b/java/test/org/openqa/selenium/grid/data/NodeStatusTest.java
index 4926f08..0c16bad 100644
--- a/java/test/org/openqa/selenium/grid/data/NodeStatusTest.java
+++ b/java/test/org/openqa/selenium/grid/data/NodeStatusTest.java
@@ -50,6 +50,7 @@ void ensureRoundTripWorks() throws URISyntaxException {
                     Instant.EPOCH,
                     new Session(
                         new SessionId(UUID.randomUUID()),
+                        new NodeId(UUID.randomUUID()),
                         new URI("http://localhost:1235"),
                         stereotype,
                         new ImmutableCapabilities("peas", "sausages"),
diff --git a/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java b/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java
index 2ed1ab9..6c1744c 100644
--- a/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java
@@ -127,7 +127,8 @@ void shouldBeAbleToRegisterALocalNode() throws URISyntaxException {
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, sessionUri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, sessionUri, stereotype, caps, Instant.now())))
             .build();
 
     Distributor local =
@@ -159,6 +160,7 @@ void shouldBeAbleToRegisterALocalNode() throws URISyntaxException {
 
   @Test
   void shouldBeAbleToRegisterACustomNode() throws URISyntaxException {
+    NodeId nodeId = new NodeId(UUID.randomUUID());
     URI sessionUri = new URI("http://example:1234");
     Node node =
         new CustomNode(
@@ -168,7 +170,12 @@ void shouldBeAbleToRegisterACustomNode() throws URISyntaxException {
             Duration.ofSeconds(300),
             c ->
                 new Session(
-                    new SessionId(UUID.randomUUID()), sessionUri, stereotype, c, Instant.now()));
+                    new SessionId(UUID.randomUUID()),
+                    nodeId,
+                    sessionUri,
+                    stereotype,
+                    c,
+                    Instant.now()));
 
     try (LocalDistributor local =
         new LocalDistributor(
@@ -207,7 +214,8 @@ void shouldBeAbleToRegisterNodesByListeningForEvents() throws URISyntaxException
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, sessionUri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, sessionUri, stereotype, caps, Instant.now())))
             .build();
 
     try (LocalDistributor local =
@@ -247,14 +255,16 @@ void shouldKeepOnlyOneNodeWhenTwoRegistrationsHaveTheSameUriByListeningForEvents
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, sessionUri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, sessionUri, stereotype, caps, Instant.now())))
             .build();
     Node secondNode =
         LocalNode.builder(tracer, bus, externalUrl.toURI(), externalUrl.toURI(), registrationSecret)
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, sessionUri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, sessionUri, stereotype, caps, Instant.now())))
             .build();
     handler.addHandler(firstNode);
     handler.addHandler(secondNode);
@@ -298,7 +308,8 @@ void distributorShouldUpdateStateOfExistingNodeWhenNodePublishesStateChange()
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, sessionUri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, sessionUri, stereotype, caps, Instant.now())))
             .build();
 
     try (LocalDistributor local =
@@ -342,6 +353,7 @@ void distributorShouldUpdateStateOfExistingNodeWhenNodePublishesStateChange()
                       Instant.now(),
                       new Session(
                           new SessionId(UUID.randomUUID()),
+                          new NodeId(UUID.randomUUID()),
                           sessionUri,
                           CAPS,
                           CAPS,
@@ -467,6 +479,7 @@ public NodeStatus getStatus() {
           sess =
               new Session(
                   running.getId(),
+                  new NodeId(UUID.randomUUID()),
                   new URI("http://localhost:14568"),
                   CAPS,
                   running.getCapabilities(),
diff --git a/java/test/org/openqa/selenium/grid/distributor/DistributorDrainingTest.java b/java/test/org/openqa/selenium/grid/distributor/DistributorDrainingTest.java
index 1170c1e..6e2c006 100644
--- a/java/test/org/openqa/selenium/grid/distributor/DistributorDrainingTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/DistributorDrainingTest.java
@@ -62,7 +62,8 @@ void drainedNodeDoesNotShutDownIfNotEmpty() throws InterruptedException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     CountDownLatch latch = new CountDownLatch(1);
@@ -115,11 +116,13 @@ void drainedNodeShutsDownAfterSessionsFinish() throws InterruptedException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .maximumConcurrentSessions(5)
             .build();
 
@@ -192,7 +195,8 @@ void testDrainedNodeShutsDownOnceEmpty() throws InterruptedException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     CountDownLatch latch = new CountDownLatch(1);
@@ -245,7 +249,8 @@ void drainingNodeDoesNotAcceptNewSessions() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
diff --git a/java/test/org/openqa/selenium/grid/distributor/DistributorNodeAvailabilityTest.java b/java/test/org/openqa/selenium/grid/distributor/DistributorNodeAvailabilityTest.java
index 97dddc4..008f425 100644
--- a/java/test/org/openqa/selenium/grid/distributor/DistributorNodeAvailabilityTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/DistributorNodeAvailabilityTest.java
@@ -62,7 +62,8 @@ void registeringTheSameNodeMultipleTimesOnlyCountsTheFirstTime() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
@@ -106,7 +107,8 @@ void shouldBeAbleToRemoveANode() throws MalformedURLException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
@@ -159,7 +161,7 @@ void shouldIncludeHostsThatAreUpInHostList() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, uri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) -> new Session(id, nodeId, uri, stereotype, c, Instant.now())))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(DOWN, "Boo!"))
             .build();
@@ -193,7 +195,7 @@ void shouldIncludeHostsThatAreUpInHostList() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, uri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) -> new Session(id, nodeId, uri, stereotype, c, Instant.now())))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(UP, "Yay!"))
             .build();
@@ -231,7 +233,8 @@ void shouldNotRemoveNodeWhoseHealthCheckPassesBeforeThreshold() throws Interrupt
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())))
             .advanced()
             .healthCheck(
                 () -> {
@@ -294,7 +297,8 @@ void shouldReturnNodesThatWereDownToPoolOfNodesOnceTheyMarkTheirHealthCheckPasse
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(isUp.get(), "TL;DR"))
             .build();
@@ -350,7 +354,8 @@ void shouldBeAbleToAddANodeAndCreateASession() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
diff --git a/java/test/org/openqa/selenium/grid/distributor/DistributorTest.java b/java/test/org/openqa/selenium/grid/distributor/DistributorTest.java
index 1fcb2be..22b3064 100644
--- a/java/test/org/openqa/selenium/grid/distributor/DistributorTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/DistributorTest.java
@@ -93,7 +93,8 @@ void creatingASessionAddsItToTheSessionMap() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
@@ -144,7 +145,8 @@ void shouldReleaseSlotOnceSessionEnds() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
 
     local =
@@ -255,7 +257,7 @@ void attemptingToStartASessionWhichFailsMarksAsTheSlotAsAvailable() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) -> {
+                    (id, nodeId, caps) -> {
                       throw new SessionNotCreatedException("OMG");
                     }))
             .build();
@@ -291,7 +293,7 @@ private Node createBrokenNode(Capabilities stereotype) {
             stereotype,
             new TestSessionFactory(
                 stereotype,
-                (id, caps) -> {
+                (id, nodeId, caps) -> {
                   throw new SessionNotCreatedException("Surprise!");
                 }))
         .build();
diff --git a/java/test/org/openqa/selenium/grid/distributor/DistributorTestBase.java b/java/test/org/openqa/selenium/grid/distributor/DistributorTestBase.java
index c54e582..b971c66 100644
--- a/java/test/org/openqa/selenium/grid/distributor/DistributorTestBase.java
+++ b/java/test/org/openqa/selenium/grid/distributor/DistributorTestBase.java
@@ -40,6 +40,7 @@
 import org.openqa.selenium.grid.data.Availability;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeStatus;
 import org.openqa.selenium.grid.data.RequestId;
 import org.openqa.selenium.grid.data.Session;
@@ -78,6 +79,7 @@ public abstract class DistributorTestBase {
   protected LocalDistributor local;
   protected Capabilities stereotype;
   protected Capabilities caps;
+  protected NodeId nodeId;
   protected URI nodeUri;
   protected URI routableUri;
   protected LocalSessionMap sessions;
@@ -89,6 +91,7 @@ protected static <A, B> EitherAssert<A, B> assertThatEither(Either<A, B> either)
 
   @BeforeEach
   public void setUp() throws URISyntaxException {
+    nodeId = new NodeId(UUID.randomUUID());
     nodeUri = new URI("http://example:5678");
     routableUri = createUri();
     tracer = DefaultTestTracer.createTracer();
@@ -167,7 +170,9 @@ protected Node createNode(Capabilities stereotype, int count, int currentLoad) {
     URI uri = createUri();
     LocalNode.Builder builder = LocalNode.builder(tracer, bus, uri, uri, registrationSecret);
     for (int i = 0; i < count; i++) {
-      builder.add(stereotype, new TestSessionFactory((id, caps) -> new HandledSession(uri, caps)));
+      builder.add(
+          stereotype,
+          new TestSessionFactory((id, nodeId, caps) -> new HandledSession(nodeId, uri, caps)));
     }
     builder.maximumConcurrentSessions(12);
 
@@ -188,8 +193,8 @@ protected void waitForAllNodesToHaveCapacity(Distributor distributor, int nodeCo
 
   protected class HandledSession extends Session implements HttpHandler {
 
-    HandledSession(URI uri, Capabilities caps) {
-      super(new SessionId(UUID.randomUUID()), uri, stereotype, caps, Instant.now());
+    HandledSession(NodeId nodeId, URI uri, Capabilities caps) {
+      super(new SessionId(UUID.randomUUID()), nodeId, uri, stereotype, caps, Instant.now());
     }
 
     @Override
diff --git a/java/test/org/openqa/selenium/grid/distributor/HeartBeatTest.java b/java/test/org/openqa/selenium/grid/distributor/HeartBeatTest.java
index d0793eb..ff72277 100644
--- a/java/test/org/openqa/selenium/grid/distributor/HeartBeatTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/HeartBeatTest.java
@@ -44,7 +44,8 @@ void shouldStartHeartBeatOnNodeStart() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .heartbeatPeriod(Duration.ofSeconds(1))
             .build();
 
diff --git a/java/test/org/openqa/selenium/grid/distributor/SessionSchedulingTest.java b/java/test/org/openqa/selenium/grid/distributor/SessionSchedulingTest.java
index 5b4b8ad..e9561cf 100644
--- a/java/test/org/openqa/selenium/grid/distributor/SessionSchedulingTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/SessionSchedulingTest.java
@@ -203,7 +203,8 @@ void shouldNotScheduleAJobIfAllSlotsAreBeingUsed() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, nodeUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, nodeUri, stereotype, c, Instant.now())))
             .build();
     local =
         new LocalDistributor(
@@ -338,7 +339,9 @@ private Set<Node> createNodeSet(
       LocalNode.Builder builder = LocalNode.builder(tracer, bus, uri, uri, registrationSecret);
       for (Capabilities caps : capabilities) {
         builder.add(
-            caps, new TestSessionFactory((id, hostCaps) -> new HandledSession(uri, hostCaps)));
+            caps,
+            new TestSessionFactory(
+                (id, nodeId, hostCaps) -> new HandledSession(nodeId, uri, hostCaps)));
       }
       Node node = builder.build();
       handler.addHandler(node);
diff --git a/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java
index af5adb6..738fe25 100644
--- a/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java
@@ -54,6 +54,7 @@
 import org.openqa.selenium.grid.data.CreateSessionResponse;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
 import org.openqa.selenium.grid.data.DistributorStatus;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeStatus;
 import org.openqa.selenium.grid.data.RequestId;
 import org.openqa.selenium.grid.data.Session;
@@ -87,6 +88,7 @@ class LocalDistributorTest {
   private static final int newSessionThreadPoolSize = Runtime.getRuntime().availableProcessors();
   private Tracer tracer;
   private EventBus bus;
+  private NodeId nodeId;
   private URI uri;
   private Node localNode;
   private Wait<Object> wait;
@@ -97,10 +99,11 @@ public void setUp() throws URISyntaxException {
     bus = new GuavaEventBus();
 
     Capabilities caps = new ImmutableCapabilities("browserName", "cheese");
+    nodeId = new NodeId(UUID.randomUUID());
     uri = new URI("http://localhost:1234");
     localNode =
         LocalNode.builder(tracer, bus, uri, uri, registrationSecret)
-            .add(caps, new TestSessionFactory((id, c) -> new Handler(c)))
+            .add(caps, new TestSessionFactory((id, nodeId, c) -> new Handler(c)))
             .maximumConcurrentSessions(2)
             .sessionTimeout(Duration.ofSeconds(30))
             .heartbeatPeriod(Duration.ofSeconds(5))
@@ -239,8 +242,8 @@ void shouldBeAbleToAddMultipleSessionsConcurrently() throws Exception {
     Capabilities caps = new ImmutableCapabilities("browserName", "cheese");
 
     class VerifyingHandler extends Session implements HttpHandler {
-      private VerifyingHandler(SessionId id, Capabilities capabilities) {
-        super(id, uri, new ImmutableCapabilities(), capabilities, Instant.now());
+      private VerifyingHandler(SessionId id, NodeId nodeId, Capabilities capabilities) {
+        super(id, nodeId, uri, new ImmutableCapabilities(), capabilities, Instant.now());
       }
 
       @Override
@@ -430,7 +433,7 @@ void slowStartingNodesShouldNotCauseReservationsToBeSerialized() {
                 caps,
                 new TestSessionFactory(
                     caps,
-                    (id, c) -> {
+                    (id, nodeId, c) -> {
                       try {
                         Thread.sleep(delay);
                       } catch (InterruptedException e) {
@@ -492,6 +495,7 @@ private class Handler extends Session implements HttpHandler {
     private Handler(Capabilities capabilities) {
       super(
           new SessionId(UUID.randomUUID()),
+          nodeId,
           uri,
           new ImmutableCapabilities(),
           capabilities,
diff --git a/java/test/org/openqa/selenium/grid/distributor/selector/DefaultSlotSelectorTest.java b/java/test/org/openqa/selenium/grid/distributor/selector/DefaultSlotSelectorTest.java
index df3eecb..801d90e 100644
--- a/java/test/org/openqa/selenium/grid/distributor/selector/DefaultSlotSelectorTest.java
+++ b/java/test/org/openqa/selenium/grid/distributor/selector/DefaultSlotSelectorTest.java
@@ -236,7 +236,6 @@ void nodesAreOrderedByNumberOfSupportedBrowsersAndLoad() {
 
   private NodeStatus createNode(List<Capabilities> stereotypes, int count, int currentLoad) {
     NodeId nodeId = new NodeId(UUID.randomUUID());
-
     URI uri = createUri();
 
     Set<Slot> slots = new HashSet<>();
@@ -250,7 +249,12 @@ private NodeStatus createNode(List<Capabilities> stereotypes, int count, int cur
                     stereotype,
                     now,
                     new Session(
-                        new SessionId(UUID.randomUUID()), uri, stereotype, stereotype, now)));
+                        new SessionId(UUID.randomUUID()),
+                        nodeId,
+                        uri,
+                        stereotype,
+                        stereotype,
+                        now)));
           }
 
           for (int i = 0; i < count - currentLoad; i++) {
@@ -284,7 +288,8 @@ private NodeStatus createNode(String... browsers) {
         .forEach(
             browser -> {
               Capabilities caps = new ImmutableCapabilities("browserName", browser);
-              nodeBuilder.add(caps, new TestSessionFactory((id, c) -> new Handler(c)));
+              nodeBuilder.add(
+                  caps, new TestSessionFactory((id, nodeId, c) -> new Handler(nodeId, c)));
             });
 
     Node myNode = nodeBuilder.build();
@@ -299,7 +304,7 @@ private NodeStatus createNodeWithStereotypes(List<ImmutableMap> stereotypes) {
     stereotypes.forEach(
         stereotype -> {
           Capabilities caps = new ImmutableCapabilities(stereotype);
-          nodeBuilder.add(caps, new TestSessionFactory((id, c) -> new Handler(c)));
+          nodeBuilder.add(caps, new TestSessionFactory((id, nodeId, c) -> new Handler(nodeId, c)));
         });
     Node myNode = nodeBuilder.build();
     return myNode.getStatus();
@@ -314,9 +319,10 @@ private URI createUri() {
   }
 
   private class Handler extends Session implements HttpHandler {
-    private Handler(Capabilities capabilities) {
+    private Handler(NodeId nodeId, Capabilities capabilities) {
       super(
           new SessionId(UUID.randomUUID()),
+          nodeId,
           uri,
           new ImmutableCapabilities(),
           capabilities,
diff --git a/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java b/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java
index 3a442da..60a72b6 100644
--- a/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java
+++ b/java/test/org/openqa/selenium/grid/graphql/GraphqlHandlerTest.java
@@ -47,6 +47,7 @@
 import org.openqa.selenium.grid.data.CreateSessionRequest;
 import org.openqa.selenium.grid.data.CreateSessionResponse;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.RequestId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.data.SessionRequest;
@@ -268,7 +269,7 @@ public Capabilities getStereotype() {
 
                   @Override
                   public Either<WebDriverException, ActiveSession> apply(
-                      CreateSessionRequest createSessionRequest) {
+                      NodeId nodeId, CreateSessionRequest createSessionRequest) {
                     return Either.left(new SessionNotCreatedException("Factory for testing"));
                   }
 
@@ -304,9 +305,9 @@ void shouldBeAbleToGetSessionCount() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor =
@@ -353,9 +354,9 @@ void shouldBeAbleToGetSessionInfo() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor =
@@ -430,9 +431,9 @@ void shouldBeAbleToGetNodeInfoForSession() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor =
@@ -505,9 +506,9 @@ void shouldBeAbleToGetSlotInfoForSession() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor =
@@ -588,9 +589,9 @@ void shouldBeAbleToGetSessionDuration() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor =
@@ -648,9 +649,9 @@ void shouldThrowExceptionWhenSessionNotFound() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor.add(node);
@@ -687,9 +688,9 @@ void shouldThrowExceptionWhenSessionIsEmpty() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new org.openqa.selenium.grid.data.Session(
-                            id, nodeUri, stereotype, caps, Instant.now())))
+                            id, nodeId, nodeUri, stereotype, caps, Instant.now())))
             .build();
 
     distributor.add(node);
diff --git a/java/test/org/openqa/selenium/grid/node/CustomLocatorHandlerTest.java b/java/test/org/openqa/selenium/grid/node/CustomLocatorHandlerTest.java
index 2d8c349..2db3e3d 100644
--- a/java/test/org/openqa/selenium/grid/node/CustomLocatorHandlerTest.java
+++ b/java/test/org/openqa/selenium/grid/node/CustomLocatorHandlerTest.java
@@ -146,7 +146,8 @@ void shouldCallTheGivenLocatorForALocator() {
         nodeBuilder
             .add(
                 caps,
-                new TestSessionFactory((id, c) -> new Session(id, nodeUri, caps, c, Instant.now())))
+                new TestSessionFactory(
+                    (id, nodeId, c) -> new Session(id, nodeId, nodeUri, caps, c, Instant.now())))
             .build();
 
     HttpHandler handler =
diff --git a/java/test/org/openqa/selenium/grid/node/NodeTest.java b/java/test/org/openqa/selenium/grid/node/NodeTest.java
index 4ce9e7e..ef95964 100644
--- a/java/test/org/openqa/selenium/grid/node/NodeTest.java
+++ b/java/test/org/openqa/selenium/grid/node/NodeTest.java
@@ -106,6 +106,7 @@ class NodeTest {
   private Node node2;
   private ImmutableCapabilities stereotype;
   private ImmutableCapabilities caps;
+  private NodeId nodeId;
   private URI uri;
   private Secret registrationSecret;
 
@@ -128,11 +129,13 @@ public void setUp(TestInfo testInfo) throws URISyntaxException {
       caps = new ImmutableCapabilities("browserName", "chrome", "se:downloadsEnabled", true);
     }
 
+    nodeId = new NodeId(UUID.randomUUID());
     uri = new URI("http://localhost:1234");
 
     class Handler extends Session implements HttpHandler {
       private Handler(Capabilities capabilities) {
-        super(new SessionId(UUID.randomUUID()), uri, stereotype, capabilities, Instant.now());
+        super(
+            new SessionId(UUID.randomUUID()), nodeId, uri, stereotype, capabilities, Instant.now());
       }
 
       @Override
@@ -143,9 +146,9 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
 
     Builder builder =
         LocalNode.builder(tracer, bus, uri, uri, registrationSecret)
-            .add(caps, new TestSessionFactory((id, c) -> new Handler(c)))
-            .add(caps, new TestSessionFactory((id, c) -> new Handler(c)))
-            .add(caps, new TestSessionFactory((id, c) -> new Handler(c)))
+            .add(caps, new TestSessionFactory((id, nodeId, c) -> new Handler(c)))
+            .add(caps, new TestSessionFactory((id, nodeId, c) -> new Handler(c)))
+            .add(caps, new TestSessionFactory((id, nodeId, c) -> new Handler(c)))
             .maximumConcurrentSessions(2);
     if (isDownloadsTestCase) {
       builder = builder.enableManagedDownloads(true).sessionTimeout(Duration.ofSeconds(1));
@@ -218,7 +221,7 @@ public Capabilities getStereotype() {
 
                   @Override
                   public Either<WebDriverException, ActiveSession> apply(
-                      CreateSessionRequest createSessionRequest) {
+                      NodeId nodeId, CreateSessionRequest createSessionRequest) {
                     return Either.left(new SessionNotCreatedException("HelperFactory for testing"));
                   }
 
@@ -255,7 +258,7 @@ void shouldOnlyCreateAsManySessionsAsFactories() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, uri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) -> new Session(id, nodeId, uri, stereotype, c, Instant.now())))
             .build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -340,7 +343,7 @@ void willRespondToWebDriverCommandsSentToOwnedSessions() {
     class Recording extends Session implements HttpHandler {
 
       private Recording() {
-        super(new SessionId(UUID.randomUUID()), uri, stereotype, caps, Instant.now());
+        super(new SessionId(UUID.randomUUID()), nodeId, uri, stereotype, caps, Instant.now());
       }
 
       @Override
@@ -352,7 +355,7 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
 
     Node local =
         LocalNode.builder(tracer, bus, uri, uri, registrationSecret)
-            .add(caps, new TestSessionFactory((id, c) -> new Recording()))
+            .add(caps, new TestSessionFactory((id, nodeId, c) -> new Recording()))
             .build();
     Node remote =
         new RemoteNode(
@@ -419,7 +422,7 @@ void aSessionThatTimesOutWillBeStoppedAndRemovedFromTheSessionMap() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, uri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) -> new Session(id, nodeId, uri, stereotype, c, Instant.now())))
             .sessionTimeout(Duration.ofMinutes(3))
             .advanced()
             .clock(clock)
@@ -442,7 +445,7 @@ void shouldNotPropagateExceptionsWhenSessionCreationFails() {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> {
+                    (id, nodeId, c) -> {
                       throw new SessionNotCreatedException("eeek");
                     }))
             .build();
@@ -461,7 +464,8 @@ void eachSessionShouldReportTheNodesUrl() throws URISyntaxException {
             .add(
                 caps,
                 new TestSessionFactory(
-                    (id, c) -> new Session(id, sessionUri, stereotype, c, Instant.now())))
+                    (id, nodeId, c) ->
+                        new Session(id, nodeId, sessionUri, stereotype, c, Instant.now())))
             .build();
 
     Either<WebDriverException, CreateSessionResponse> response =
diff --git a/java/test/org/openqa/selenium/grid/node/config/DriverServiceSessionFactoryTest.java b/java/test/org/openqa/selenium/grid/node/config/DriverServiceSessionFactoryTest.java
index a23294a..003dc11 100644
--- a/java/test/org/openqa/selenium/grid/node/config/DriverServiceSessionFactoryTest.java
+++ b/java/test/org/openqa/selenium/grid/node/config/DriverServiceSessionFactoryTest.java
@@ -36,6 +36,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.Duration;
+import java.util.UUID;
 import java.util.function.Predicate;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -43,6 +44,7 @@
 import org.openqa.selenium.ImmutableCapabilities;
 import org.openqa.selenium.WebDriverException;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.internal.Either;
 import org.openqa.selenium.remote.Dialect;
@@ -94,6 +96,7 @@ void shouldNotInstantiateSessionIfNoDialectSpecifiedInARequest() {
 
     Either<WebDriverException, ActiveSession> session =
         factory.apply(
+            new NodeId(UUID.randomUUID()),
             new CreateSessionRequest(ImmutableSet.of(), toPayload("chrome"), ImmutableMap.of()));
 
     assertThat(session.isLeft()).isTrue();
@@ -106,6 +109,7 @@ void shouldNotInstantiateSessionIfCapabilitiesDoNotMatch() {
 
     Either<WebDriverException, ActiveSession> session =
         factory.apply(
+            new NodeId(UUID.randomUUID()),
             new CreateSessionRequest(
                 ImmutableSet.of(Dialect.W3C), toPayload("firefox"), ImmutableMap.of()));
 
@@ -121,6 +125,7 @@ void shouldNotInstantiateSessionIfBuilderCanNotBuildService() {
 
     Either<WebDriverException, ActiveSession> session =
         factory.apply(
+            new NodeId(UUID.randomUUID()),
             new CreateSessionRequest(
                 ImmutableSet.of(Dialect.W3C), toPayload("chrome"), ImmutableMap.of()));
 
@@ -143,6 +148,7 @@ void shouldNotInstantiateSessionIfRemoteEndReturnsInvalidResponse() throws IOExc
 
     Either<WebDriverException, ActiveSession> session =
         factory.apply(
+            new NodeId(UUID.randomUUID()),
             new CreateSessionRequest(
                 ImmutableSet.of(Dialect.W3C), toPayload("chrome"), ImmutableMap.of()));
 
@@ -175,6 +181,7 @@ void shouldInstantiateSessionIfEverythingIsOK() throws IOException {
 
     Either<WebDriverException, ActiveSession> session =
         factory.apply(
+            new NodeId(UUID.randomUUID()),
             new CreateSessionRequest(
                 ImmutableSet.of(Dialect.W3C), toPayload("chrome"), ImmutableMap.of()));
 
diff --git a/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java b/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java
index 69f1ba3..2ae921b 100644
--- a/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java
+++ b/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java
@@ -56,6 +56,7 @@
 import org.openqa.selenium.grid.config.TomlConfig;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.SessionFactory;
 import org.openqa.selenium.grid.node.data.YesSlotMatcher;
@@ -781,7 +782,7 @@ public Capabilities getStereotype() {
 
         @Override
         public Either<WebDriverException, ActiveSession> apply(
-            CreateSessionRequest createSessionRequest) {
+            NodeId nodeId, CreateSessionRequest createSessionRequest) {
           return Either.left(new SessionNotCreatedException("HelperFactory for testing"));
         }
 
diff --git a/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java b/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java
index 7b57bae..257d0c9 100644
--- a/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java
+++ b/java/test/org/openqa/selenium/grid/node/local/CreateSessionTest.java
@@ -70,8 +70,9 @@ void shouldAcceptAW3CPayload() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, uri, new ImmutableCapabilities(), caps, Instant.now())))
             .build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -126,8 +127,9 @@ void shouldPreferUsingTheW3CProtocol() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, uri, new ImmutableCapabilities(), caps, Instant.now())))
             .build();
 
     Either<WebDriverException, CreateSessionResponse> response =
diff --git a/java/test/org/openqa/selenium/grid/node/local/LocalNodeTest.java b/java/test/org/openqa/selenium/grid/node/local/LocalNodeTest.java
index ccd2b44..045f62b 100644
--- a/java/test/org/openqa/selenium/grid/node/local/LocalNodeTest.java
+++ b/java/test/org/openqa/selenium/grid/node/local/LocalNodeTest.java
@@ -46,6 +46,7 @@
 import org.openqa.selenium.events.local.GuavaEventBus;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
 import org.openqa.selenium.grid.data.CreateSessionResponse;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeStatus;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.data.Slot;
@@ -84,7 +85,8 @@ public void setUp() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())))
             .build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -200,8 +202,8 @@ void shouldBeAbleToCreateSessionsConcurrently() throws Exception {
     Capabilities caps = new ImmutableCapabilities("browserName", "cheese");
 
     class VerifyingHandler extends Session implements HttpHandler {
-      private VerifyingHandler(SessionId id, Capabilities capabilities) {
-        super(id, uri, new ImmutableCapabilities(), capabilities, Instant.now());
+      private VerifyingHandler(SessionId id, NodeId nodeId, Capabilities capabilities) {
+        super(id, nodeId, uri, new ImmutableCapabilities(), capabilities, Instant.now());
       }
 
       @Override
@@ -263,7 +265,7 @@ void nodeDrainsAfterSessionCountIsReached() throws URISyntaxException {
       builder.add(
           stereotype,
           new TestSessionFactory(
-              (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+              (id, nodeId, caps) -> new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     }
     LocalNode localNode = builder.build();
 
@@ -296,7 +298,8 @@ void seVncCdpUrlCapabilityWhenGridUrlWithSubPath() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     LocalNode localNode = builder.build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -331,7 +334,8 @@ void seVncCdpUrlCapabilityWhenGridUrlWithTrailingSlash() throws URISyntaxExcepti
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     LocalNode localNode = builder.build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -364,7 +368,8 @@ void responseCapsShowContainerName() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     LocalNode localNode = builder.build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -392,7 +397,8 @@ void cdpIsDisabledAndResponseCapsShowThat() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     LocalNode localNode = builder.build();
 
     Either<WebDriverException, CreateSessionResponse> response =
@@ -420,7 +426,8 @@ void bidiIsDisabledAndResponseCapsShowThat() throws URISyntaxException {
             .add(
                 stereotype,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, stereotype, caps, Instant.now())));
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, stereotype, caps, Instant.now())));
     LocalNode localNode = builder.build();
 
     Either<WebDriverException, CreateSessionResponse> response =
diff --git a/java/test/org/openqa/selenium/grid/router/EndToEndTest.java b/java/test/org/openqa/selenium/grid/router/EndToEndTest.java
index af228d1..34c24f3 100644
--- a/java/test/org/openqa/selenium/grid/router/EndToEndTest.java
+++ b/java/test/org/openqa/selenium/grid/router/EndToEndTest.java
@@ -49,6 +49,7 @@
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.grid.config.Config;
 import org.openqa.selenium.grid.config.TomlConfig;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.node.SessionFactory;
 import org.openqa.selenium.grid.router.DeploymentTypes.Deployment;
@@ -149,15 +150,17 @@ public static SessionFactory create(Config config, Capabilities stereotype) {
         throw new RuntimeException(e);
       }
 
-      return new TestSessionFactory(stereotype, (id, caps) -> new SpoofSession(serverUri, caps));
+      return new TestSessionFactory(
+          stereotype, (id, nodeId, caps) -> new SpoofSession(nodeId, serverUri, caps));
     }
   }
 
   private static class SpoofSession extends Session implements HttpHandler {
 
-    private SpoofSession(URI serverUri, Capabilities capabilities) {
+    private SpoofSession(NodeId nodeId, URI serverUri, Capabilities capabilities) {
       super(
           new SessionId(UUID.randomUUID()),
+          nodeId,
           serverUri,
           new ImmutableCapabilities(),
           capabilities,
diff --git a/java/test/org/openqa/selenium/grid/router/JmxTest.java b/java/test/org/openqa/selenium/grid/router/JmxTest.java
index 2561eee..02ae737 100644
--- a/java/test/org/openqa/selenium/grid/router/JmxTest.java
+++ b/java/test/org/openqa/selenium/grid/router/JmxTest.java
@@ -121,9 +121,14 @@ void shouldBeAbleToRegisterNode() throws URISyntaxException {
               .add(
                   CAPS,
                   new TestSessionFactory(
-                      (id, caps) ->
+                      (id, nodeId, caps) ->
                           new Session(
-                              id, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                              id,
+                              nodeId,
+                              nodeUri,
+                              new ImmutableCapabilities(),
+                              caps,
+                              Instant.now())))
               .build();
 
       assertThat(localNode).isNotNull();
@@ -269,8 +274,9 @@ void shouldBeAbleToMonitorHub() throws Exception {
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
             .build();
 
     NewSessionQueue sessionQueue =
diff --git a/java/test/org/openqa/selenium/grid/router/NewSessionCreationTest.java b/java/test/org/openqa/selenium/grid/router/NewSessionCreationTest.java
index 5ff4d19..2badc01 100644
--- a/java/test/org/openqa/selenium/grid/router/NewSessionCreationTest.java
+++ b/java/test/org/openqa/selenium/grid/router/NewSessionCreationTest.java
@@ -137,9 +137,14 @@ void ensureJsCannotCreateANewSession() throws URISyntaxException {
             .add(
                 Browser.detect().getCapabilities(),
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new Session(
-                            id, uri, Browser.detect().getCapabilities(), caps, Instant.now())))
+                            id,
+                            nodeId,
+                            uri,
+                            Browser.detect().getCapabilities(),
+                            caps,
+                            Instant.now())))
             .build();
     distributor.add(node);
 
@@ -201,12 +206,13 @@ void shouldNotRetryNewSessionRequestOnUnexpectedError() throws URISyntaxExceptio
     // Does not reach second attempt.
     TestSessionFactory sessionFactory =
         new TestSessionFactory(
-            (id, caps) -> {
+            (id, nodeId, caps) -> {
               if (count.get() == 0) {
                 count.incrementAndGet();
                 throw new SessionNotCreatedException("Expected the exception");
               } else {
-                return new Session(id, nodeUri, new ImmutableCapabilities(), caps, Instant.now());
+                return new Session(
+                    id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now());
               }
             });
 
@@ -273,8 +279,8 @@ void shouldRejectRequestForUnsupportedCaps() throws URISyntaxException {
 
     TestSessionFactory sessionFactory =
         new TestSessionFactory(
-            (id, caps) ->
-                new Session(id, nodeUri, new ImmutableCapabilities(), caps, Instant.now()));
+            (id, nodeId, caps) ->
+                new Session(id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now()));
 
     LocalNode localNode =
         LocalNode.builder(tracer, bus, nodeUri, nodeUri, registrationSecret)
diff --git a/java/test/org/openqa/selenium/grid/router/ProxyWebsocketTest.java b/java/test/org/openqa/selenium/grid/router/ProxyWebsocketTest.java
index 54ffd00..6846b53 100644
--- a/java/test/org/openqa/selenium/grid/router/ProxyWebsocketTest.java
+++ b/java/test/org/openqa/selenium/grid/router/ProxyWebsocketTest.java
@@ -51,6 +51,7 @@
 import org.openqa.selenium.events.local.GuavaEventBus;
 import org.openqa.selenium.grid.config.Config;
 import org.openqa.selenium.grid.config.MapConfig;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.server.BaseServerOptions;
 import org.openqa.selenium.grid.server.Server;
@@ -125,6 +126,7 @@ void shouldForwardTextMessageToServer(Supplier<String> values)
     sessions.add(
         new Session(
             id,
+            new NodeId(UUID.randomUUID()),
             backend.getUrl().toURI(),
             new ImmutableCapabilities(),
             new ImmutableCapabilities(),
@@ -160,6 +162,7 @@ void shouldForwardTextMessageFromServerToLocalEnd(Supplier<String> values)
     sessions.add(
         new Session(
             id,
+            new NodeId(UUID.randomUUID()),
             backend.getUrl().toURI(),
             new ImmutableCapabilities(),
             new ImmutableCapabilities(),
@@ -215,6 +218,7 @@ void shouldBeAbleToSendMessagesOverSecureWebSocket(Supplier<String> values)
     sessions.add(
         new Session(
             id,
+            new NodeId(UUID.randomUUID()),
             backend.getUrl().toURI(),
             new ImmutableCapabilities(),
             new ImmutableCapabilities(),
diff --git a/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java b/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java
index 6d75033..720c06e 100644
--- a/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java
+++ b/java/test/org/openqa/selenium/grid/router/ReverseProxyEndToEndTest.java
@@ -42,6 +42,7 @@
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.grid.config.Config;
 import org.openqa.selenium.grid.config.TomlConfig;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.node.SessionFactory;
 import org.openqa.selenium.grid.router.DeploymentTypes.Deployment;
@@ -144,15 +145,17 @@ public static SessionFactory create(Config config, Capabilities stereotype) {
       } catch (URISyntaxException e) {
         throw new RuntimeException(e);
       }
-      return new TestSessionFactory(stereotype, (id, caps) -> new SpoofSession(serverUri, caps));
+      return new TestSessionFactory(
+          stereotype, (id, nodeId, caps) -> new SpoofSession(nodeId, serverUri, caps));
     }
   }
 
   private static class SpoofSession extends Session implements HttpHandler {
 
-    private SpoofSession(URI serverUri, Capabilities capabilities) {
+    private SpoofSession(NodeId nodeId, URI serverUri, Capabilities capabilities) {
       super(
           new SessionId(UUID.randomUUID()),
+          nodeId,
           serverUri,
           new ImmutableCapabilities(),
           capabilities,
diff --git a/java/test/org/openqa/selenium/grid/router/RouterTest.java b/java/test/org/openqa/selenium/grid/router/RouterTest.java
index 352495f..5ae502c 100644
--- a/java/test/org/openqa/selenium/grid/router/RouterTest.java
+++ b/java/test/org/openqa/selenium/grid/router/RouterTest.java
@@ -200,9 +200,14 @@ void shouldListAllNodesTheDistributorIsAwareOf() throws URISyntaxException {
             .add(
                 chromeCapabilities,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new Session(
-                            id, firstNodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                            id,
+                            nodeId,
+                            firstNodeUri,
+                            new ImmutableCapabilities(),
+                            caps,
+                            Instant.now())))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(isUp.get(), "TL;DR"))
             .build();
@@ -212,9 +217,14 @@ id, firstNodeUri, new ImmutableCapabilities(), caps, Instant.now())))
             .add(
                 firefoxCapabilities,
                 new TestSessionFactory(
-                    (id, caps) ->
+                    (id, nodeId, caps) ->
                         new Session(
-                            id, secondNodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                            id,
+                            nodeId,
+                            secondNodeUri,
+                            new ImmutableCapabilities(),
+                            caps,
+                            Instant.now())))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(isUp.get(), "TL;DR"))
             .build();
@@ -249,13 +259,15 @@ void ifNodesHaveSpareSlotsButAlreadyHaveMaxSessionsGridIsNotReady() throws URISy
             .add(
                 chromeCapabilities,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, uri, new ImmutableCapabilities(), caps, Instant.now())))
             .add(
                 firefoxCapabilities,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, uri, new ImmutableCapabilities(), caps, Instant.now())))
             .maximumConcurrentSessions(1)
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(isUp.get(), "TL;DR"))
@@ -292,8 +304,8 @@ private Node getNode(
         .add(
             capabilities,
             new TestSessionFactory(
-                (id, caps) ->
-                    new Session(id, uri, new ImmutableCapabilities(), caps, Instant.now())))
+                (id, nodeId, caps) ->
+                    new Session(id, nodeId, uri, new ImmutableCapabilities(), caps, Instant.now())))
         .advanced()
         .healthCheck(() -> new HealthCheck.Result(availability.get(), "TL;DR"))
         .build();
diff --git a/java/test/org/openqa/selenium/grid/router/SessionCleanUpTest.java b/java/test/org/openqa/selenium/grid/router/SessionCleanUpTest.java
index 9944736..151f0fb 100644
--- a/java/test/org/openqa/selenium/grid/router/SessionCleanUpTest.java
+++ b/java/test/org/openqa/selenium/grid/router/SessionCleanUpTest.java
@@ -56,6 +56,7 @@
 import org.openqa.selenium.grid.data.Availability;
 import org.openqa.selenium.grid.data.CreateSessionResponse;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.NodeStatus;
 import org.openqa.selenium.grid.data.RequestId;
 import org.openqa.selenium.grid.data.Session;
@@ -272,7 +273,8 @@ void shouldRemoveSessionAfterNodeIsDown() throws URISyntaxException {
             .add(
                 capabilities,
                 new TestSessionFactory(
-                    (id, caps) -> new Session(id, uri, capabilities, caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(id, nodeId, uri, capabilities, caps, Instant.now())))
             .heartbeatPeriod(Duration.ofSeconds(500000))
             .advanced()
             .healthCheck(() -> new HealthCheck.Result(availability.get(), "TL;DR"))
@@ -385,15 +387,17 @@ public static SessionFactory create(Config config, Capabilities stereotype) {
         throw new RuntimeException(e);
       }
 
-      return new TestSessionFactory(stereotype, (id, caps) -> new SpoofSession(serverUri, caps));
+      return new TestSessionFactory(
+          stereotype, (id, nodeId, caps) -> new SpoofSession(nodeId, serverUri, caps));
     }
   }
 
   private static class SpoofSession extends Session implements HttpHandler {
 
-    private SpoofSession(URI serverUri, Capabilities capabilities) {
+    private SpoofSession(NodeId nodeId, URI serverUri, Capabilities capabilities) {
       super(
           new SessionId(UUID.randomUUID()),
+          nodeId,
           serverUri,
           new ImmutableCapabilities(),
           capabilities,
diff --git a/java/test/org/openqa/selenium/grid/router/SessionQueueGridTest.java b/java/test/org/openqa/selenium/grid/router/SessionQueueGridTest.java
index be3d745..ca6c161 100644
--- a/java/test/org/openqa/selenium/grid/router/SessionQueueGridTest.java
+++ b/java/test/org/openqa/selenium/grid/router/SessionQueueGridTest.java
@@ -120,13 +120,15 @@ public void setup() throws URISyntaxException, MalformedURLException {
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) ->
-                        new Session(id, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
+                    (id, nodeId, caps) ->
+                        new Session(
+                            id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now())))
             .maximumConcurrentSessions(5)
             .build();
     handler.addHandler(localNode);
diff --git a/java/test/org/openqa/selenium/grid/router/SessionQueueGridWithTimeoutTest.java b/java/test/org/openqa/selenium/grid/router/SessionQueueGridWithTimeoutTest.java
index cd48aef..2ebf6d1 100644
--- a/java/test/org/openqa/selenium/grid/router/SessionQueueGridWithTimeoutTest.java
+++ b/java/test/org/openqa/selenium/grid/router/SessionQueueGridWithTimeoutTest.java
@@ -118,13 +118,13 @@ public void setup() throws URISyntaxException, MalformedURLException {
             .add(
                 CAPS,
                 new TestSessionFactory(
-                    (id, caps) -> {
+                    (id, nodeId, caps) -> {
                       try {
                         Thread.sleep(6000); // simulate a session that takes long to create
                       } catch (Exception e) {
                       }
                       return new Session(
-                          id, nodeUri, new ImmutableCapabilities(), caps, Instant.now());
+                          id, nodeId, nodeUri, new ImmutableCapabilities(), caps, Instant.now());
                     }))
             .maximumConcurrentSessions(1)
             .build();
diff --git a/java/test/org/openqa/selenium/grid/sessionmap/SessionMapTest.java b/java/test/org/openqa/selenium/grid/sessionmap/SessionMapTest.java
index 18aee99..1572d14 100644
--- a/java/test/org/openqa/selenium/grid/sessionmap/SessionMapTest.java
+++ b/java/test/org/openqa/selenium/grid/sessionmap/SessionMapTest.java
@@ -33,6 +33,7 @@
 import org.openqa.selenium.NoSuchSessionException;
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.events.local.GuavaEventBus;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.data.SessionClosedEvent;
 import org.openqa.selenium.grid.sessionmap.local.LocalSessionMap;
@@ -52,6 +53,7 @@
 class SessionMapTest {
 
   private SessionId id;
+  private NodeId nodeId;
   private Session expected;
   private SessionMap local;
   private HttpClient client;
@@ -61,9 +63,11 @@ class SessionMapTest {
   @BeforeEach
   public void setUp() throws URISyntaxException {
     id = new SessionId(UUID.randomUUID());
+    nodeId = new NodeId(UUID.randomUUID());
     expected =
         new Session(
             id,
+            nodeId,
             new URI("http://localhost:1234"),
             new ImmutableCapabilities(),
             new ImmutableCapabilities(),
diff --git a/java/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java b/java/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java
index 4ba3f67..2680a94 100644
--- a/java/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java
+++ b/java/test/org/openqa/selenium/grid/sessionmap/jdbc/JdbcBackedSessionMapTest.java
@@ -36,6 +36,7 @@
 import org.openqa.selenium.NoSuchSessionException;
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.events.local.GuavaEventBus;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.sessionmap.SessionMap;
 import org.openqa.selenium.remote.SessionId;
@@ -104,6 +105,7 @@ void canCreateAJdbcBackedSessionMap() throws URISyntaxException {
     Session expected =
         new Session(
             new SessionId(UUID.randomUUID()),
+            new NodeId(UUID.randomUUID()),
             new URI("http://example.com/foo"),
             new ImmutableCapabilities("foo", "bar"),
             new ImmutableCapabilities("key", "value"),
@@ -124,6 +126,7 @@ void shouldBeAbleToRemoveSessions() throws URISyntaxException {
     Session expected =
         new Session(
             new SessionId(UUID.randomUUID()),
+            new NodeId(UUID.randomUUID()),
             new URI("http://example.com/foo"),
             new ImmutableCapabilities("foo", "bar"),
             new ImmutableCapabilities("key", "value"),
diff --git a/java/test/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMapTest.java b/java/test/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMapTest.java
index ce8ebfe..55c6b46 100644
--- a/java/test/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMapTest.java
+++ b/java/test/org/openqa/selenium/grid/sessionmap/redis/RedisBackedSessionMapTest.java
@@ -33,6 +33,7 @@
 import org.openqa.selenium.NoSuchSessionException;
 import org.openqa.selenium.events.EventBus;
 import org.openqa.selenium.events.local.GuavaEventBus;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.net.PortProber;
 import org.openqa.selenium.remote.SessionId;
@@ -80,6 +81,7 @@ void canGetTheUriOfASessionWithoutNeedingUrl() throws URISyntaxException {
     Session expected =
         new Session(
             new SessionId(UUID.randomUUID()),
+            new NodeId(UUID.randomUUID()),
             new URI("http://example.com/foo"),
             new ImmutableCapabilities(),
             new ImmutableCapabilities(),
@@ -96,6 +98,7 @@ void canCreateARedisBackedSessionMap() throws URISyntaxException {
     Session expected =
         new Session(
             new SessionId(UUID.randomUUID()),
+            new NodeId(UUID.randomUUID()),
             new URI("http://example.com/foo"),
             new ImmutableCapabilities(),
             new ImmutableCapabilities("cheese", "beyaz peynir"),
@@ -112,6 +115,7 @@ void shouldBeAbleToRemoveSessions() throws URISyntaxException {
     Session expected =
         new Session(
             new SessionId(UUID.randomUUID()),
+            new NodeId(UUID.randomUUID()),
             new URI("http://example.com/foo"),
             new ImmutableCapabilities(),
             new ImmutableCapabilities("cheese", "beyaz peynir"),
diff --git a/java/test/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueueTest.java b/java/test/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueueTest.java
index 97f85db..50517d0 100644
--- a/java/test/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueueTest.java
+++ b/java/test/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueueTest.java
@@ -66,6 +66,7 @@
 import org.openqa.selenium.SessionNotCreatedException;
 import org.openqa.selenium.grid.data.CreateSessionResponse;
 import org.openqa.selenium.grid.data.DefaultSlotMatcher;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.RequestId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.data.SessionRequest;
@@ -188,6 +189,7 @@ void testCompleteWithCreatedSession(Supplier<TestData> supplier) throws Interrup
               Session session =
                   new Session(
                       sessionId,
+                      new NodeId(UUID.randomUUID()),
                       URI.create("https://example.com"),
                       CAPS,
                       capabilities,
@@ -235,6 +237,7 @@ void testCompleteWithSessionInTimeout(Supplier<TestData> supplier) throws Interr
               Session session =
                   new Session(
                       sessionId,
+                      new NodeId(UUID.randomUUID()),
                       URI.create("https://example.com"),
                       CAPS,
                       capabilities,
@@ -280,6 +283,7 @@ void shouldBeAbleToAddToQueueAndGetValidResponse(Supplier<TestData> supplier) {
               Session session =
                   new Session(
                       sessionId,
+                      new NodeId(UUID.randomUUID()),
                       URI.create("https://example.com"),
                       CAPS,
                       capabilities,
@@ -391,6 +395,7 @@ void shouldBeAbleToAddToQueueWithTimeoutAndTimeoutResponse(Supplier<TestData> su
               Session session =
                   new Session(
                       sessionId,
+                      new NodeId(UUID.randomUUID()),
                       URI.create("https://example.com"),
                       CAPS,
                       capabilities,
@@ -553,6 +558,7 @@ void shouldBeAbleToRetryRequest(Supplier<TestData> supplier) {
                     Session session =
                         new Session(
                             sessionId,
+                            new NodeId(UUID.randomUUID()),
                             new URI("http://example.com"),
                             CAPS,
                             capabilities,
@@ -606,6 +612,7 @@ void shouldBeAbleToHandleMultipleSessionRequestsAtTheSameTime(Supplier<TestData>
                     Session session =
                         new Session(
                             sessionId,
+                            new NodeId(UUID.randomUUID()),
                             new URI("https://example.com"),
                             CAPS,
                             capabilities,
diff --git a/java/test/org/openqa/selenium/grid/testing/TestSessionFactory.java b/java/test/org/openqa/selenium/grid/testing/TestSessionFactory.java
index 60719a2..915a72b 100644
--- a/java/test/org/openqa/selenium/grid/testing/TestSessionFactory.java
+++ b/java/test/org/openqa/selenium/grid/testing/TestSessionFactory.java
@@ -26,11 +26,11 @@
 import java.net.URL;
 import java.time.Instant;
 import java.util.UUID;
-import java.util.function.BiFunction;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.ImmutableCapabilities;
 import org.openqa.selenium.WebDriverException;
 import org.openqa.selenium.grid.data.CreateSessionRequest;
+import org.openqa.selenium.grid.data.NodeId;
 import org.openqa.selenium.grid.data.Session;
 import org.openqa.selenium.grid.node.ActiveSession;
 import org.openqa.selenium.grid.node.BaseActiveSession;
@@ -44,15 +44,21 @@
 
 public class TestSessionFactory implements SessionFactory {
 
-  private final Capabilities stereotype;
-  private final BiFunction<SessionId, Capabilities, Session> sessionGenerator;
+  public interface SessionGenerator<T, U, R, N> {
+    N apply(T t, U u, R r);
+  }
 
-  public TestSessionFactory(BiFunction<SessionId, Capabilities, Session> sessionGenerator) {
+  private final Capabilities stereotype;
+  private final SessionGenerator<SessionId, NodeId, Capabilities, Session> sessionGenerator;
+
+  public TestSessionFactory(
+      SessionGenerator<SessionId, NodeId, Capabilities, Session> sessionGenerator) {
     this(new ImmutableCapabilities(), sessionGenerator);
   }
 
   public TestSessionFactory(
-      Capabilities stereotype, BiFunction<SessionId, Capabilities, Session> sessionGenerator) {
+      Capabilities stereotype,
+      SessionGenerator<SessionId, NodeId, Capabilities, Session> sessionGenerator) {
     this.stereotype = ImmutableCapabilities.copyOf(stereotype);
     this.sessionGenerator = sessionGenerator;
   }
@@ -63,9 +69,10 @@ public Capabilities getStereotype() {
   }
 
   @Override
-  public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sessionRequest) {
+  public Either<WebDriverException, ActiveSession> apply(
+      NodeId nodeId, CreateSessionRequest sessionRequest) {
     SessionId id = new SessionId(UUID.randomUUID());
-    Session session = sessionGenerator.apply(id, sessionRequest.getDesiredCapabilities());
+    Session session = sessionGenerator.apply(id, nodeId, sessionRequest.getDesiredCapabilities());
 
     URL url;
     try {
@@ -82,6 +89,7 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
     BaseActiveSession activeSession =
         new BaseActiveSession(
             session.getId(),
+            session.getNodeId(),
             url,
             downstream,
             W3C,