Как объединить соединения postgresql в nodejs с шаблоном дизайна фасада? - PullRequest
0 голосов
/ 13 октября 2018

Здравствуйте, я пишу простое веб-приложение, используя дизайн, похожий на шаблон дизайна фасада.Приложение написано на Typescript с использованием nodejs, expressjs, node-postres и inversify.Допустим, у меня есть этот простой пример

Router.ts

router.get('/test', testController.test);

TestController.ts

import { Request, Response } from 'express';
import { ITestUC } from '../usecase/TestUC';
import { di } from '../core/Di';
import { TYPES } from '../core/Types';

class TestController {

    public async test(req: Request, res: Response, next: Function) {
        const uc = di.get<ITestUC>(TYPES.ITestUC);
        await uc.run();
        res.send({ data:1 });
    }
}

export const testController = new TestController();

TestUC.ts

import "reflect-metadata";
import { injectable, interfaces } from "inversify";
import { di } from "../core/Di";
import { TYPES } from "../core/Types";

import { ITestManager1 } from "../library/Test/TestManager1";
import { ITestManager2 } from "../library/Test/TestManager2";
import { PoolClient } from "pg";
import { PostgresClient, IPostgresClient } from "../core/PostgresClient";
import { IPostgresPool } from "../core/PostgresPool";

function db(transaction: boolean) {
    return (target: any, property: string, descriptor: TypedPropertyDescriptor<() => void>) => {
        const fn = descriptor.value;
        if(!fn) return;
        descriptor.value = async function (){
            let poolClient: PoolClient,
                postgresClient: PostgresClient = new PostgresClient();

            try {
                poolClient = await di.get<IPostgresPool>(TYPES.IPostgresPool).pool.connect();
                postgresClient.set(poolClient);
                di.rebind<IPostgresClient>(TYPES.IPostgresClient).toDynamicValue((context: interfaces.Context) => { return postgresClient });

                if (transaction) postgresClient.begin();

                await fn.apply(this);

                if (transaction) postgresClient.commit();
            } catch (e) {
                if (transaction) postgresClient.rollback();
                throw e;
            } finally { 
                postgresClient.get().release();
            }
        }
    }
}

@injectable()
export class TestUC implements ITestUC {
    @db(true)
    public async run(): Promise<void> {
        const manager1 = await di.get<ITestManager1>(TYPES.ITestManager1);
        manager1.test1('m1');

        const manager2 = await di.get<ITestManager2>(TYPES.ITestManager2);
        manager2.test1('m2');
    }
}

export interface ITestUC {
    run(): Promise<void>
}

TestManager1.ts

import { injectable, inject} from "inversify";
import "reflect-metadata";
import { TYPES } from "../../core/Types";
import { ITestSql1 } from "./TestSql1";

@injectable()
export class TestManager1 implements ITestManager1 {
    @inject(TYPES.ITestSql1) private sql: ITestSql1;

    public async test1(value: string) {
        await this.sql.test1(value);
    }
}

export interface ITestManager1 {
    test1(value: string)
}

TestSql1.ts

import { injectable, inject } from "inversify";
import "reflect-metadata";
import { IPostgresClient } from "../../core/PostgresClient";
import { TYPES } from "../../core/Types";

@injectable()
export class TestSql1 implements ITestSql1{
    @inject(TYPES.IPostgresClient) db: IPostgresClient;

    public async test1(value: string) {
        const query = {
            name: 'insert-test',
            text: `
                INSERT INTO pr.test (
                    process,
                    operation,
                    key
                ) VALUES (
                    $1,
                    $2,
                    $3
                )`,
            values: [
                this.db.get()['processID'], 
                1, 
                value
            ]
        };
        await this.db.get().query(query);
    }
}

export interface ITestSql1 {
    test1(value: string)
}

PostgresClient.ts

import { PoolClient } from "pg";

export class PostgresClient implements IPostgresClient  {
    private client: PoolClient;

    get(): PoolClient {
        return this.client;
    }

    set(client: PoolClient) {
        this.client = client;
    }

    async begin() {
        await this.client.query('BEGIN');
    }

    async commit() {
        await this.client.query('COMMIT');
    }

    async rollback() {
        await this.client.query('ROLLBACK');
    }
}

export interface IPostgresClient {
    get(): PoolClient;
    set(client: PoolClient);
    commit();
    rollback();
    begin();
}

TestManager2.ts и TestSql2.ts в основном совпадают с TestManager1.ts и TestSql1.ts

Моя проблема заключается в том, что каждыйПохоже, что запрос использует только одно и то же соединение postgresql из пула (протестировано с JMeter) и сериализует все запросы API.Pool даже не создает других подключений к postgresql.Похоже, что другие запросы ожидают завершения предыдущего запроса или освобождения соединения postgresql.

Как создать одно соединение (транзакцию) для каждого запроса, используя пул node-postgres, и в то же время не блокировать другие запросы?

Этот код блокирует?Или я что-то не так понял в документации?Или просто этот дизайн не подходит для nodejs?Я действительно не сейчас и застрял на неделю.

...