Одно подключение на пользователя

Я знаю, что этот вопрос уже задавался, но, похоже, нужно уточнить еще некоторые вещи. :)

База данных разработана таким образом, что каждый пользователь имеет соответствующие права на чтение документов, поэтому пул соединений должен иметь соединение с разными пользователями, что выходит за рамки концепции пула соединений. Из-за оптимизации и производительности мне нужно вызвать так называемую «подготовку пользователя», которая включает в себя настройку переменных сеанса, вычисление и кэширование значений в кеше и т. д., а затем выполнять запросы.

На данный момент у меня есть два решения. В первом решении я сначала проверяю, все ли готово для пользователя, а затем выполняю один или несколько запросов. Если он не подготовлен, мне нужно вызвать «подготовку пользователя», а затем выполнить запрос или запросы. С этим решением я сильно теряю производительность, потому что каждый раз мне приходится выполнять проверку, и поэтому я выбрал другое решение.

Второе решение включает «пул базы данных», где каждый пул предназначен для одного пользователя. Только при первом подключении useCount === 0 (не использую {direct: true}) я вызываю "подготовку пользователя" (это хранимая процедура, которая устанавливает некоторые переменные сеанса и подготавливает кеш), а затем выполняю sql-запросы.

Подготовку пользователя я выполнил в событии подключения в параметре initOptions для инициализации pgPromise. Я использовал pg-promise-demo, поэтому мне не нужно объяснять остальную часть кода.

Код для инициализации 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);

И веб-API выглядит так:

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-promise использует синглтон DatabasePool, экспортированный из dbPool.js, который похож на мой класс DBPool, но с целью дать «ПРЕДУПРЕЖДЕНИЕ: создание дубликата объекта базы данных для того же соединения». Можно ли использовать синглтон DatabasePool вместо моего синглтона dbPool?

Мне кажется, что dbContext (второй параметр в инициализации pgp) может решить мою проблему, но только в том случае, если его можно было бы передать как функцию, а не как значение или объект. Я ошибаюсь или dbContext может быть динамическим при доступе к объекту базы данных?

Интересно, есть ли третье (лучшее) решение? Или любое другое предложение.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вас беспокоит это предупреждение:

WARNING: Creating a duplicate database object for the same connection

но вы намерены поддерживать отдельный пул для каждого пользователя, вы можете указать это, указав любой уникальный параметр для соединения. Например, вы можете включить пользовательское свойство с именем пользователя:

const cn = {
    database: 'my-db',
    port: 12345,
    user: 'my-login-user',
    password: 'my-login-password'

    ....

    my_dynamic_user: 'john-doe'
}

Этого будет достаточно, чтобы библиотека увидела, что в вашем соединении есть что-то уникальное, не совпадающее с другими соединениями, и поэтому она не выдаст это предупреждение.

Это будет работать и для строк подключения.


Обратите внимание, что то, чего вы пытаетесь достичь, может работать хорошо только тогда, когда общее количество подключений значительно превышает количество пользователей. Например, если вы можете использовать до 100 подключений, до 10 пользователей. Затем вы можете выделить 10 пулов, в каждом из которых может быть до 10 подключений. В противном случае пострадает масштабируемость вашей системы, так как общее количество подключений является очень ограниченным ресурсом, обычно вы никогда не превысите 100 подключений, так как это создает чрезмерную нагрузку на ЦП, выполняя одновременно столько физических подключений. Вот почему совместное использование одного пула соединений намного лучше масштабируется.

Другие вопросы по теме