일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 한빛미디어
- coursera
- 플레이 프레임워크
- avo
- scala
- Spring
- scala 2.10
- spring-kafka
- play framework
- kafka interactive query
- Elk
- statestore
- springboot
- kafka streams
- reactive
- kafkastream
- 카프카
- kafkastreams
- Slick
- Logstash
- enablekafkastreams
- confluent
- gradle
- schema registry
- RabbitMQ
- spring-cloud-stream
- aws
- Kafka
- Elasticsearch
- spring-batch
- Today
- Total
b
LINE 앱 ID Generator (snowflake) 본문
마침, LINE 세미나에서 비슷한 주제가 있는 것을 확인하여서 해당 내용을 정리하였다.
나와 크게 다른점은 timestamp step 을 1ms 가 아닌 10ms 로 변경하여서 사용범위를 100년 이상으로 증가시킨 점 정도이다.
- 글로벌 유니크하고, 선형으로 커지는 ID 이고 백엔드에서 ID가 생성된다
- 메신저의 액티브 유저는 2억명이고, 하루 40억개의 메시지가 발행된다.
- 메시지가 발행될때에는 ID 가 없고, 서버에서 메시지를 받았을 때 ‘서버에서 ID를 채번’ 그리고 그 ID를 다시 클라이언트에게도 보내준다.
이 라인앱의 ID Generator 가 필요로하는 Requirements : 빠르고, 단순하고, 유지보수가 쉬워야 한다.
첫번째 적용 : Redis Master-Replica 모델을 적용함
단순하다. 하지만 정말 괜찮을까?
Master 가 죽으면 Replica 를 바라보게 할 것이다. 하지만 Async 로 Master 데이터가 복제되므로 '데이터가 유실되거나, Replica 마다 마지막 복제 ID가 다를 수 있기 때문에 정확한 마지막 ID를 알기 어렵다' -> ID의 채번에 문제가 생기므로 장애
ID 생성 자체가 '백엔드(레디스)' 에서 이루어지므로 라인앱 <-> 레디스 네트워크 상에 문제가 발생한다면 장애
어떻게 고칠까 고민해보니... 결국엔 snowflake 로 도달하게 된다
Snowflake 를 그대로 적용하기 보다는 약간 커스텀 해서 사용하기로 결정함
이 이름을 MIG 라고 하였다.
- resolution 를 1ms -> 10ms 로 변경해서 2188년까지 사용가능
- seq bit 를 18 bit 로 확장
- 그래서 2600 만개의 ID가 1개의 MIG 인스턴스에서 생성가능하다.
(주, 나는 NodeBit 12, Seq 를 8 bit로 선언했고 GSI 라고 이름 붙였는는데-_-)
이렇게 MIG 를 멀티 IDC 에서 사용하면 모든 것이 Happy 할줄 알았지만
또 다른 문제가 남아있다.
테스트 중에 발견된 사례 2가지를 소개하겟다.
이슈1 : ID가 선형증가되지 않는다
clock drift(backword) (주: 나는 if (current < lastTimestamp) 이렇게 에러 로그만 찍고 있다. 이걸 throw exception 하고, 아마 retries 에 의해 다른 MIG 가 채번을 하겠지. 그 동안 이 인스턴스는 ID 생성을 하지 않는다. 그래서 '언젠가는 monotonic 하게 다시 동작할 것이다')
RTT 감소와 와 atomic command 을 lua script로 사용중.
(주: 우리도 그렇게 했다. 그럼 라인은 어떤어떤 커맨드들을 합쳤을까?)
하지만 LUA 를 쓸때 큰 버그를 하나 겪음
lua script 에서 문자->숫자를 변환할 때 'redis lua 버전에 따라 integer 대응이 정상적이지 않음. LUA 5.3 에서는 정수형이 도입이 되었지만, 하위호환성 때문에 여전히 이 문제는 있어서 루아에서 BigInteger를 구현하는 등 다른 방식으로 이용함)