Closed mattfbacon closed 3 years ago
Naturally this applies to Creational/singleton.ts
.
I did see that singletons are generally frowned upon but I think it is still worth implementing them as in some cases they can be useful.
Well, what if you want only one connection to one database.
I am not sure what you mean by "not global", can you elaborate? Let's say you want to reuse the connection as much as possible, to save resources maybe. Wouldn't you want your one connection to be global?
Reusing a connection (e.g., via a connection pool) is different from using it in multiple async blocks at the same time. Consider the following function:
function actOnDb() {
db.foo();
db.bar();
}
If this function is called by two async blocks at the same time, you would want them to execute like this:
But that is not guaranteed and it could also occur as:
This can create a really obscure and hard-to-find bug.
On the other hand, by having two database connections when you are doing two things to the database at the same time, you can delegate any necessary synchronization to the database, e.g., by starting a transaction.
The transactions themselves are also good examples. Consider the following function:
function sensitiveAtomicDatabaseAction() {
db.startTransaction();
db.sensitive();
db.sensitive2();
if (db.checkSomething()) {
db.commit();
} else {
db.rollback();
}
}
I think you can see how interleaving calls to startTransaction
and rollback
/commit
can be problematic. A single database connection is not reentrant.
Some databases like SQLite only allow one concurrent write so in this case, we might as well reuse the connection and let the database handles queuing. The transaction stuff is good but not too related to the pattern and just shows how you can reuse this connection. But as you mentioned, I will add the map for different database types.
Some databases like SQLite only allow one concurrent write
This is pretty much irrelevant to the issue shown above. You would probably need a mutex (either in the programming language, or using BEGIN EXCLUSIVE
) for the database in that case, but it's not important.
Also, SQLite does allow multiple connections so that would be an easy way around this, as I was saying. In that case you would be able to make a type of "connection pool", where you could request a connection that would be guaranteed to be only for your use for some period of time.
but then it wouldn't be a singleton pattern, so I will leave it as is.
Your example of a DB connection is a classic counterexample against singletons: a DB connection is not really unique and global:
It's not unique because you can have connections to multiple different databases.
It's not global because the DB itself handles multiple connections to the same database, and trying to handle this within your singleton would be reinventing the wheel.
My suggestion is to remove this.