2018. 7. 18. 14:36ㆍProgramming/Redis
c++ hiredis rediscommand
#0 0x00007f344c6f9428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f344c6fb02a in __GI_abort () at abort.c:89
#2 0x00007f344c73b7ea in __libc_message (do_abort=2, fmt=fmt@entry=0x7f344c854ed8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f344c747781 in malloc_printerr (ar_ptr=0x7f3140000020, ptr=0x7f31400042b0, str=0x7f344c851c75 "corrupted size vs. prev_size", action=<optimized out>) at malloc.c:5006
#4 _int_realloc (av=av@entry=0x7f3140000020, oldp=oldp@entry=0x7f3140004290, oldsize=oldsize@entry=32, nb=nb@entry=80) at malloc.c:4298
#5 0x00007f344c748839 in __GI___libc_realloc (oldmem=oldmem@entry=0x7f31400042a0, bytes=bytes@entry=66) at malloc.c:3045
#6 0x0000000000944add in sdsMakeRoomFor (s=0x7f31400042a3 "", addlen=addlen@entry=31) at sds.c:221
#7 0x0000000000945463 in sdscatlen (s=<optimized out>, t=0x7f31400ce5f0, len=31) at sds.c:379
#8 0x0000000000944240 in __redisAppendCommand (c=0x7f31400ab660, cmd=<optimized out>, len=<optimized out>) at hiredis.c:910
#9 0x00000000009442cc in redisvAppendCommand (c=0x7f31400ab660, format=<optimized out>, ap=<optimized out>) at hiredis.c:942
#10 0x00000000009444cd in redisvCommand (c=0x7f31400ab660, format=<optimized out>, ap=<optimized out>) at hiredis.c:1003
라이브중 redisvCommand에서 Append 하는 상황에서 서버가 떨어지는 상황 발생
원인은 redisvCommand -> redisvAppendCommand 하는 중 가변인자로 명령어 조합하는 중에 메모리 재할당 과정에서 메모리를 잘못 할당될 가능성이 있다고 판단함.
그렇기 때문에 redisvCommand가 아닌 redisCommand로 Command 문자열을 sprintf로 조합하여 전송하는 방식으로 수정
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(nullptr, 0, fmt, ap);
va_end(ap);
va_start(ap, fmt);
char* replycommand = new char[len + 1];
vsprintf(replycommand, fmt, ap);
va_end(ap);
reply = (redisReply*)redisCommand(m_redisContext, replycommand);
delete[] replycommand;
가변인자로 들어온 인자에 대해서 문자열을 동적할당 하여 받아주려면 linux버전에서는 _vscprintf를 사용할 수 없음.
그렇기 때문에 비슷한 기능을 하는 gcc에서 사용 가능한 vsnprintf 함수를 사용하여 함수의 리턴값에서 문자열의 길이를 반환하여 길이 측정 후 동적 할당 하도록 함
여기서 다시 문제가 생김
static const char* ranklua = "local score = redis.call('ZSCORE', ARGV[1], ARGV[2]);"
"if (score == false) then "
" return {0, -1}; "
"end;"
"local rank = redis.call('ZCOUNT', ARGV[1], '('..score, '+inf');"
"return {tonumber(score), tonumber(rank)};";
REPLY reply = GetReply("EVAL %s 0 SRank:%d %llu", ranklua, season, guildSequence);
이런식의 lua를 %s 인자 형태로 넘기게 될 경우에 모든 command라인을 명령어로 인식함. 결국 rollback
int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
char *cmd;
int len;
len = redisvFormatCommand(&cmd,format,ap);
if (len == -1) {
__redisSetError(c,REDIS_ERR_OOM,"Out of memory");
return REDIS_ERR;
} else if (len == -2) {
__redisSetError(c,REDIS_ERR_OTHER,"Invalid format string");
return REDIS_ERR;
}
if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
free(cmd);
return REDIS_ERR;
}
free(cmd);
return REDIS_OK;
}
int redisAppendCommand(redisContext *c, const char *format, ...) {
va_list ap;
int ret;
va_start(ap,format);
ret = redisvAppendCommand(c,format,ap);
va_end(ap);
return ret;
}
redisAppendCommand에서 redisvAppendCommand함수를 호출하여 커맨드 조합하므로 수정하나 안하나 동일한 문제가 발생함.
// 수정 후 redis monitor에 잡힌 command
"EVAL" "local" "score" "=" "redis.call('ZSCORE'," "ARGV[1]," "ARGV[2]);if" "(score" "==" "false)" "then" "return" "{0," "-1};" "end;local" "rank" "=" "redis.call('ZCOUNT'," "ARGV[1]," "'('..score," "'+inf');return" "{tonumber(score)," "tonumber(rank)};" "0" "SRank:2" "34"
// 기존 redis monitor에 잡힌 command
"EVAL" "local score = redis.call('ZSCORE', ARGV[1], ARGV[2]);if (score == false) then return {0, -1}; end;local rank = redis.call('ZCOUNT', ARGV[1], '('..score, '+inf');return {tonumber(score), tonumber(rank)};" "0" "SRank:2" "34"
결론 1. linux는 windows에서 사용하는 가변인자의 길이를 측정할 함수를 사용할 수 없으므로 linux버전에 맞게 튜닝해야됨
결론 2. redisvCommand 함수를 사용할 경우 간헐적으로 메모리가 잘못될 수 있음
결론 3. redisvCommand를 redisCommand로 변경할 경우 luascript로 명령어를 실행하지 않는 반경에서 수정 가능함.
결론 4. luascript를 사용 할 경우, 메모리가 터지지 않도록 운에 맡기자..
'Programming > Redis' 카테고리의 다른 글
[Redis] Hiredis vs cpp_redis (0) | 2019.04.29 |
---|---|
레디스 개발자 antirez 블로그 주소 (0) | 2018.08.27 |
keys와 scan 풀탐색 시간 비교 (0) | 2018.08.02 |