diff --git a/src/cache/DbQueryResultCache.ts b/src/cache/DbQueryResultCache.ts index 14513e02a9..2b55163268 100644 --- a/src/cache/DbQueryResultCache.ts +++ b/src/cache/DbQueryResultCache.ts @@ -158,7 +158,11 @@ export class DbQueryResultCache implements QueryResultCache { * Stores given query result in the cache. */ async storeInCache(options: QueryResultCacheOptions, savedCache: QueryResultCacheOptions|undefined, queryRunner?: QueryRunner): Promise { - queryRunner = this.getQueryRunner(queryRunner); + const shouldCreateQueryRunner = queryRunner === undefined || queryRunner?.getReplicationMode() === "slave"; + + if (queryRunner === undefined || shouldCreateQueryRunner) { + queryRunner = this.connection.createQueryRunner("master"); + } let insertedValues: ObjectLiteral = options; if (this.connection.driver instanceof SqlServerDriver) { // todo: bad abstraction, re-implement this part, probably better if we create an entity metadata for cache table @@ -203,6 +207,10 @@ export class DbQueryResultCache implements QueryResultCache { .values(insertedValues) .execute(); } + + if (shouldCreateQueryRunner) { + await queryRunner.release(); + } } /** diff --git a/src/driver/mongodb/MongoQueryRunner.ts b/src/driver/mongodb/MongoQueryRunner.ts index 6f3dab66f3..45ce50dbeb 100644 --- a/src/driver/mongodb/MongoQueryRunner.ts +++ b/src/driver/mongodb/MongoQueryRunner.ts @@ -46,8 +46,9 @@ import {TableUnique} from "../../schema-builder/table/TableUnique"; import {Broadcaster} from "../../subscriber/Broadcaster"; import {TableCheck} from "../../schema-builder/table/TableCheck"; import {TableExclusion} from "../../schema-builder/table/TableExclusion"; -import { TypeORMError } from "../../error"; - +import {TypeORMError} from "../../error"; +import {ReplicationMode} from "../types/ReplicationMode"; + /** * Runs queries on a single MongoDB connection. */ @@ -507,6 +508,10 @@ export class MongoQueryRunner implements QueryRunner { throw new TypeORMError(`Schema update queries are not supported by MongoDB driver.`); } + getReplicationMode(): ReplicationMode { + return 'master'; + } + /** * Checks if database with the given name exist. */ diff --git a/src/query-runner/BaseQueryRunner.ts b/src/query-runner/BaseQueryRunner.ts index 37fb0f0702..8e15bee4a2 100644 --- a/src/query-runner/BaseQueryRunner.ts +++ b/src/query-runner/BaseQueryRunner.ts @@ -203,6 +203,10 @@ export abstract class BaseQueryRunner { } } + getReplicationMode(): ReplicationMode { + return this.mode; + } + // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- diff --git a/src/query-runner/QueryRunner.ts b/src/query-runner/QueryRunner.ts index e24dcfd8e8..4d07569b32 100644 --- a/src/query-runner/QueryRunner.ts +++ b/src/query-runner/QueryRunner.ts @@ -14,6 +14,7 @@ import {TableCheck} from "../schema-builder/table/TableCheck"; import {IsolationLevel} from "../driver/types/IsolationLevel"; import {TableExclusion} from "../schema-builder/table/TableExclusion"; import {QueryResult} from "./QueryResult"; +import {ReplicationMode} from "../driver/types/ReplicationMode"; /** * Runs queries on a single database connection. @@ -149,6 +150,11 @@ export interface QueryRunner { */ getViews(viewPaths?: string[]): Promise; + /** + * Returns replication mode (ex: `master` or `slave`). + */ + getReplicationMode(): ReplicationMode; + /** * Checks if a database with the given name exist. */ diff --git a/test/github-issues/5919/entities.ts b/test/github-issues/5919/entities.ts new file mode 100644 index 0000000000..9c02b767e0 --- /dev/null +++ b/test/github-issues/5919/entities.ts @@ -0,0 +1,10 @@ +import { Column, Entity, PrimaryGeneratedColumn } from "../../../src"; + +@Entity() +export class Comment { + @PrimaryGeneratedColumn() + id: number; + + @Column() + text: string; +} diff --git a/test/github-issues/5919/issue-6399.ts b/test/github-issues/5919/issue-6399.ts new file mode 100644 index 0000000000..96ca851733 --- /dev/null +++ b/test/github-issues/5919/issue-6399.ts @@ -0,0 +1,107 @@ +import { expect } from "chai"; +import Sinon from "sinon"; +import { Connection } from "../../../src"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Comment } from "./entities"; + +describe("github issues > #5919 Caching won't work with replication enabled", () => { + let connections: Connection[]; + + beforeEach(async () => { + connections = await createTestingConnections({ + entities: [Comment], + schemaCreate: true, + dropSchema: true, + cache: true, + enabledDrivers: ["postgres"], + }); + await reloadTestingDatabases(connections); + }); + afterEach(() => closeTestingConnections(connections)); + + it("should not another queryRunner for cache with a given masterQueryRunner", () => + Promise.all( + connections.map(async (connection) => { + const comment1 = new Comment(); + comment1.text = "tata"; + await connection.manager.save(comment1); + + const masterQueryRunner = connection.createQueryRunner( + "master" + ); + const createQueryRunnerSpy = Sinon.spy( + connection, + "createQueryRunner" + ); + + const results1 = await connection + .createQueryBuilder() + .from(Comment, "c") + .cache(true) + .setQueryRunner(masterQueryRunner) + .getRawMany(); + + expect(results1.length).eq(1); + + expect(createQueryRunnerSpy.notCalled); + + // add another one and ensure cache works + const comment2 = new Comment(); + comment2.text = "tata"; + await connection.manager.save(comment2); + + const results2 = await connection + .createQueryBuilder() + .from(Comment, "c") + .cache(true) + .setQueryRunner(masterQueryRunner) + .getRawMany(); + + expect(results2.length).eq(1); + }) + )); + + it("should create another queryRunner for cache with a given slaveQueryRunner", () => + Promise.all( + connections.map(async (connection) => { + const comment1 = new Comment(); + comment1.text = "tata"; + await connection.manager.save(comment1); + + const slaveQueryRunner = connection.createQueryRunner("slave"); + const createQueryRunnerSpy = Sinon.spy( + connection, + "createQueryRunner" + ); + + const results1 = await connection + .createQueryBuilder() + .from(Comment, "c") + .cache(true) + .setQueryRunner(slaveQueryRunner) + .getRawMany(); + + expect(results1.length).eq(1); + + expect(createQueryRunnerSpy.calledOnce); + + // add another one and ensure cache works + const comment2 = new Comment(); + comment2.text = "tata"; + await connection.manager.save(comment2); + + const results2 = await connection + .createQueryBuilder() + .from(Comment, "c") + .cache(true) + .setQueryRunner(slaveQueryRunner) + .getRawMany(); + + expect(results2.length).eq(1); + }) + )); +});