Aliheym / typeorm-transactional

A Transactional Method Decorator for TypeORM that uses Async Local Storage or cls-hooked to handle and propagate transactions between different repositories and service methods.
MIT License
201 stars 27 forks source link

`@Transactional({ connectionName })` does not switch repositories injected by NestJS #24

Closed tsugitta closed 1 year ago

tsugitta commented 1 year ago

For the test case below, START TRANSACTION and COMMIT are indeed called on dataSource2, but the inner query seems to be called on dataSource (we can tell because the loggers are different.) . I think the expected behavior is that the inner query will also be called for dataSource2.

https://github.com/tsugitta/typeorm-transactional/commit/0f6b03c5e31a366fd05b877a85ab4a84eac0155e#diff-982a749f6b7e9cc4078b9440af9f56d7e329f4d64da6e625542bd1a24a71e908R98-R99

If there is any mistake in the usage, I'd appreciate it if you could tell me.

Test log ``` npm run test tests/nest.test.ts > typeorm-transactional@0.4.1 test > npm run setup-test-db && jest --runInBand "tests/nest.test.ts" > typeorm-transactional@0.4.1 setup-test-db > npm run teardown-test-db; docker-compose -f tests/docker-compose.yaml up -d && sleep 3 > typeorm-transactional@0.4.1 teardown-test-db > docker-compose -f tests/docker-compose.yaml down --remove-orphans -v Stopping tests_postgres_1 ... done Removing tests_postgres_1 ... done Removing network tests_default Creating network "tests_default" with the default driver Creating tests_postgres_1 ... done console.log query: SELECT * FROM current_database() at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT * FROM current_schema() at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT version(); at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: START TRANSACTION at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT * FROM current_schema() at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT * FROM current_database() at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE ("table_schema" = 'public' AND "table_name" = 'posts') at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = 'public' AND "table_name" = 'typeorm_metadata' at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: CREATE TABLE "posts" ("id" SERIAL NOT NULL, "message" character varying NOT NULL, "slug" character varying, CONSTRAINT "UQ_54ddf9075260407dcfdd7248577" UNIQUE ("slug"), CONSTRAINT "PK_2829ac61eff60fcec60d7274b9e" PRIMARY KEY ("id")) at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: COMMIT at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT * FROM current_database() at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT * FROM current_schema() at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT version(); at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: START TRANSACTION at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT * FROM current_schema() at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT * FROM current_database() at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT "table_schema", "table_name" FROM "information_schema"."tables" WHERE ("table_schema" = 'public' AND "table_name" = 'posts') at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT TRUE FROM information_schema.columns WHERE table_name = 'pg_class' and column_name = 'relispartition' at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: SELECT columns.*, pg_catalog.col_description(('"' || table_catalog || '"."' || table_schema || '"."' || table_name || '"')::regclass::oid, ordinal_position) AS description, ('"' || "udt_schema" || '"."' || "udt_name" || '"')::"regtype" AS "regtype", pg_catalog.format_type("col_attr"."atttypid", "col_attr"."atttypmod") AS "format_type" FROM "information_schema"."columns" LEFT JOIN "pg_catalog"."pg_attribute" AS "col_attr" ON "col_attr"."attname" = "columns"."column_name" AND "col_attr"."attrelid" = ( SELECT "cls"."oid" FROM "pg_catalog"."pg_class" AS "cls" LEFT JOIN "pg_catalog"."pg_namespace" AS "ns" ON "ns"."oid" = "cls"."relnamespace" WHERE "cls"."relname" = "columns"."table_name" AND "ns"."nspname" = "columns"."table_schema" ) WHERE ("table_schema" = 'public' AND "table_name" = 'posts') at Function.logInfo (src/platform/PlatformTools.ts:229:17) at async Promise.all (index 0) console.log query: SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "cnst"."conname" AS "constraint_name", pg_get_constraintdef("cnst"."oid") AS "expression", CASE "cnst"."contype" WHEN 'p' THEN 'PRIMARY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' WHEN 'x' THEN 'EXCLUDE' END AS "constraint_type", "a"."attname" AS "column_name" FROM "pg_constraint" "cnst" INNER JOIN "pg_class" "t" ON "t"."oid" = "cnst"."conrelid" INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "cnst"."connamespace" LEFT JOIN "pg_attribute" "a" ON "a"."attrelid" = "cnst"."conrelid" AND "a"."attnum" = ANY ("cnst"."conkey") WHERE "t"."relkind" IN ('r', 'p') AND (("ns"."nspname" = 'public' AND "t"."relname" = 'posts')) at Function.logInfo (src/platform/PlatformTools.ts:229:17) at async Promise.all (index 1) console.log query: SELECT "ns"."nspname" AS "table_schema", "t"."relname" AS "table_name", "i"."relname" AS "constraint_name", "a"."attname" AS "column_name", CASE "ix"."indisunique" WHEN 't' THEN 'TRUE' ELSE'FALSE' END AS "is_unique", pg_get_expr("ix"."indpred", "ix"."indrelid") AS "condition", "types"."typname" AS "type_name" FROM "pg_class" "t" INNER JOIN "pg_index" "ix" ON "ix"."indrelid" = "t"."oid" INNER JOIN "pg_attribute" "a" ON "a"."attrelid" = "t"."oid" AND "a"."attnum" = ANY ("ix"."indkey") INNER JOIN "pg_namespace" "ns" ON "ns"."oid" = "t"."relnamespace" INNER JOIN "pg_class" "i" ON "i"."oid" = "ix"."indexrelid" INNER JOIN "pg_type" "types" ON "types"."oid" = "a"."atttypid" LEFT JOIN "pg_constraint" "cnst" ON "cnst"."conname" = "i"."relname" WHERE "t"."relkind" IN ('r', 'p') AND "cnst"."contype" IS NULL AND (("ns"."nspname" = 'public' AND "t"."relname" = 'posts')) at Function.logInfo (src/platform/PlatformTools.ts:229:17) at async Promise.all (index 2) console.log query: SELECT "con"."conname" AS "constraint_name", "con"."nspname" AS "table_schema", "con"."relname" AS "table_name", "att2"."attname" AS "column_name", "ns"."nspname" AS "referenced_table_schema", "cl"."relname" AS "referenced_table_name", "att"."attname" AS "referenced_column_name", "con"."confdeltype" AS "on_delete", "con"."confupdtype" AS "on_update", "con"."condeferrable" AS "deferrable", "con"."condeferred" AS "deferred" FROM ( SELECT UNNEST ("con1"."conkey") AS "parent", UNNEST ("con1"."confkey") AS "child", "con1"."confrelid", "con1"."conrelid", "con1"."conname", "con1"."contype", "ns"."nspname", "cl"."relname", "con1"."condeferrable", CASE WHEN "con1"."condeferred" THEN 'INITIALLY DEFERRED' ELSE 'INITIALLY IMMEDIATE' END as condeferred, CASE "con1"."confdeltype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confdeltype", CASE "con1"."confupdtype" WHEN 'a' THEN 'NO ACTION' WHEN 'r' THEN 'RESTRICT' WHEN 'c' THEN 'CASCADE' WHEN 'n' THEN 'SET NULL' WHEN 'd' THEN 'SET DEFAULT' END as "confupdtype" FROM "pg_class" "cl" INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" INNER JOIN "pg_constraint" "con1" ON "con1"."conrelid" = "cl"."oid" WHERE "con1"."contype" = 'f' AND (("ns"."nspname" = 'public' AND "cl"."relname" = 'posts')) ) "con" INNER JOIN "pg_attribute" "att" ON "att"."attrelid" = "con"."confrelid" AND "att"."attnum" = "con"."child" INNER JOIN "pg_class" "cl" ON "cl"."oid" = "con"."confrelid" AND "cl"."relispartition" = 'f'INNER JOIN "pg_namespace" "ns" ON "cl"."relnamespace" = "ns"."oid" INNER JOIN "pg_attribute" "att2" ON "att2"."attrelid" = "con"."conrelid" AND "att2"."attnum" = "con"."parent" at Function.logInfo (src/platform/PlatformTools.ts:229:17) at async Promise.all (index 3) console.log query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = 'public' AND "table_name" = 'typeorm_metadata' at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: COMMIT at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: TRUNCATE TABLE "posts" at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: START TRANSACTION at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: START TRANSACTION at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: INSERT INTO "posts"("message", "slug") VALUES ($1, DEFAULT) RETURNING "id" -- PARAMETERS: ["NestJS - A successful post"] at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: COMMIT at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: COMMIT at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: START TRANSACTION at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: SELECT "Post"."id" AS "Post_id", "Post"."message" AS "Post_message", "Post"."slug" AS "Post_slug" FROM "posts" "Post" WHERE ("Post"."message" = $1) LIMIT 1 -- PARAMETERS: ["NestJS - A successful post"] at Function.logInfo (src/platform/PlatformTools.ts:229:17) console.log query: COMMIT at SimpleConsoleLogger.logQuery (src/logger/SimpleConsoleLogger.ts:35:21) console.log query: TRUNCATE TABLE "posts" at Function.logInfo (src/platform/PlatformTools.ts:229:17) PASS tests/nest.test.ts Integration with Nest.js ✓ should create a post using service (44 ms) ○ skipped should fail to create a post using service ○ skipped should create new transaction for "REQUIRES_NEW" propagation ○ skipped should call transaction hooks Test Suites: 1 passed, 1 total Tests: 3 skipped, 1 passed, 4 total Snapshots: 0 total Time: 2.571 s Ran all test suites matching /tests\/nest.test.ts/i. Jest did not exit one second after the test run has completed. This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue. ```
tsugitta commented 1 year ago

I'm sorry, I misunderstood. The role of this library is to start and end transactions using decorators, and to reuse a single transaction even if it is used multiple times, not to switch the destination of internal queries (actually it is rare for the same table to exist in multiple connections and be writable).