Repository: Automattic/mongoose (JavaScript)

Issue:
Ready state showing disconnected when using useDb
### Prerequisites

- [x] I have written a descriptive issue title
- [x] I have searched existing issues to ensure the bug has not already been reported


### Mongoose version

8.18.1

### Node.js version

22.15.0

### MongoDB server version

8.0.13

### Typescript version (if applicable)

5.8.3

### Description

On Mongoose version 8.7 and greater I'm having an issue when using .useDb. I believe it is related to #14812 .

When a child connection is created using .useDb and `_lastHeartbeatAt` appears to be copied from the parent connection. The `serverHeartbeatSucceeded` event is not being handled for the child connection so `_lastHeartbeatAt` is not updated. So if the parent connection has had a heartbeat (`_lastHeartbeatAt != null`) before the child connection was created, the child connection returns readyState disconnected after a period of time.

This is causing some requests to hang like syncIndexes and startSession. Other requests process without issue.




### Steps to Reproduce

```
    const connection = createConnection(process.env.SHARED_MONGO_URI);
    connection.getClient().on('serverHeartbeatSucceeded', () => console.log('parent connection heartbeat ' + connection.readyState.toString()));

    // Wait for parent connection to have heartbeat
    await new Promise((resolve) => setTimeout(resolve, 15000));
    const childConnectionOne = connection.useDb('dbA');
    childConnectionOne.getClient().on('serverHeartbeatSucceeded', () => console.log('child connection heartbeat ' + childConnectionOne.readyState.toString()));
```

1. Parent connection created (`__lastHeartbeatAt == null`)
2. Parent connection has heartbeat (`_lastHeartbeatAt != null`)
3. Child connection created (`_lastHeartbeatAt != null`)
4. Both connections have heartbeat
5. Parent connection has serverHeartbeatSucceeded which updates `_lastHeartbeatAt`
6. Child connection `_lastHeartbeatAt` is NOT updated
7. Logic in #14812 beings to return `readyState` 0 after a few heartbeats



### Expected Behavior

Connections created from useDb should update their `_lastHeartbeatAt` field when there is a heartbeat so that readyState does not return the incorrect state.


Discussion:
Thanks for the detailed debugging, we put in a fix in #15640 



Patch:
diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js
index 9e9f8952f6f..e45d22b0a08 100644
--- a/lib/drivers/node-mongodb-native/connection.js
+++ b/lib/drivers/node-mongodb-native/connection.js
@@ -118,7 +118,7 @@ NativeConnection.prototype.useDb = function(name, options) {
 
   newConn.name = name;
 
-  // push onto the otherDbs stack, this is used when state changes
+  // push onto the otherDbs stack, this is used when state changes and when heartbeat is received
   if (options.noListener !== true) {
     this.otherDbs.push(newConn);
   }
@@ -501,6 +501,9 @@ function _setClient(conn, client, options, dbName) {
 
   client.on('serverHeartbeatSucceeded', () => {
     conn._lastHeartbeatAt = Date.now();
+    for (const otherDb of conn.otherDbs) {
+      otherDb._lastHeartbeatAt = conn._lastHeartbeatAt;
+    }
   });
 
   if (options.monitorCommands) {


Test patch:
diff --git a/test/connection.test.js b/test/connection.test.js
index d996b42e965..cf7d83bf210 100644
--- a/test/connection.test.js
+++ b/test/connection.test.js
@@ -870,6 +870,22 @@ describe('connections:', function() {
 
       await db.close();
     });
+
+    it('updates child dbs lastHeartbeatAt (gh-15635)', async function() {
+      const db = await mongoose.createConnection(start.uri).asPromise();
+
+      const schema = mongoose.Schema({ name: String }, { autoCreate: false, autoIndex: false });
+      const Test = db.model('Test', schema);
+      await Test.deleteMany({});
+      await Test.create({ name: 'gh-11821' });
+
+      const db2 = db.useDb(start.databases[1]);
+
+      const now = Date.now();
+      db.client.emit('serverHeartbeatSucceeded');
+      assert.ok(db._lastHeartbeatAt >= now);
+      assert.ok(db2._lastHeartbeatAt >= now);
+    });
   });
 
   describe('shouldAuthenticate()', function() {