Reputation: 11289
I built a TS, MongoDB Client wrapper. for some reason when I call the function that gets the connection, its callback is called twice.
There are 2 calls in total to the get() function, 1 before the export as you can see and another from a mocha test.
I am pretty new to TS and JS in general, but this seems a bit off.
import {Db, MongoClient} from "mongodb"; import {MongoConfig} from '../config/config' class DbClient { private cachedDb : Db = null; private async connectToDatabase() { console.log('=> connect to database'); let connectionString : string = "mongodb://" + MongoConfig.host + ":" + MongoConfig.port; return MongoClient.connect(connectionString) .then(db => { console.log('=> connected to database'); this.cachedDb = db.db(MongoConfig.database); return this.cachedDb; }); } public async get() { if (this.cachedDb) { console.log('=> using cached database instance'); return Promise.resolve(this.cachedDb); }else{ return this.connectToDatabase(); } } } let client = new DbClient(); client.get(); export = client;
where the console output is:
=> connect to database => connected to database => connected to database
Any particular reason this is misbehaving?
Upvotes: 0
Views: 959
Reputation: 106800
There are 2 calls in total to the get() function, 1 before the export as you can see and another from a mocha test.
I suspect the output has an additional => connect to database
. As I said in the comments: There's a "race condition" where get()
could be called multiple times before this.cachedDb
is set which would lead to multiple connections/instances of Db being created.
For example:
const a = client.get();
const b = client.get();
// then
a.then(resultA => {
b.then(resultB => {
console.log(resultA !== resultB); // true
});
});
Solution
The problem can be fixed by storing the promise as the cached value (also, no need to have the async
keyword on the methods as Randy pointed out, as there's no values being awaited in any of the methods so you can just return the promises):
import {Db, MongoClient} from "mongodb";
import {MongoConfig} from '../config/config'
class DbClient {
private cachedGet: Promise<Db> | undefined;
private connectToDatabase() {
console.log('=> connect to database');
const connectionString = `mongodb://${MongoConfig.host}:${MongoConfig.port}`;
return MongoClient.connect(connectionString);
}
get() {
if (!this.cachedGet) {
this.cachedGet = this.connectToDatabase();
// clear the cached promise on failure so that if a caller
// calls this again, it will try to reconnect
this.cachedGet.catch(() => {
this.cachedGet = undefined;
});
}
return this.cachedGet;
}
}
let client = new DbClient();
client.get();
export = client;
Note: I'm not sure about the best way of using MongoDB (I've never used it), but I suspect connections should not be so long lived as to be cached like this (or should probably only be cached for a short time and then disconnected). You'll need to investigate that though.
Upvotes: 2