mikro-orm 버그 리포팅 후기 (feat. auto increment)

이슈등록 링크: https://github.com/mikro-orm/mikro-orm/issues/5460

 

[ 문제 상황 ] 

사내에서 mikro-orm 을 Mysql 과 사용하고 있는데, 사내 db는  짝수 채번을 하고 있는데, 이상하게 mikro-orm 에서 여러 entity 를 영속화하면 pk 가 순차적으로 나오는 문제가 있었다. 

 

[ 이슈의 정체 ] 

JPA의 경우 ORM 채번을 할 때, 아래의 mysql ok packet 을 통해서 이루어진다. 

https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html

 

이후에 auto_increment 에 대한 설정 값을 요청하여서, 이를 기반으로 영속성 컨텍스트에 auto_increment pk 를 세팅을 하기 마련이다.

 

단순 repository.save 호출을 한다고 가정하면, 아래의 패킷이 날아간다. 

1. begin 

2. insert into 'table' ('데이터')

3. commit

 

그리고 2번 요청의 응답 패킷(OK packet)에 자세히 보면 아래와 같이 Last INSERT ID 라는 값이 내려온다. 

이 값을 기준으로 entity 에 id 를 세팅해준다. (참고로, commit 되기전에 db 에서는 채번이 되고, db 에서 한번 채번되는 경우에 tx 가 롤백이 되더라도 이후에 같은 값을 사용하지는 않는다 -> pk 는 항상 순차가 아닐 수도 있음)

 

사실 이런 부분은 mikro-orm 도 똑같을거라고 생각을 했는데, mikro-orm 자체가 어떤 특별한 재주를 부리는건 아닐거라서 MySQL 서버와의 통신을 통해서 채번한 결과를 사용하는건 당연해보였다. 

 

그런데, 아래와 같이 여러 개의 entity 를 저장하는 경우에 이상하게 pk 가 순차로 나왔다. (실제 코드는 아니고, 단순화하였다)

mysql 채번 결과를 바탕으로 entity 의 pk가 정해질 텐데,  pk가 순차적으로 생성되니 의아했다.

const result = await this.postRepository.save([
      new PostEntity('title'),
      new PostEntity('title2'),
      new PostEntity('title3'),
    ]);
    return result;
  }

@Injectable()
export class PostRepository {
  constructor(
    @InjectRepository(PostEntity)
    private readonly postRepository: EntityRepository<PostEntity>,
  ) {}

  async save(entity: PostEntity | PostEntity[]): Promise<PostEntity | PostEntity[]> {
    await this.postRepository.getEntityManager().persistAndFlush(entity);

    return entity;
  }
}

 

 

그래서, 해당 패킷이 어떻게 나가는지 wireshark 를 통해 패킷 분석을 해보았다 

wireshark 를 사용하여 packet 을 확인해보니 auto_increment 에 대한 쿼리가 패킷으로 잘 날아갔다. 

 

Ok packet이 잘 왔고, auto_increment 변수에 대한 값도 잘 조회하고 있었다. 

 

그런데 결과는 아래와 같았다. 

- entity: pk 는 1씩 증가

- tx commit 이후 db: db 레코드의 pk는 2씩 증가하고 있었다.

 

mikro-orm 버그가 확실해 보였고, 브레이크 포인트를 찍어서 확인을 해보았다. 

 

아래 코드에서 this.autoIncrementIncrement 필드에 1을 세팅하고 있는게 문제의 원인이었다.

mikro-orm 의 connection 은 mikro-orm/knex 라는 라이브러리를 통해 이루어지는데, 여기서 res는  Value 로 내려오는데,

할당 시에는 res?.auto_increment_increment 로 할당하고 있던게 문제였다.

async getAutoIncrementIncrement(ctx) {
        if (this.autoIncrementIncrement == null) {
            // the increment step may differ when running a cluster, see https://github.com/mikro-orm/mikro-orm/issues/3828
            const res = await this.connection.execute(`show variables like 'auto_increment_increment'`, [], 'get', ctx, { enabled: false });
            /* istanbul ignore next */
            this.autoIncrementIncrement = res?.auto_increment_increment ? +res?.auto_increment_increment : 1;
        }
        return this.autoIncrementIncrement;
    }

 

 

[ 후기 ] 

사실 팀 내에서 이 문제에 대해서 인지는 하고 있었고, 다른 방식으로 문제를 해결하고 있었다. 

그런데 시간이 나서, 트러블 슈팅을 해보았고 위와 같은 이슈가 있다는 것을 알게 되었던 것이다. 

 

그래서 이 부분에 대해서 이슈를 등록했고, 올라온지 몇시간도 안되어서 수정되었다.

 

사실 처음부터 pr 을 올릴까 말까 고민을 했는데, 

orm 특성상 driver 의 버전에 의해 생기는 버그일 수도 있다고 생각을 해서 pr 을 올리지 않고 상세하게 이슈 리포팅을 했다.

 
무지성 pr을 올릴걸하는 아쉬움이 남는다 ㅋㅋ

tistory 게시글 링크: https://pius712.tistory.com/22

댓글을 작성해보세요.