저장을 습관화

에러 기록 - AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session. 본문

공부/node.js

에러 기록 - AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.

ctrs 2023. 11. 27. 02:36

- 증상

[Nest] 26544  - 2023. 11. 25. 오후 11:01:36   ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)...
AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.
    at AlreadyHasActiveConnectionError.TypeORMError [as constructor] (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\error\TypeORMError.ts:7:9)
    at new AlreadyHasActiveConnectionError (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\error\AlreadyHasActiveConnectionError.ts:8:9)
    at ConnectionManager.Object.<anonymous>.ConnectionManager.create (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\connection\ConnectionManager.ts:57:23)
    at C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\globals.ts:77:35
    at step (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:143:27)
    at Object.next (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:124:57)
    at C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:117:75
    at new Promise (<anonymous>)
    at Object.__awaiter (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:113:16)
    at createConnection (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\typeorm\globals.js:55:20)
[Nest] 26544  - 2023. 11. 25. 오후 11:01:39   ERROR [TypeOrmModule] Unable to connect to the database. Retrying (2)...
AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.
    at AlreadyHasActiveConnectionError.TypeORMError [as constructor] (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\error\TypeORMError.ts:7:9)
    at new AlreadyHasActiveConnectionError (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\error\AlreadyHasActiveConnectionError.ts:8:9)
    at ConnectionManager.Object.<anonymous>.ConnectionManager.create (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\connection\ConnectionManager.ts:57:23)
    at C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\src\globals.ts:77:35
    at step (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:143:27)
    at Object.next (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:124:57)
    at C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:117:75
    at new Promise (<anonymous>)
    at Object.__awaiter (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\tslib\tslib.js:113:16)
    at createConnection (C:\Users\admin\OneDrive\바탕 화면\typeorm-in-the-nest\node_modules\typeorm\globals.js:55:20)
 FAIL  test/app.e2e-spec.ts (10.952 s)
  AppController (e2e)
    √ / (GET) (845 ms)
    hello jest
      × two plus two is four (5014 ms)

  ● AppController (e2e) › hello jest › two plus two is four

    thrown: "Exceeded timeout of 5000 ms for a hook.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

       7 |   let app: INestApplication;
       8 |
    >  9 |   beforeEach(async () => {
         |   ^
      10 |     const moduleFixture: TestingModule = await Test.createTestingModule({        
      11 |       imports: [AppModule],
      12 |     }).compile();

      at app.e2e-spec.ts:9:3
      at Object.<anonymous> (app.e2e-spec.ts:6:1)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        11.106 s
Ran all test suites.
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

 

AlreadyHasActiveConnectionError: Cannot create a new connection named "default", because connection with such name already exist and it now has an active connection session.

이미 사용중인 연결 에러: "default"라는 이름의 연결이 이미 존재하며 활성화된 연결 세션이므로, 같은 이름으로 새로운 연결을 생성 할 수 없습니다.

 

 

e2e 테스트 중 발생

테스트 항목이 하나일 경우 발생하지 않으나 항목이 여러 개일 경우 발생하였음

 

 

- 해결

app.module.ts에 TypeOrmModuleOptions에 keepConnectionAlive: true 옵션을 주었다.

// ...
const typeOrmModuleOptions = {
  useFactory: async (
    configService: ConfigService,
  ): Promise<TypeOrmModuleOptions> => ({
    // ...
    keepConnectionAlive: true,
  }),
// ...

 

이 옵션은 어플리케이션이 종료되어도 DB 연결을 유지하는 설정이다.

default 값은 false이다.

 

app.e2e-spec.ts에는 기본값으로 beforEach 메소드가 있다. 내용은 아래와 같다.

beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

 

beforeEach는 이름 그대로 각 테스트 항목을 시작하기 전 실행되는 메소드인데,

그 안에는 'app.init', 어플리케이션을 초기화(시작)한다는 내용이 있다.

이 과정에서 의존성 주입 및 DB 연결이 진행된다.

 

즉 첫번째 테스트 때 어플리케이션이 시작되었고, 이때 DB가 연결되었다.

첫번째 테스트가 종료되었다. 아직 jest의 e2e describe 안에 있고, 어플리케이션을 여전히 실행 중이다.

 

두번째 테스트를 시작하면서 beforeEach의 app.init 의해 다시 한번 어플리케이션을 초기화(시작)하려 하였고

이 과정에서 DB 연결 또한 초기화하며 시도한다.

 

하지만 어플리케이션을 재초기화하는 것이지 종료한 적은 없었기에

여전히 'default'라는 이름으로 활성화된 DB 연결이 존재하였으므로

 

'AlreadyHasActiveConnectionError, "default"라는 이름의 연결이 이미 존재하며 활성화된 연결 세션이므로, 같은 이름으로 새로운 연결을 생성할 수 없습니다.'라는 에러가 발생한 것이다.

 

여기서 KeepConnectionAlive: true 옵션을 줌으로써

DB 연결은 어플리케이션의 생명 주기와는 독립적으로 유지되었고 각각의 테스트 케이스는 DB 연결을 공유하였다.

각각의 테스트가 시작되어 app.init이 호출되어 어플리케이션이 초기화할때

DB 연결은 이미 활성화되었고 연결 중인 'default'을 그대로 사용하게 되어 새로운 연결을 시도하지 않는다.

 

 

 

- 응급 조치 (현재 제거된 방법)

app.e2e-spec.ts

// ...
describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  afterEach(async () => {
    await app.close();
  });
// ...

 

afterEach 메소드로 각 테스트가 끝날때마다 어플리케이션을 종료하고,

다음 테스트가 시작될때 beforeEach 메소드로 어플리케이션을 다시 켜주었다.

 

afterEach 메소드는 app.module.ts에서 KeepConnectionAlive: true 옵션을 준 뒤 제거하였다.