배치를 이용해서 Elasticsearch에 데이터를 삽입하던 중 version conflict라는 오류가 자주 발생했다. 처음에는 Elasticsearch 버전이 동일한데 왜? 오류가 나는지 몰랐다.

그래서 검색해보니 인덱스안에 document에는 각자 관리하는 version이 존재한다. 이 version은 document가 수정될 때 하나씩 올라가게 되는데 version이 10인 상태에 document에 여러 서버 모듈에서 해당 document에 업데이트를 하려고 하니 문제가 발생하였다.

그 이유는 version 10인 상태에서 작업에 들어간 두 모듈은 한 모듈이 먼저 11로 업데이트를 시키고 다음 모듈이 작업을 진행하려고 할 때 자기가 알고 있던 마지막 version인 10이 아니라 11로 바껴있는것을 보고 에러를 뱉어내는것이다. 이렇게 까지 세심하게 챙겨줄지 몰랐다. 알면 알수록 elasticsearch라는 db는 정말 매력적이다.

PUT wedul_index 
{
  "mappings": {
      "_doc": {
        "dynamic": "false",
        "properties": {
          "name": {
            "type": "text"
          }
        }
      }
  }
}

위와 같이 인덱스가 있고 document 하나가 들어있다. 여기에 age라는 값과 gender를 집어넣어보자. 이를 동시에 호출해보자.

document

그럼 document 하나에 필드를 동시에 업데이트하는 update.sh라는 스크립트를 만들어서 실행시켜보자.

curl -X POST "localhost:9200/wedul_index/_update_by_query" -H 'Content-Type: application/json' -d' { "script": { "source": "ctx._source[\u0027gender\u0027] = \u0027M\u0027"}, "query": { "match": { "name": "위들" } } } ‘
curl -X POST "localhost:9200/wedul_index/_update_by_query" -H 'Content-Type: application/json' -d' { "script": { "source": "ctx._source.age = 10", "lang": "painless" }, "query": { "match": { "name": "위들" } } } ‘

그럼 위에 설명했던 것 처럼 버전이 먼저 변경이 되면서 다음과 같은 에러를 뱉어낸다.

[{"index":"wedul_index","type":"_doc","id":"3MSd5WsB_jV9Cf9TkYLV","cause":{"type":"version_conflict_engine_exception","reason":"[_doc][3MSd5WsB_jV9Cf9TkYLV]: version conflict, current version [3] is different than the one provided [2]","index_uuid":"sJI8sBnrTP-OW8OG8YBqWA","shard":"3","index":"wedul_index"},"status":409}]

 

이를 해결하기 위해서는 retry_on_conflict 옵션을 함꼐 부여할 수 있는데 이 옵션은 version conflict이 발생했을 때, 업데이트 재시도를 몇회 할건지 지정하는 옵션이다.

좀 더 자세한 사항은 아래 elasticsearch 메뉴얼을 보면 자세히 나와있다.

참조
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html

lombok 사용할 때 다음과 같은 에러를 본적이 있을 것이다.


[에러내용]

Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.


이 에러는 상속을 받은 자식클래스에 발생하는 에러로서 다음과 같이 해결해줄 수 있다


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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.wedul.wedulpos.user.dto;
 
import org.apache.ibatis.type.Alias;
 
import com.wedul.common.dto.CommonDto;
import com.wedul.common.util.HashUtil;
 
import lombok.Data;
import lombok.EqualsAndHashCode;
 
/**
 * User정보 Dto 
 * 
 * @author wedul
 * @date 2017. 11. 4.
 * @name UserDto
 */
@Alias("UserDto")
@Data
@EqualsAndHashCode(callSuper=false)
public class UserDto extends CommonDto {
    private String email;
    private String password;
    private boolean isadmin;
    
    public UserDto() {}
    
    public UserDto(String email) {
        this.email = email;
    }
    
    public UserDto(String email, String password) {
        this.email = email;
        this.password = password;
    }
    
    public UserDto(String email, String password, boolean isadmin) {
        this.email = email;
        this.password = password;
        this.isadmin = isadmin;
    }
    
    public String getEcPassword() {
        return HashUtil.sha256(this.password);
    }
    
}
 
cs




자바에는 몇 가지에 throwable을 제공한다.

점검지정 예외 (checked error)
컴파일 시점에 예외가 발생하는 부분으로 컴파일 시에 에러를 처리하는 코드를 삽입하지 않으면 컴파일이 되지 않는다.


1
2
3
4
5
6
7
8
9
10
public void ioOperation(boolean isResourceAvailable) {
  try {
    if (!isResourceAvailable) {
      throw new IOException();
    }
  } catch(IOException e) {
    // Handle caught exceptions.
  }
}
 
cs




unchecked error
컴파일 시점에 체크되지 않는 에러
실생시점 예외(runtime exception)와 오류(error)은 모두 unchecked error에 속한다.



실행시점 예외
NullPointerException, ArrayIndexOutOfBoundException와 같이 실행시간에 발생되는 에러



오류
OutofMemoryError ThreadDeath 같은 에러
catch 블럭으로 잡아도 대응 방법이 없다.




이런 예외들을 어떻게 기준을 잡고 처리해야 하는지 알아보자.

unchecked error의 기본적인 규칙은 caller 측에서 복구할것으로 여겨지는 상황에 대해서는 점검지점 예외를 사용해야한다.

-> 점검지점 예외를 사용한다는 것은 API 사용자에게 복구할 권한을 준다는 것이다. 그러므로 점검지점 예에서 발생한 에러는 catch에서 무조건 처리를 해야한다.


프로그래밍 오류를 표현할 때는 실행시점 예외를 사용하라.

-> 실행시점에 발생하는 대다수의 에러는 규칙위반으로 발생한다. 예를 들어 배열의 인덱스를 넘어가는 접근을 할때 발생하는 ArrayIndexOutOfBoundException이 대표적인 예이다.


예외 정보 문자열을 파싱해서 원하는 정보를 얻어서 처리하는 것은 굉장히 위험한 행동이다.

-> 예외정보는 각 버전 별로 변경될 수 있으며, 문자열에 경우 내용이 변하거나 없어질 수있다. 또한 정확한 예외에 대한 명세를 한 클래스 자체가 거의 없기 때문에 더욱더 위험하다.



결론을 말하면 복구 가능한경우에는 try-catch, throws를 사용하여 점검가능한 예외를 사용하고, 그것이 아닌 경우에는 실행시점 예외를 사용하라.

출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙58 인용.

프로젝트 진행을 위해 필요한 라이브러리 설치를 위해 npm 명령어를 사용했는데 다음과 같은 오류가 발생하였다.

[명령어]
jeongcheol-ui-MacBook-Pro:gridstack jeongcheol$ npm install gridstack --save

[에러내용]
npm ERR! code ENOSELF
npm ERR! Refusing to install package with name "gridstack" under a package
npm ERR! also called "gridstack". Did you name your project the same
npm ERR! as the dependency you're installing?
npm ERR!
npm ERR! For more information, see:
npm ERR!     <https://docs.npmjs.com/cli/install#limitations-of-npms-install-algorithm>
 
npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/jeongcheol/.npm/_logs/2018-05-25T14_26_24_708Z-debug.log


이 에러는 저 위에 내용을 읽어보면 알겠지만 현재 설치하려는 라이브러리와 프로젝트의 이름이 동일할 때 발생한다.

[조치방법]
1. 폴더명을 변경
2. package.json의 프로젝트 이름을 변경한다.

-> gridstack => gridstack_test




[결과화면]
변경 후에는 잘된다.







+ Recent posts