(core) Fixing lock issues and reverting back to single connection.

Summary:
Removing `createNewConnection` method that was used in tests to create a
"scoped" version of `HomeDbManager`. Currently this won't work as there are
many methods (like `Users.findOne`) that are using the default (global) connection.

Additionally `HomeDBManger` had couple of bugs that were causing locks, which
manifested themselves in postgresql tests (that are not serializing transactions).
Repository methods like `Users.findOne` or `user.save()`, even when wrapped in
transaction were using a separate connection from the pool (and a separate
transaction).

Some tests in `UsersManager` are still skipped or refactored, as sinon's `fakeTimers`
doesn't work well with postgresql driver (which is using `setTimout` a lot).

Date mappings in `User` entity were fixed, they were using `SQLite` configuration only,
which caused problems with postgresql database.

Test Plan: Refactored.

Reviewers: paulfitz

Reviewed By: paulfitz

Subscribers: paulfitz

Differential Revision: https://phab.getgrist.com/D4342
This commit is contained in:
Jarosław Sadziński
2024-09-11 19:42:28 +02:00
parent 51acc9a8cc
commit 0ca70e9d43
6 changed files with 130 additions and 168 deletions

View File

@@ -55,43 +55,33 @@ export function getConnectionName() {
*/
const connectionMutex = new Mutex();
async function buildConnection(overrideConf?: Partial<DataSourceOptions>) {
const settings = getTypeORMSettings(overrideConf);
const connection = await createConnection(settings);
// When using Sqlite, set a busy timeout of 3s to tolerate a little
// interference from connections made by tests. Logging doesn't show
// any particularly slow queries, but bad luck is possible.
// This doesn't affect when Postgres is in use. It also doesn't have
// any impact when there is a single connection to the db, as is the
// case when Grist is run as a single process.
if (connection.driver.options.type === 'sqlite') {
await connection.query('PRAGMA busy_timeout = 3000');
}
return connection;
}
export async function getOrCreateConnection(): Promise<Connection> {
return connectionMutex.runExclusive(async () => {
return connectionMutex.runExclusive(async() => {
try {
// If multiple servers are started within the same process, we
// share the database connection. This saves locking trouble
// with Sqlite.
return getConnection(getConnectionName());
const connection = getConnection();
return connection;
} catch (e) {
if (!String(e).match(/ConnectionNotFoundError/)) {
throw e;
}
return buildConnection();
const connection = await createConnection(getTypeORMSettings());
// When using Sqlite, set a busy timeout of 3s to tolerate a little
// interference from connections made by tests. Logging doesn't show
// any particularly slow queries, but bad luck is possible.
// This doesn't affect when Postgres is in use. It also doesn't have
// any impact when there is a single connection to the db, as is the
// case when Grist is run as a single process.
if (connection.driver.options.type === 'sqlite') {
await connection.query('PRAGMA busy_timeout = 3000');
}
return connection;
}
});
}
export async function createNewConnection(overrideConf?: Partial<DataSourceOptions>): Promise<Connection> {
return connectionMutex.runExclusive(async () => {
return buildConnection(overrideConf);
});
}
export async function runMigrations(connection: Connection) {
// on SQLite, migrations fail if we don't temporarily disable foreign key
// constraint checking. This is because for sqlite typeorm copies each
@@ -100,9 +90,7 @@ export async function runMigrations(connection: Connection) {
// transaction, or it has no effect.
const sqlite = connection.driver.options.type === 'sqlite';
if (sqlite) { await connection.query("PRAGMA foreign_keys = OFF;"); }
await connection.transaction(async tr => {
await tr.connection.runMigrations();
});
await connection.runMigrations({ transaction: "all" });
if (sqlite) { await connection.query("PRAGMA foreign_keys = ON;"); }
}