Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/database/pool/pool.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Client, Pool, PoolClient } from "pg";6import { syncSchema } from "@cocalc/database/postgres/schema";78import {9pgdatabase as database,10pghost as host,11pguser as user,12pgssl as ssl,13} from "@cocalc/backend/data";14import { getLogger } from "@cocalc/backend/logger";15import { STATEMENT_TIMEOUT_MS } from "../consts";16import getCachedPool, { CacheTime } from "./cached";17import dbPassword from "./password";1819const L = getLogger("db:pool");2021export * from "./util";2223let pool: Pool | undefined = undefined;2425export default function getPool(cacheTime?: CacheTime): Pool {26if (cacheTime != null) {27return getCachedPool(cacheTime);28}29if (pool == null) {30L.debug(31`creating a new Pool(host:${host}, database:${database}, user:${user}, ssl:${JSON.stringify(ssl)} statement_timeout:${STATEMENT_TIMEOUT_MS}ms)`,32);33pool = new Pool({34password: dbPassword(),35user,36host,37database,38statement_timeout: STATEMENT_TIMEOUT_MS, // fixes https://github.com/sagemathinc/cocalc/issues/601439// the test suite assumes small pool, or there will be random failures sometimes (?)40max: process.env.PGDATABASE == TEST ? 2 : undefined,41ssl,42});43const end = pool.end.bind(pool);44pool.end = async () => {45pool = undefined;46end();47};48}49return pool;50}5152export async function getTransactionClient(): Promise<PoolClient> {53const client = await getPoolClient();54try {55await client.query("BEGIN");56} catch (err) {57await client.query("ROLLBACK");58client.release();59throw err;60}61return client;62}6364export async function getPoolClient(): Promise<PoolClient> {65const pool = await getPool();66return await pool.connect();67}6869export function getClient(): Client {70return new Client({ password: dbPassword(), user, host, database, ssl });71}7273// This is used for testing. It ensures the schema is loaded and74// test database is defined.7576// Call this with {reset:true} to reset the ephemeral77// database to a clean state with the schema loaded.78// You *can't* just initEphemeralDatabase({reset:true}) in the pre-amble79// of jest tests though, since all the tests are running in parallel, and80// they would mess up each other's state...81const TEST = "smc_ephemeral_testing_database";82export async function initEphemeralDatabase({83reset,84}: { reset?: boolean } = {}) {85if (database != TEST) {86throw Error(87`You can't use initEphemeralDatabase() and test using the database if the env variabe PGDATABASE is not set to ${TEST}!`,88);89}90const db = new Pool({91password: dbPassword(),92user,93host,94database: "smc",95statement_timeout: STATEMENT_TIMEOUT_MS,96ssl,97});98const { rows } = await db.query(99"SELECT COUNT(*) AS count FROM pg_catalog.pg_database WHERE datname = $1",100[TEST],101);102//await db.query(`DROP DATABASE IF EXISTS ${TEST}`);103const databaseExists = rows[0].count > 0;104if (!databaseExists) {105await db.query(`CREATE DATABASE ${TEST}`);106}107await db.end();108// sync the schema109await syncSchema();110if (databaseExists && reset) {111// Drop all data from all tables for a clean slate.112// Unfortunately, this can take a little while.113await dropAllData();114}115}116117async function dropAllData() {118const pool = getPool();119if (pool?.["options"]?.database != TEST) {120// safety check!121throw Error(122`You can't use dropAllData() if the env variabe PGDATABASE is not set to ${TEST}!`,123);124}125const client = await pool.connect();126127try {128// Get all table names129const result = await client.query(130"SELECT tablename FROM pg_tables WHERE schemaname='public'",131);132const tableNames = result.rows.map((row) => row.tablename);133await client.query(`TRUNCATE ${tableNames.join(",")}`);134} catch (err) {135throw err;136} finally {137client.release();138}139}140141142