ElasticSearch 데이터 보정 중에 휴먼에러로 인해 이슈가 생길'뻔' 했던, 그리고 그 과정에서 알게된 내용을 공유합니다.
POST {IndexName}/_update_by_query
{
"script": {
"source": "ctx._source.{field1} = 0; ctx._source.{field2} = 0;",
"lang": "painless"
}
},
"query": {
"terms": {
"seq": [
// seq(PK) 리스트
]
}
}
}
현재 내가 개발하고있는 샵바이
의 통계 서비스는 ElasticSearch를 통해 제공되고 있다.
그리고, 한 고객사의 통계 데이터를 보정해줄 일이 있었다.
이미 이전에 한번 보정을 진행했었는데, 바로 다음날 추가적인 요청이 들어왔다.
내용은 동일한거라, 기존에 사용했던 쿼리를 재활용 했는데 이 과정에서 실수
가 있었다.
이전에 보정한 쿼리는 각 seq마다 다른 값으로 보정이 필요했기에 params를 사용했지만,
다음날에 보정한 쿼리는 모두 0으로 보정하면 됐기에, params를 날렸다.
그렇게 해서 위에 이슈 쿼리
가 만들어졌다.
그리고, 아무런 의심 없이 리얼(운영)환경에 바로 날려버렸다. 😱
쿼리를 날렸는데, 너무 오래걸리는 것이다. 한,, 10초는 기다렸던 것 같다.
이상하다,,, seq가 400개라서 오래걸리는건가? 서버가 잠깐 느린건가?
그리고 kibana의 응답값에 409 conflict
가 발생했다.
쿼리를 다시 봤는데......
그렇다.. 중괄호 하나를 실수로 지우지 않아서 WHERE 절이 없는 UPDATE 쿼리가 만들어졌고, 그대로 날렸다...
전체 데이터를 업데이트 하느라 시간이 오래 걸린 것이다.
Elasticsearch 는 Transaction 개념이 없는걸로 아는데?
그러면 충돌나기 전 데이터는 전부 반영된거 아닌가?
그래서 확인을 해봤다.
GET {indexName}/_search
{
"_source": ["{field1}", "{field2}"],
"version": true
}
// 응답
"hits": [
{
"_index": "{indexName}",
"_id": "114613",
"_version": 1, // 최초 document 생성 시 version은 1임. 즉 문서 update가 안됐다는 의미
"_score": 1,
"_source": {
"{field1}": 1, // 0으로 업데이트 안돼있음
"{field2}": 20 // 0으로 업데이트 안돼있음
}
},
...
]
다행히 document에 반영은 안되어있었다.
그렇다. 409 conflict가 나면 해당 요청 내에서 이미 데이터가 수정 됐을 지라도 반영이 안된다.
Elasticsearch는 document를 업데이트 할 때 아래 순서에 따라 업데이트 된다고 한다.
나는 2번에서 에러가 발생했기에 3번 re-indexing 이 진행되지 않은 케이스이다.
일반적으로 RDB에서는 트랜잭션 격리수준에 따라 데이터나 row를 Lock하여 충돌을 사전에
방지한다 (= Pessimistic Lock)
Elasticsearch는 데이터가 충돌할 가능성이 낮다고 판단하여 사전에 lock을 걸지 않는다. (= Optimistic Lock)
하지만 데이터 충돌이 일어나면, update 자체를 실패시키고 사용자에게 결정하도록 한다.
ES의 OCC는 데이터 무결성을 유지하는데 도움을 주며, 아래의 원칙에 기반한다.
문득 이런 생각이 들었다.
ES는 트랜잭션 개념이 없는데, 데이터를 쓰다가 실패하면 전부 롤백이 되는건 트랜잭션이랑 뭐가 다를까?
내가 생각한 차이점은
단순히 이정도였다. 하지만 트랜잭션의 본질(?) 인 ACID를 생각해보니 더 쉽게 이해가 되었다.
A (Atomicity) : 트랜잭션 내의 '모든' 요청들이 완전히 실행되거나 실행되지 않아야 함
C (Consistency) : 트랜잭션이 완료되면 DB는 항상 일관된 상태
I (Isolation) : 하나의 트랜잭션이 실행중인 동안 다른 트랜잭션은 접근 못함
D (Durability) : 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 반영
또한, RDBMS에서의 롤백은 일단 데이터를 쓰고, 원래대로 되돌리는 것을 의미하는 반면에
ES는 애초에 원본 document를 수정하는 것이 아니라 복사된 document에 값을 쓰고 index에 덮어쓰는 방식이기 때문에 되돌린다(Rollback)
는 개념으로 보기는 어렵겠다는 생각이 들었다.
이번 케이스에서는 운좋게 살아남았(?)지만, 앞으로도 이런 일은 없어야 한다.
(사실 이런 에러메시지가 있었는데, 그냥 키바나 버그인줄 알고 넘어갔었다,, 🙁)
이번에는 409 conflict가 발생해서 데이터가 써지지 않았지만,
만약 모든 데이터가 업데이트 되버리면 어떻게 될까?
통계 데이터는 RDB (mysql)에 먼저 저장되고, ES에 데이터가 복사된다.
따라서 ES의 데이터가 잘못됐을 경우
위와 같은 플로우로 롤백이 진행된다.
https://www.elastic.co/guide/en/elasticsearch/guide/master/version-control.html
https://www.elastic.co/kr/blog/elasticsearch-versioning-support