2

Hello i am writing simple web application using design similar to facade design pattern. Application is written in Typescript using nodejs, expressjs, node-postres and inversify. Let say i have this simple example

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 and TestSql2.ts are basically same as TestManager1.ts and TestSql1.ts

My problem is that every request seems to use only one same postgresql connection from pool (Tested with JMeter) and serialize all api request. Pool doesn't even create other connections to postgresql. It looks like other requests waits for previous request end or postgresql connection release.

How to instantiate one connection (transaction) for every request using node-postgres pool and at the same time don't block other requests?

Is this code blocking? Or i misunderstood somthing in documentation? Or simply this design isn't suitable for nodejs? I really don't now and stuck for week.

  • If you want to spare yourself from the pain, checkout [pg-promise](https://github.com/vitaly-t/pg-promise), it does connections and transactions automatically. – vitaly-t Oct 21 '18 at 16:42

0 Answers0