jedis returnBrokenResource 메서드에 대한 오해

프로그래밍|2016. 3. 9. 09:47

jedis의 returnBrokenResource 메서드에 대해 잘못 알고 있는 부분이 있어 정리한다.


@Before

public void init() {


GenericObjectPoolConfig config = new GenericObjectPoolConfig();

config.setMaxTotal(20);

config.setBlockWhenExhausted(true);

config.setMaxWaitMillis(1000);


JedisShardInfo shard1 = new JedisShardInfo("test.com", 6300);

JedisShardInfo shard2 = new JedisShardInfo("test.com", 6301);

JedisShardInfo shard3 = new JedisShardInfo("test.com", 6302);


jedisShardInfos = new ArrayList<>();

jedisShardInfos.add(shard1);

jedisShardInfos.add(shard2);

jedisShardInfos.add(shard3);


pool = new ShardedJedisPool(config, jedisShardInfos);

template = new ShardedJedisTemplate();

template.setShardedJedisPool(pool);

}



public static <T> T execute(ShardedJedisPool jedisPool, ShardedJedisReturnedCallback<T> shardedJedisReturnedCallback) {

ShardedJedis shardedJedis = null;

boolean broken = false;

try {

shardedJedis = jedisPool.getResource();

T returnValue = shardedJedisReturnedCallback.doInShardedJedis(shardedJedis);

return returnValue;

} catch (JedisConnectionException e) {

if (shardedJedis != null) {

broken = true;

jedisPool.returnBrokenResource(shardedJedis);

}

throw new RedisException(e.getMessage(), e);


} finally {

if (shardedJedis != null && broken == false) {

jedisPool.returnResource(shardedJedis);

}

}

}



@Test

public void shardTest() {

for (int i = 0; i < 10; i++) {

String status = template.set("test:key" + i, "testvalue");

System.out.println(status);

}

}


위의 shardTest() 메소드가 실행되면 10번의 OK가 출력이 되고 3대의 redis 장비에 데이터가 샤딩되어진 것을 확인할 수 있다.

redis(6300):0>keys *

1) test:key1

2) test:key2

3) test:key8

4) test:key6


redis(6301):0>keys *

1) test:key9

2) test:key7

3) test:key4


redis(6302):0>keys *

1) test:key3

2) test:key5

3) test:key0


만약 위의 환경에서 6302 포트를 사용하는 레디스 서버를 셧다운 한 후 shardTest() 테스트 함수를 다시 실행하게 된다면 6302에 적재되는 데이터가 6300, 6301 서버로 데이터가 분배되어야 할 것으로 생각을 하였다.


이와 같이 생각한 이유는 jedis 라이브러리의 jedisPool.returnBrokenResource(shardedJedis); 가 하는 역할이 pool 안에서 connection timeout으로 인해 오류가 난 리소스를 제거하는 것으로 알고 있었기 때문이다.

허나 아무리 테스트를 해봐도 서비스되고 있는 레디스 장비에 적절하게 데이터가 샤딩되지 않았다.


결국 https://mail.google.com/mail/u/0/#inbox/153541b65559f797 에 질문을 올렸고, 그 답을 찾을 수 있었다.


먼저 jedisPool.getResource(); 호출 시 pool 객체 안에 idle 상태의 ShardedJedis 가 존재하면 해당 객체를 return 하고 없으면 ShardedJedis 생성한 후 이를 사용하게 된다.

ShardedJedis 객체를 생성할 때의 코드를 보면 생성자에 shards 변수를 넘겨주고 있는데 shards 변수에는 모든 서버의 정보가 저장되어 있다. (6300, 6301, 6302 레디스 서버 정보)

private List<JedisShardInfo> shards; <-- 해당 변수에 server1, server2, server3 정보가 저장되어 있음


@Override

    public PooledObject<ShardedJedis> makeObject() throws Exception {

      ShardedJedis jedis = new ShardedJedis(shards, algo, keyTagPattern);

      return new DefaultPooledObject<ShardedJedis>(jedis);

    }


특정 서버의 장비가 셧다운되어 connection timeout이 발생하여도 shards 변수에는 변동이 없기 때문에 위의 테스트 코드는 다음과 같은 시나리오대로 동작을 하게 되었던 것이다.


0에서 9까지의 값에 따른 데이터 분포는 다음과 같다고 가정하자.

redis(6300)

 - 1,2,8,6


redis(6301)

 - 9,7,4


redis(6302) <- 해당 장비 셧다운 상태

 - 3,5,0


1번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis 객체가 존재하지 않아 새로 생성 후 값 저장. 이후 pool에 자원 반납


2번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 해당 객체를 꺼내서 사용 후 반납


3번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 재사용하는데 connection timeout 발생하여 ShardedJedis 객체를 pool에서 제거


4번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis 객체가 존재하지 않아 새로 생성 후 값 저장. 이후 pool에 자원 반납


5번 값을 저장할 때 pool안에 IDLE 상태의 ShardedJedis가 존재하여 재사용하는데 connection timeout 발생하여 ShardedJedis 객체를 pool에서 제거


결과적으로 내가 jedis returnBrokenResource 메서드에 대해 오해하게 된 것은 jedis에서 제공하고 있는 샤딩 동작 방법을 제대로 이해하지 못한 것이였고, 풀에서 관리하는 ShardedJedis 객체가 최초 애플리케이션이 구동할 때 한번에 생성을 하는 줄 알았다는 것이다.



댓글()