Одно соединение на пользователя - PullRequest
1 голос
/ 03 мая 2019

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

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

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

Второе решение включает «пул базы данных», где каждый пул предназначен для одного пользователя.,Только при первом соединении 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 может быть динамическим при доступе к объекту базы данных?

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

1 Ответ

0 голосов
/ 03 мая 2019

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

ВНИМАНИЕ: Создание дублированного объекта базы данных для того же соединения

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

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 соединений, поскольку это создает чрезмерную нагрузку на ЦП, на котором одновременно выполняется большое количество физических соединений. Вот почему совместное использование одного пула подключений гораздо лучше масштабируется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...