开玩笑的猫鼬导致内存泄漏
Posted
技术标签:
【中文标题】开玩笑的猫鼬导致内存泄漏【英文标题】:jest mongoose causing memory leak 【发布时间】:2020-12-31 00:18:13 【问题描述】:2020 年 9 月 14 日更新
我有一个我编写的示例测试用例。当我运行这段代码时,测试用例通过了,但它抱怨说拆解没有正确发生并且有一个打开的连接。任何人都可以发现它是什么:
方法 1 - 内存泄漏
import Connection, createConnection from 'mongoose';
import __MONGO_URI__ from './__MONGO_URI__';
let conn: Connection | null = null;
const getConnection: (MONGO_DB_NAME: string) => Promise<Connection> = async MONGO_DB_NAME =>
if (conn == null)
conn = await createConnection(__MONGO_URI__,
dbName: MONGO_DB_NAME,
bufferCommands: false, // Disable mongoose buffering
bufferMaxEntries: 0, // and MongoDB driver buffering
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
);
return conn;
;
const MONGO_DB_NAME = 'mongo-test';
let db: Connection;
describe('mongo - connection test to ensure setup teardown', () =>
beforeAll(async done =>
db = await getConnection(MONGO_DB_NAME);
done();
);
afterAll(async done =>
if (conn)
await db.dropDatabase();
await conn.close();
done();
);
it('true = true', () =>
expect(true).toBe(true);
);
);
错误:
工作进程未能正常退出并被强制退出。这可能是由于不正确的拆卸导致测试泄漏造成的。尝试使用 --runInBand --detectOpenHandles 运行以查找泄漏。
如果我把它全部剥离出来:
方法 2 - 内存泄漏
import Connection, createConnection from 'mongoose';
import __MONGO_URI__ from './__MONGO_URI__';
let conn: Connection | null = null;
const getConnection: (MONGO_DB_NAME: string) => Promise<Connection> = async MONGO_DB_NAME =>
if (conn == null)
conn = await createConnection(__MONGO_URI__,
dbName: MONGO_DB_NAME,
bufferCommands: false,
bufferMaxEntries: 0,
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
);
return conn;
;
const MONGO_DB_NAME = 'mongo-test';
let db: Connection;
describe('mongo - connection test to ensure setup teardown', () =>
beforeAll(async () =>
db = await getConnection(MONGO_DB_NAME);
console.log('db = ', db);
);
it('true = true', () =>
expect(true).toBe(true);
);
);
我还是有同样的问题
或者甚至这样做同样的问题:
方法 3 - 内存泄漏
import Connection, createConnection from 'mongoose';
import __MONGO_URI__ from './__MONGO_URI__';
let conn: Connection | null = null;
const getConnection: (MONGO_DB_NAME: string) => Promise<Connection | null> = MONGO_DB_NAME =>
new Promise(resolve =>
if (conn == null)
conn = createConnection(__MONGO_URI__,
dbName: MONGO_DB_NAME,
bufferCommands: false,
bufferMaxEntries: 0,
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
);
conn.on('connected', () =>
console.log('connected?');
resolve(conn);
);
resolve(conn);
);
const MONGO_DB_NAME = 'mongo-test';
let db: Connection;
describe('mongo - connection test to ensure setup teardown', () =>
beforeAll(async () =>
db = await getConnection(MONGO_DB_NAME);
console.log('db = ', db);
);
it('true = true', () =>
expect(true).toBe(true);
);
);
这种方法也有同样的问题
方法 4 - 内存泄漏
import mongoose from 'mongoose';
import __MONGO_URI__ from './__MONGO_URI__';
let conn: typeof mongoose;
const getConnection: (MONGO_DB_NAME: string) => Promise<typeof mongoose> = async MONGO_DB_NAME =>
if (!conn)
conn = await mongoose.connect(__MONGO_URI__,
dbName: MONGO_DB_NAME,
// bufferCommands: false,
// bufferMaxEntries: 0,
// useNewUrlParser: true,
// useUnifiedTopology: true,
// useCreateIndex: true
);
return conn;
;
const MONGO_DB_NAME = 'mongo-test';
let db: typeof mongoose;
describe('mongo - connection test to ensure setup teardown', () =>
beforeAll(async () =>
db = await getConnection(MONGO_DB_NAME);
console.log('db = ', db);
);
it('true = true', () =>
expect(true).toBe(true);
);
);
【问题讨论】:
那么,使用--runInBand --detectOpenHandles
运行的输出是什么?
没有错误输出吗? async
和 done
是一种反模式。应该是async
一个人。如果 dropDatabase 失败并且未达到关闭状态,则可能会出现问题。
--runInBand --detectOpenHandles 实际上并没有显示任何内容并且卡住并且没有退出测试
@EstusFlask 感谢您的反馈。导致内存泄漏的不是删除数据库或关闭连接。这是连接的实际创建。 createConnection 导致内存泄漏。如果我去掉代码只是为了shi。即使我删除了异步等待。
这不是连接本身导致泄漏,而是它被打开而不是关闭的事实。 Mongoose 连接肯定可以关闭,并且它们可以毫无问题地用于 Jest 测试。方法 2-4 在隔离问题方面没有任何价值,因为它们甚至不尝试关闭连接。按照我建议的方式尝试,去掉done
,因为它有缺陷并且可以抑制错误。调试await conn.close()
真的被调用了。这是我在这里看到的唯一可能的问题。
【参考方案1】:
最终解决方案 - 感谢:@EstusFlask
getMongoConnection.ts
import Connection, createConnection from 'mongoose';
import __MONGO_URI__ from './__MONGO_URI__';
let conn: Connection | null = null;
const getConnection: (MONGO_DB_NAME: string) => Promise<Connection | null> = MONGO_DB_NAME =>
new Promise(resolve =>
if (conn == null)
conn = createConnection(__MONGO_URI__,
dbName: MONGO_DB_NAME,
bufferCommands: false,
bufferMaxEntries: 0,
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
);
conn.on('connected', () =>
resolve(conn);
);
resolve(conn);
);
export default getConnection;
mytest.test.ts
import Connection from 'mongoose';
import getConnection from './getMongoConnection';
let db: Connection | null;
const MONGO_DB_NAME = 'mongo-test';
const collectionName = 'users';
describe('mongo - connection test to ensure setup teardown', () =>
beforeAll(async () =>
db = await getConnection(MONGO_DB_NAME);
);
afterAll(async () =>
if (db)
await db.dropDatabase();
await db.close();
);
it('should insert a doc into collection', async () =>
if (db)
const users = db.collection(collectionName);
const mockUser = _id: 'some-user-id', name: 'John' ;
await users.insertOne(mockUser);
const insertedUser = await users.findOne( _id: 'some-user-id' );
expect(insertedUser).toEqual(mockUser);
);
);
【讨论】:
以上是关于开玩笑的猫鼬导致内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章