Я знаю, что этот вопрос уже задавался, но, похоже, нужно еще кое-что прояснить.:)
База данных разработана таким образом, чтобы у каждого пользователя были соответствующие права на чтение документов, поэтому пул соединений должен иметь соединение с разными пользователями, что не входит в концепцию пула соединений.Из-за оптимизации и производительности мне нужно вызывать так называемую «подготовку пользователя», которая включает установку переменных сеанса, вычисление и кэширование значений в кеше и т. Д., А затем после выполнения запросов.
Сейчас яесть два решения.В первом решении я сначала проверяю, что все подготовлено для пользователя, а затем выполняю один или несколько запросов.В случае, если он не подготовлен, мне нужно вызвать «подготовку пользователя», а затем выполнить запрос или запросы.С этим решением я теряю большую производительность, потому что каждый раз, когда мне приходится выполнять проверку, и поэтому я выбрал другое решение.
Второе решение включает «пул базы данных», где каждый пул предназначен для одного пользователя.,Только при первом соединении useCount === 0 (я не использую {direct: true}) я называю «подготовка пользователя» (это хранимая процедура, которая устанавливает некоторые переменные сеанса и подготавливает кэш), а затем выполняю sql-запросы.
Подготовка пользователя. Я выполнил событие connect в параметре initOptions для инициализации pgPromise.Я использовал pg-обещание-демо, поэтому мне не нужно объяснять остальную часть кода.
Код для инициализации pgp с помощью оболочки пулов баз данных выглядит так:
import * as promise from "bluebird";
import pgPromise from "pg-promise";
import { IDatabase, IMain, IOptions } from "pg-promise";
import { IExtensions, ProductsRepository, UsersRepository, Session, getUserFromJWT } from "../db/repos";
import { dbConfig } from "../server/config";
// pg-promise initialization options:
export const initOptions: IOptions<IExtensions> = {
promiseLib: promise,
async connect(client: any, dc: any, useCount: number) {
if (useCount === 0) {
try {
await client.query(pgp.as.format("select prepareUser($1)", [getUserFromJWT(session.JWT)]));
} catch(error) {
console.error(error);
}
}
},
extend(obj: IExtensions, dc: any) {
obj.users = new UsersRepository(obj);
obj.products = new ProductsRepository(obj);
}
};
type DB = IDatabase<IExtensions>&IExtensions;
const pgp: IMain = pgPromise(initOptions);
class DBPool {
private pool = new Map();
public get = (ct: any): DB => {
const checkConfig = {...dbConfig, ...ct};
const {host, port, database, user} = checkConfig;
const dbKey = JSON.stringify({host, port, database, user})
let db: DB = this.pool.get(dbKey) as DB;
if (!db) {
// const pgp: IMain = pgPromise(initOptions);
db = pgp(checkConfig) as DB;
this.pool.set(dbKey, db);
}
return db;
}
}
export const dbPool = new DBPool();
import diagnostics = require("./diagnostics");
diagnostics.init(initOptions);
И веб-интерфейс выглядит следующим образом:
GET("/api/getuser/:id", (req: Request) => {
const user = getUserFromJWT(session.JWT);
const db = dbPool.get({ user });
return db.users.findById(req.params.id);
});
Меня интересует, правильно ли создается исходный код для экземпляра pgp или его следует создавать в блоке if внутри метода get (строка закомментирована)?
Я видел, что pg-обещание использует синглтон DatabasePool, экспортированный из dbPool.js, который похож на мой класс DBPool, но с целью дать «ВНИМАНИЕ: Создание дублированного объекта базы данных для того же соединения».Можно ли использовать синглтон DatabasePool вместо моего синглтона dbPool?
Мне кажется, что dbContext (второй параметр в инициализации pgp) может решить мою проблему, но только если он может быть перенаправлен как функция, а некак значение или объект.Я ошибаюсь или dbContext может быть динамическим при доступе к объекту базы данных?
Интересно, существует ли третье (лучшее) решение?Или любое другое предложение.