-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
24ebcde
commit cf46f71
Showing
6 changed files
with
345 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
'use strict'; | ||
|
||
/* eslint-disable no-undef */ | ||
/* eslint-disable no-unused-vars */ | ||
const driverPath = "/Users/aditi.khare/Desktop/node-mongodb-native/lib"; | ||
const func = (async function ({ MongoClient, uri, expect, log, sinon, mongodb, getTimerCount }) { | ||
const dns = require('dns'); | ||
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => { | ||
throw { code: 'ENODATA' }; | ||
}); | ||
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => { | ||
const formattedUri = mongodb.HostAddress.fromString(uri.split('//')[1]); | ||
return [ | ||
{ | ||
name: formattedUri.host, | ||
port: formattedUri.port, | ||
weight: 0, | ||
priority: 0, | ||
protocol: formattedUri.host.isIPv6 ? 'IPv6' : 'IPv4' | ||
} | ||
]; | ||
}); | ||
/* sinon.stub(mongodb, 'checkParentDomainMatch').callsFake(async () => { | ||
console.log('in here!!!'); | ||
}); */ | ||
const client = new MongoClient('mongodb+srv://localhost'); | ||
await client.connect(); | ||
await client.close(); | ||
expect(getTimerCount()).to.equal(0); | ||
sinon.restore(); | ||
}); | ||
const scriptName = "srv-poller-timer"; | ||
const uri = "mongodb://localhost:27017/integration_tests?authSource=admin"; | ||
|
||
const mongodb = require(driverPath); | ||
const { MongoClient } = mongodb; | ||
const process = require('node:process'); | ||
const util = require('node:util'); | ||
const timers = require('node:timers'); | ||
const fs = require('node:fs'); | ||
const sinon = require('sinon'); | ||
const { expect } = require('chai'); | ||
const { setTimeout } = require('timers'); | ||
|
||
let originalReport; | ||
const logFile = scriptName + '.logs.txt'; | ||
const sleep = util.promisify(setTimeout); | ||
|
||
const run = func; | ||
|
||
/** | ||
* | ||
* Returns an array containing the new libuv resources created after script started. | ||
* A new resource is something that will keep the event loop running. | ||
* | ||
* In order to be counted as a new resource, a resource MUST: | ||
* - Must NOT share an address with a libuv resource that existed at the start of script | ||
* - Must be referenced. See [here](https://nodejs.org/api/timers.html#timeoutref) for more context. | ||
* | ||
* We're using the following tool to track resources: `process.report.getReport().libuv` | ||
* For more context, see documentation for [process.report.getReport()](https://nodejs.org/api/report.html), and [libuv](https://docs.libuv.org/en/v1.x/handle.html). | ||
* | ||
*/ | ||
function getNewLibuvResourceArray() { | ||
let currReport = process.report.getReport().libuv; | ||
const originalReportAddresses = originalReport.map(resource => resource.address); | ||
|
||
/** | ||
* @typedef {Object} LibuvResource | ||
* @property {string} type What is the resource type? For example, 'tcp' | 'timer' | 'udp' | 'tty'... (See more in [docs](https://docs.libuv.org/en/v1.x/handle.html)). | ||
* @property {boolean} is_referenced Is the resource keeping the JS event loop active? | ||
* | ||
* @param {LibuvResource} resource | ||
*/ | ||
function isNewLibuvResource(resource) { | ||
const serverType = ['tcp', 'udp']; | ||
return ( | ||
!originalReportAddresses.includes(resource.address) && resource.is_referenced // if a resource is unreferenced, it's not keeping the event loop open | ||
); | ||
} | ||
|
||
currReport = currReport.filter(resource => isNewLibuvResource(resource)); | ||
return currReport; | ||
} | ||
|
||
/** | ||
* Returns an object of the new resources created after script started. | ||
* | ||
* | ||
* In order to be counted as a new resource, a resource MUST either: | ||
* - Meet the criteria to be returned by our helper utility `getNewLibuvResourceArray()` | ||
* OR | ||
* - Be returned by `process.getActiveResourcesInfo() | ||
* | ||
* The reason we are using both methods to detect active resources is: | ||
* - `process.report.getReport().libuv` does not detect active requests (such as timers or file reads) accurately | ||
* - `process.getActiveResourcesInfo()` does not contain enough server information we need for our assertions | ||
* | ||
*/ | ||
function getNewResources() { | ||
return { | ||
libuvResources: getNewLibuvResourceArray(), | ||
activeResources: process.getActiveResourcesInfo() | ||
}; | ||
} | ||
|
||
/** | ||
* @returns Number of active timers in event loop | ||
*/ | ||
const getTimerCount = () => process.getActiveResourcesInfo().filter(r => r === 'Timeout').length; | ||
|
||
// A log function for debugging | ||
function log(message) { | ||
// remove outer parentheses for easier parsing | ||
const messageToLog = JSON.stringify(message) + ' \n'; | ||
fs.writeFileSync(logFile, messageToLog, { flag: 'a' }); | ||
} | ||
|
||
async function main() { | ||
originalReport = process.report.getReport().libuv; | ||
process.on('beforeExit', () => { | ||
log({ beforeExitHappened: true }); | ||
}); | ||
await run({ MongoClient, uri, log, expect, mongodb, sleep, sinon, getTimerCount }); | ||
log({ newResources: getNewResources() }); | ||
} | ||
|
||
main() | ||
.then(() => {}) | ||
.catch(e => { | ||
log({ error: { message: e.message, stack: e.stack, resources: getNewResources() } }); | ||
process.exit(1); | ||
}); | ||
|
||
setTimeout(() => { | ||
// this means something was in the event loop such that it hung for more than 10 seconds | ||
// so we kill the process | ||
log({ | ||
error: { | ||
message: 'Process timed out: resources remain in the event loop', | ||
resources: getNewResources() | ||
} | ||
}); | ||
process.exit(99); | ||
// using `unref` will ensure this setTimeout call is not a resource / does not keep the event loop running | ||
}, 10000).unref(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{"domainMatch":{"BSON":{"BSONType":{"double":1,"string":2,"object":3,"array":4,"binData":5,"undefined":6,"objectId":7,"bool":8,"date":9,"null":10,"regex":11,"dbPointer":12,"javascript":13,"symbol":14,"javascriptWithScope":15,"int":16,"timestamp":17,"long":18,"decimal":19,"minKey":-1,"maxKey":127},"EJSON":{},"onDemand":{"ByteUtils":{},"NumberUtils":{}}},"BSONType":{"double":1,"string":2,"object":3,"array":4,"binData":5,"undefined":6,"objectId":7,"bool":8,"date":9,"null":10,"regex":11,"dbPointer":12,"javascript":13,"symbol":14,"javascriptWithScope":15,"int":16,"timestamp":17,"long":18,"decimal":19,"minKey":-1,"maxKey":127},"BatchType":{"INSERT":1,"UPDATE":2,"DELETE":3},"AutoEncryptionLoggerLevel":{"FatalError":0,"Error":1,"Warning":2,"Info":3,"Trace":4},"GSSAPICanonicalizationValue":{"on":true,"off":false,"none":"none","forward":"forward","forwardAndReverse":"forwardAndReverse"},"AuthMechanism":{"MONGODB_AWS":"MONGODB-AWS","MONGODB_CR":"MONGODB-CR","MONGODB_DEFAULT":"DEFAULT","MONGODB_GSSAPI":"GSSAPI","MONGODB_PLAIN":"PLAIN","MONGODB_SCRAM_SHA1":"SCRAM-SHA-1","MONGODB_SCRAM_SHA256":"SCRAM-SHA-256","MONGODB_X509":"MONGODB-X509","MONGODB_OIDC":"MONGODB-OIDC"},"Compressor":{"none":0,"snappy":1,"zlib":2,"zstd":3},"CURSOR_FLAGS":["tailable","oplogReplay","noCursorTimeout","awaitData","exhaust","partial"],"CursorTimeoutMode":{"ITERATION":"iteration","LIFETIME":"cursorLifetime"},"MongoErrorLabel":{"RetryableWriteError":"RetryableWriteError","TransientTransactionError":"TransientTransactionError","UnknownTransactionCommitResult":"UnknownTransactionCommitResult","ResumableChangeStreamError":"ResumableChangeStreamError","HandshakeError":"HandshakeError","ResetPool":"ResetPool","PoolRequstedRetry":"PoolRequstedRetry","InterruptInUseConnections":"InterruptInUseConnections","NoWritesPerformed":"NoWritesPerformed"},"ExplainVerbosity":{"queryPlanner":"queryPlanner","queryPlannerExtended":"queryPlannerExtended","executionStats":"executionStats","allPlansExecution":"allPlansExecution"},"ServerApiVersion":{"v1":"1"},"ReturnDocument":{"BEFORE":"before","AFTER":"after"},"ProfilingLevel":{"off":"off","slowOnly":"slow_only","all":"all"},"ReadConcernLevel":{"local":"local","majority":"majority","linearizable":"linearizable","available":"available","snapshot":"snapshot"},"ReadPreferenceMode":{"primary":"primary","primaryPreferred":"primaryPreferred","secondary":"secondary","secondaryPreferred":"secondaryPreferred","nearest":"nearest"},"ServerType":{"Standalone":"Standalone","Mongos":"Mongos","PossiblePrimary":"PossiblePrimary","RSPrimary":"RSPrimary","RSSecondary":"RSSecondary","RSArbiter":"RSArbiter","RSOther":"RSOther","RSGhost":"RSGhost","Unknown":"Unknown","LoadBalancer":"LoadBalancer"},"TopologyType":{"Single":"Single","ReplicaSetNoPrimary":"ReplicaSetNoPrimary","ReplicaSetWithPrimary":"ReplicaSetWithPrimary","Sharded":"Sharded","Unknown":"Unknown","LoadBalanced":"LoadBalanced"}}} | ||
{"error":{"message":"Cannot read properties of undefined (reading 'fromString')","stack":"TypeError: Cannot read properties of undefined (reading 'fromString')\n at Object.<anonymous> (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:12:74)\n at Object.invoke (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/behavior.js:177:32)\n at Object.functionStub (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/stub.js:44:43)\n at Function.invoke (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/proxy-invoke.js:53:47)\n at bound querySrv [as resolveSrv] (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/proxy.js:275:26)\n at resolveSRVRecord (/Users/aditi.khare/Desktop/node-mongodb-native/lib/connection_string.js:43:42)\n at MongoClient._connect (/Users/aditi.khare/Desktop/node-mongodb-native/lib/mongo_client.js:200:74)\n at MongoClient.connect (/Users/aditi.khare/Desktop/node-mongodb-native/lib/mongo_client.js:164:40)\n at func (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:27:42)\n at main (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:124:9)","resources":{"libuvResources":[],"activeResources":["FSReqPromise","TTYWrap"]}}} | ||
{"error":{"message":"Cannot read properties of undefined (reading 'fromString')","stack":"TypeError: Cannot read properties of undefined (reading 'fromString')\n at Object.<anonymous> (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:12:74)\n at Object.invoke (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/behavior.js:177:32)\n at Object.functionStub (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/stub.js:44:43)\n at Function.invoke (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/proxy-invoke.js:53:47)\n at bound querySrv [as resolveSrv] (/Users/aditi.khare/Desktop/node-mongodb-native/node_modules/sinon/lib/sinon/proxy.js:275:26)\n at resolveSRVRecord (/Users/aditi.khare/Desktop/node-mongodb-native/lib/connection_string.js:43:42)\n at MongoClient._connect (/Users/aditi.khare/Desktop/node-mongodb-native/lib/mongo_client.js:200:74)\n at MongoClient.connect (/Users/aditi.khare/Desktop/node-mongodb-native/lib/mongo_client.js:164:40)\n at func (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:27:42)\n at main (/Users/aditi.khare/Desktop/node-mongodb-native/srv-poller-timer.cjs:124:9)","resources":{"libuvResources":[],"activeResources":["FSReqPromise","TTYWrap"]}}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.