(core) add missing tsconfig file that affects IDEs

Summary:
I missed committing a file that is important for editing files comfortably in the ext directory in an IDE. This diff:

 * Adds tsconfig-base-ext.json - that was the only intended change
 * Unrelated: Forces all creation of connections to the home db through a new `getOrCreateConnection` method which changes the `busy_timeout` if using Sqlite. This was an attempt to fix random "database is locked" test failures. I believe multiple connections to the home db as an sqlite file do not happen in self-hosted Grist (where there is a single node process) or in our SaaS (where the database is in postgres). It does affect Grist started using `devServerMain.ts` (where multiple processes accessing same database are started) or various test configurations when extra database connections are opened.
 * Unrelated: I added a `busy_timeout` for session storage, when it uses Sqlite. Again, I don't believe this affects self-hosted Grist or our SaaS.
 * Tweaked a `BillingDiscount` test that looked perhaps vulnerable to a stripe request stalling.

I can't be sure my tweaks actually help, since I didn't succeed in replicating the failures. Update: looks like the "locked" error can still happen :(

Test Plan: manual

Reviewers: jarek

Reviewed By: jarek

Subscribers: jarek

Differential Revision: https://phab.getgrist.com/D3450
This commit is contained in:
Paul Fitzpatrick
2022-05-27 11:40:59 -04:00
parent 6b372fa6cd
commit b9a4b2b58f
5 changed files with 50 additions and 29 deletions

View File

@@ -50,6 +50,7 @@ import {
now,
readJson
} from 'app/gen-server/sqlUtils';
import {getOrCreateConnection} from 'app/server/lib/dbUtils';
import {makeId} from 'app/server/lib/idUtils';
import * as log from 'app/server/lib/log';
import {Permit} from 'app/server/lib/Permit';
@@ -60,10 +61,8 @@ import {Request} from "express";
import {
Brackets,
Connection,
createConnection,
DatabaseType,
EntityManager,
getConnection,
SelectQueryBuilder,
WhereExpression
} from "typeorm";
@@ -345,14 +344,7 @@ export class HomeDBManager extends EventEmitter {
}
public async connect(): Promise<void> {
try {
// If multiple servers are started within the same process, we
// share the database connection. This saves locking trouble
// with Sqlite.
this._connection = getConnection();
} catch (e) {
this._connection = await createConnection();
}
this._connection = await getOrCreateConnection();
this._dbType = this._connection.driver.options.type;
}

View File

@@ -1,5 +1,6 @@
import {synchronizeProducts} from 'app/gen-server/entity/Product';
import {Connection, createConnection} from 'typeorm';
import {Mutex} from 'async-mutex';
import {Connection, createConnection, getConnection} from 'typeorm';
// Summary of migrations found in database and in code.
interface MigrationSummary {
@@ -38,11 +39,39 @@ export async function getMigrations(connection: Connection): Promise<MigrationSu
* Run any needed migrations, and make sure products are up to date.
*/
export async function updateDb(connection?: Connection) {
connection = connection || await createConnection();
connection = connection || await getOrCreateConnection();
await runMigrations(connection);
await synchronizeProducts(connection, true);
}
/**
* Get a connection to db if one exists, or create one. Serialized to
* avoid duplication.
*/
const connectionMutex = new Mutex();
export async function getOrCreateConnection(): Promise<Connection> {
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();
} catch (e) {
const connection = await createConnection();
// 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 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

View File

@@ -73,8 +73,16 @@ function createSessionStoreFactory(sessionsDB: string): () => SessionStore {
const store = new SQLiteStore({
dir: path.dirname(sessionsDB),
db: path.basename(sessionsDB), // SQLiteStore no longer appends a .db suffix.
table: 'sessions'
table: 'sessions',
});
// In testing, and monorepo's "yarn start", session is accessed from multiple
// processes, so could hit lock failures.
// connect-sqlite3 has a concurrentDb: true flag that can be set, but
// it puts the database in WAL mode, which would have implications
// for self-hosters (a second file to think about). Instead we just
// set a busy timeout.
store.db.run('PRAGMA busy_timeout = 1000');
return assignIn(store, { async close() {}});
};
}