GithubHelp home page GithubHelp logo

naver / arcus-c-client Goto Github PK

View Code? Open in Web Editor NEW
12.0 18.0 16.0 2.47 MB

ARCUS C client

Home Page: https://github.com/naver/arcus

License: Apache License 2.0

C 27.15% C++ 63.04% Shell 0.92% Python 0.90% Perl 0.05% DTrace 0.03% Makefile 0.11% M4 7.18% GDB 0.01% Yacc 0.37% Lex 0.25%
arcus-c-client libmemcached arcus

arcus-c-client's People

Contributors

computerphilosopher avatar greenmonn avatar hoonmin avatar hoyaaaa avatar hyongyoubkim avatar ing-eoking avatar jhpark816 avatar lynix94 avatar minwoojin avatar namsic avatar suhwanjang avatar theverytak avatar uhm0311 avatar whchoi83 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arcus-c-client's Issues

Response message를 fetch하는 fetch_value_header()에서 buffer 크기 지정이 잘못됨.

아래와 같이 buffer 크기가 잘못된 경우가 있다.

  • bkey 값을 fetch하는 경우 아래와 같이 호출함
    • fetch_value_header(ptr, to_read_string, &read_length, MEMCACHED_COLL_MAX_BYTE_STRING_LENGTH)
    • MEMCACHED_COLL_MAX_BYTE_STRING_LENGTH는 (31*2+1)로 정의됨.
    • byte array bkey 최대 길이인 31 외에 "0x" 문자가 포함되므로 , 2 바이트 크게 지정해야 함.
  • map key를 fetch하는 경우 아래와 같이 호출하고 있음
    • fetch_value_header(ptr, to_read_string, &read_length, MEMCACHED_COLL_MAX_BYTE_STRING_LENGTH)
    • MEMCACHED_COLL_MAX_BYTE_STRING_LENGTH는 map key와는 무관한 값임.
    • 최대 길이인 250 외에 '\0' 문자 포함하여 251로 지정되어야 함.
  • 밸류 크기인 bytes 값을 fetch하는 경우 아래와 같이 호출
    • fetch_value_header(ptr, to_read_string, &read_length, BYTES_MAX_LENGTH)
    • BYTES_MAX_LENGTH는 10이며, '\0' 문자 포함하기 위해 1 바이트 크기 지정해야 함.
  • flags 값을 fetch하는 경우, bytes 값 fetch와 같은 문제

그 외, 불필요하게 크게 지정된 부분과 일관성 없는 부분이 있음.

program core because of server close connection

program core because memcache invoke abort()
arcus-c-client version: 1.13.0

(gdb) bt
#0  0x00000038e5a32495 in raise () from /lib64/libc.so.6
#1  0x00000038e5a33c75 in abort () from /lib64/libc.so.6
#2  0x00007f73a98b77ba in memcached_set_error (self=..., rc=MEMCACHED_ERRNO, at=0x7f73a98de53b "libmemcached/get.cc:205") at libmemcached/error.cc:316
#3  0x00007f73a98bbff8 in ascii_get_by_key (ptr=0x7f73a3ed1000, master_server_key=4294967295, is_group_key_set=false, key=0x7f7270678868 "***************", key_length=22)
    at libmemcached/get.cc:205
#4  0x00007f73a98bcbab in memcached_get_by_key (ptr=0x7f73a3ed1000, group_key=0x0, group_key_length=0, key=0x7f7270678868 "**************", key_length=22, 
    value_length=0x7f738b7f5ed8, flags=0x7f738b7f5ee0, error=0x7f738b7f5ee4) at libmemcached/get.cc:541
#5  0x00007f73a98bd12c in memcached_get (ptr=0x7f73a3ed1000, key=0x7f7270678868 "****************", key_length=22, value_length=0x7f738b7f5ed8, flags=0x7f738b7f5ee0, 
    error=0x7f738b7f5ee4) at libmemcached/get.cc:643
Segmentation fault

(gdb) f 3
#3  0x00007f73a98bbff8 in ascii_get_by_key (ptr=0x7f73a3ed1000, master_server_key=4294967295, is_group_key_set=false, key=0x7f7270678868 "***************", key_length=22)
    at libmemcached/get.cc:205
205     in libmemcached/get.cc
(gdb) p rc
$5 = MEMCACHED_ERRNO
(gdb) p server_key
$6 = 10
(gdb) p command
$7 = 0x7f73a98de533 "get "
(gdb) p vector
$8 = {{length = 4, buffer = 0x7f73a98de533}, {length = 0, buffer = 0x0}, {length = 22, buffer = 0x7f7270678868}, {length = 2, buffer = 0x7f73a98de538}}

(gdb) p *instance
$10 = {options = {is_allocated = false, is_initialized = true, is_shutting_down = false, is_dead = false}, number_of_hosts = 0, cursor_active = 0, port = 11247, fd = -1, 
  io_bytes_sent = 0, server_failure_counter = 1, server_failure_counter_query_id = 2, weight = 1, version = 11, state = MEMCACHED_SERVER_STATE_IN_TIMEOUT, io_wait_count = {read = 0, 
    write = 0}, major_version = 255 '\377', minor_version = 255 '\377', micro_version = 255 '\377', is_enterprise = false, type = MEMCACHED_CONNECTION_TCP, read_ptr = 0x7f728b4abfd8 "", 
  read_buffer_length = 0, read_data_length = 0, write_buffer_offset = 0, address_info = 0x7f72706f6300, address_info_next = 0x7f72706f6300, next_retry = 1, immediate_reconnect = false, 
  groupindex = -1, next = 0x0, root = 0x7f73a3ed1000, limit_maxbytes = 0, error_messages = 0x7f729906f800, read_buffer = '\000' <repeats 8195 times>, 
  write_buffer = "version\r\n", '\000' <repeats 8186 times>, hostname = "127.0.0.1", '\000' <repeats 1010 times>}

(gdb) p *(instance->error_messages)
$11 = {root = 0x7f73a3ed1000, query_id = 2, next = 0x0, rc = MEMCACHED_ERRNO, local_errno = 104, size = 148, 
  message = "[2021-07-06 11:36:14.528575] mc_id:27 mc_qid:2 er_qid:2 SYSTEM ERROR(Connection reset by peer), host: 127.0.0.1:11247 -> libmemcached/io.cc:501"}
static memcached_return_t ascii_get_by_key(memcached_st *ptr,
                                           uint32_t master_server_key,
                                           bool is_group_key_set,
                                           const char *key,
                                           const size_t key_length)
{
  memcached_return_t rc;
  memcached_server_write_instance_st instance;
  uint32_t server_key;

  if (is_group_key_set)
  {
    server_key= master_server_key;
  }
  else
  {
    server_key= memcached_generate_hash_with_redistribution(ptr, key, key_length);
  }
  instance= memcached_server_instance_fetch(ptr, server_key);

  const char *command= (ptr->flags.support_cas ? "gets " : "get ");
  struct libmemcached_io_vector_st vector[]=
  {
    { strlen(command), command },
    { memcached_array_size(ptr->_namespace), memcached_array_string(ptr->_namespace) },
    { key_length, key },
    { 2, "\r\n" }
  };

  rc= memcached_vdo(instance, vector, 4, true);
  if (memcached_failed(rc))
  {
    memcached_io_reset(instance);
    memcached_set_error(*ptr, rc, MEMCACHED_AT);   /******core at this line, because of rc is MEMCACHED_ERRNO*********/
  }
  return rc;
}

"CLEANUP: enable optimize mget operation with instance version" 리뷰와 수정

CLEANUP: enable optimize mget operation with instance version 커밋에 대한 리뷰 의견입니다.
이번에 다시 보니, 수정해야 할 큰 사항들이 보입니다.

첫째, 각 memcached server의 버전 정보를 얻는 방식에 관하여,

  • 문제점
    • 기존 코드를 너무 많이 변경하고 있음.
    • 최신 libmemcached 코드와 차이가 많음.
  • 최신 libmemcached 확인
    • 각 memcached server의 버전 정보를 구하는 기능이 이미 들어가 있음: memcached_version_instance()
    • version response 처리에서 version 정보 획득은 response 모듈에서 처리하고 있음.
    • memcached_version() 함수도 병렬 수행이 가능하게 최적화되어 있음.
  • 수정 방안
    • 최신 libmemcached 구현 방안을 그대로 따르는 것이 나을 것 같음.
    • enterprise 버전 확인도 response 모듈에서 처리되어야 함.
    • mget 지원 여부의 설정은 외부에서 설정하는 것이 좋을 듯 하지만, 적절한 위치는 검토 필요.

둘째, mget 연산의 최적화 처리

  • 문제점
    • 기존 get, gets, mget 처리 코드가 1개 함수 내에 모두 들어가다 보니, 복잡해 짐.
  • 수정 방안
    • 기존 get, gets 연산은 기존 함수로 처리하고
    • mget 연산이면 별도 구현된 mget 함수를 호출하여 처리하면 나을 듯.
  • 추가 확인해야 할 사항
    • gets 연산도 mget 처리하여도 되지 않나 ?
    • cache server와 java client에서 gets 연산의 처리 방식 확인 필요.
    • get vs. mget 판단 기준: 현재는 2개 이상의 key이면 mget
      • N개 이상의 key 조건이면서, 어떤 N값이 좋은 지를 판단 및 근거 제시

io 모듈 코드 정리

기존 io 모듈 코드의 readability 가 좋지 않으므로 최신 libmemcached 코드를 참고하여 개선하도록 합니다.

  • TCP / UDP protocol 별 io 함수 분리
  • code refactoring

사용되지 않는 memcached_set_errno() 함수 제거.

아래의 memcached_set_errno() 함수는 사용되지 않으므로 제거하도록 합시다.

  memcached_return_t memcached_set_errno(memcached_st& memc, int local_errno, const char *at, const char *str, size_t length);

  memcached_return_t memcached_set_errno(memcached_server_st&, int local_errno, const char *at, const char *str, size_t length);

아래와 같이 에러 메세지를 memcached_string_t 구조로 받는 다른 memcached_set_errno() 함수가 존재하므로, 위의 함수는 제거하여도 됩니다.

  memcached_return_t memcached_set_errno(memcached_st& memc, int local_errno, const char *at, memcached_string_t& str);

  memcached_return_t memcached_set_errno(memcached_server_st&, int local_errno, const char *at, memcached_string_t& str);

내부에서 사용되는 API의 type 변경

현재 collection 구조에서 사용하는
xxx_query_init() 함수의 API type은 LIBMEMCACHED_API 이다.
LIBMEMCACHED_API type은 application에서 호출 하는 type 이다.
application에서 호출하지 않는 type은 LIBMEMCACHED_LOCAL 이다.

xxx_query_init()을 application에서 호출하는 collection은 b+tree가 유일하다.
API type을 점검하고, 내부에서만 사용하는 API 는 LOCAL type으로 변경 한다.

[CLEANUP] 서버 모델에 따른 zk connect wait code refactoring

arcus는 아래 세 가지의 서버 모델을 지원하고 있고 각각의 초기화 메소드를 제공하고 있다.

  • single thread
    • arcus_return_t arcus_connect(memcached_st *mc, const char *ensemble_list, const char *svc_code)
  • multi thread
    • arcus_return_t arcus_pool_connect(memcached_pool_st *pool, const char *ensemble_list, const char *svc_code)
  • multi process
    • arcus_return_t arcus_proxy_create(memcached_st *mc, const char *ensemble_list, const char *svc_code)

위에 따라 각각 zookeeper와 연결을 각 메소드 내에서 진행하고 있는 상태이다.
(arcus_connect와 arcus_pool_connect는 내부에 같은 함수를 사용하여 공유한다.)

  • single/multi thread : do_arcus_connect()
  • multi process : arcus_proxy_create()

실제 내부 코드는 거의 같은 상태인데, 각각 연결을 wait하는 코드가 분리되어 있다.
관련부분을 검토하여 하나의 함수로 표현 가능하다면 하나의 함수로 제공하는게
코드 관리나 readability 관점에서 더 좋다고 판단된다.

참고.

arcus_return_t arcus_proxy_create()
{
  /* Creates a new ZooKeeper client thread. */
  rc= do_arcus_zk_connect(mc);
  if (rc != ARCUS_SUCCESS) {
    return ARCUS_ERROR;
  }

  arcus = static_cast<arcus_st *>(memcached_get_server_manager(mc));
  if (arcus->is_initializing) {
    struct timeval now;
    struct timespec ts;

    ZOO_LOG_WARN(("Waiting for the cache server list..."));

    /* Wait for the cache list (timed out after 5 sec.) */
    gettimeofday(&now, NULL);
    ts.tv_sec= now.tv_sec + (ARCUS_ZK_SESSION_TIMEOUT_IN_MS / 1000 / 3);
    ts.tv_nsec= now.tv_usec * 1000;

    pthread_mutex_lock(&lock_arcus);
    if (pthread_cond_timedwait(&cond_arcus, &lock_arcus, &ts)) {
      ZOO_LOG_ERROR(("pthread_cond_timedwait failed. %s(%d)", strerror(errno), errno));
      rc= ARCUS_ERROR;
    }
    pthread_mutex_unlock(&lock_arcus);

    if (rc == ARCUS_SUCCESS) {
      ZOO_LOG_WARN(("Done"));
    }
  }
  return rc;
}

static inline arcus_return_t do_arcus_connect()
{
  /* Creates a new ZooKeeper client thread. */
  rc= do_arcus_zk_connect(mc);
  if (rc != ARCUS_SUCCESS) {
    return ARCUS_ERROR;
  }

  ZOO_LOG_WARN(("Waiting for the cache server list..."));

  pthread_mutex_lock(&lock_arcus);
  arcus= static_cast<arcus_st *>(memcached_get_server_manager(mc));
  if (arcus->is_initializing) {
    struct timeval now;
    struct timespec ts;

    /* Wait for the cache list (timed out after 5 sec.) */
    gettimeofday(&now, NULL);
    ts.tv_sec= now.tv_sec + (ARCUS_ZK_SESSION_TIMEOUT_IN_MS / 1000 / 3);
    ts.tv_nsec= now.tv_usec * 1000;

    if (pthread_cond_timedwait(&cond_arcus, &lock_arcus, &ts)) {
      ZOO_LOG_ERROR(("pthread_cond_timedwait failed. %s(%d)", strerror(errno), errno));
      rc= ARCUS_ERROR;
    }
  }
  pthread_mutex_unlock(&lock_arcus);

  if (rc == ARCUS_SUCCESS) {
    ZOO_LOG_WARN(("Done"));
  }
  return rc;
}

memcached_get_by_key() 함수 마지막에 dummy fetch 필요성 검토.

memcached_get_by_key() 함수의 마지막에 아래와 같은 dummy fetch 코드가 있다.
필요성을 검토해 보고, 필요가 없다면 제거합시다.

  size_t dummy_length;
  uint32_t dummy_flags;
  memcached_return_t dummy_error;

  char *dummy_value= memcached_fetch(ptr, NULL, NULL,
                                     &dummy_length, &dummy_flags,
                                     &dummy_error);
  assert_msg(dummy_value == 0, "memcached_fetch() returned additional values beyond the single get it expected");
  assert_msg(dummy_length == 0, "memcached_fetch() returned additional values beyond the single get it expected");
  assert_msg(ptr->query_id >= query_id +1, "Programmer error, the query_id was not incremented.");
  //assert_msg(ptr->query_id == query_id +1, "Programmer error, the query_id was not incremented.");

smget 수행에서의 error handling 수정 필요

여러 cache nodes로 부터 smget response를 받는 과정 중에 오류가 발생할 경우,
smget result는 제대로 된 결과를 가지고 있지 않은 상태이며,
그 상태에서 MEMCACHED_SUCCESS를 리턴하는 문제가 있다.

ARCUS code tag 추가.

이번에 최신 libmemcached 코드를 보니, 기존 보다 최적화되어 있고 readability도 나은 것 같습니다.
만약, arcus-c-client가 기반으로 하는 libmemcached 코드를 최신 libmemcached로 이전한다면,
arcus-c-client에서 ARCUS 관련 코드에 대해 code tag 추가해 두는 것이 필요합니다.

내부 검토 후에 결정하도록 하고, 현재는 이슈로만 올려둡니다.

[CLEANUP] do_arcus_zk_close()의 정리 작업 보완

zk와 연결을 close하는 do_arcus_zk_close() 는 zookeeper_close()를 호출하여
zk client의 연결을 해제하고, arcus의 zk 관련 설정들을 초기화 한다.
이 중 arcus->is_initializing 변수는 arcus와 zk의 처음 연결 확인을 위한 변수로
zk close 하면 다음 zk connect를 위해 설정하여 connect 이후 wait 해야 함을
나타내야 한다.

관련 부분을 검토하고 반영한다.

[DOC] docs/manual 보강

github docs의 operation 매뉴얼을 보강한다.

arcus c client 매뉴얼은 주로 API에 대한 설명만 되어있는 상태로,
사용법 및 사용 후 코드 example에 대한 보강이 필요하다.

bop smget 의 key, count 제약 코드 값 수정

c-client 에서 bop smget 의 key, count 제약을 cache server 지원과 동일하도록 수정을 검토한다.

  • cache server
 /* In bop smget, max limit on the number of given keys */
 #define MAX_SMGET_KEY_COUNT     10000
 /* In bop smget, max limit on (offset+count) */
 #define MAX_SMGET_REQ_COUNT     2000
  • c-client
#define MEMCACHED_COLL_MAX_BOP_SMGET_KEY_COUNT 2000
#define MEMCACHED_COLL_MAX_BOP_SMGET_ELEM_COUNT 1000

KEY_COUNT(2000) 초과 시 에러를 리턴함.

   for (size_t i=0; i<number_of_keys; i++)
   {
     uint32_t serverkey= key_to_serverkey[i];
     lenkeys[serverkey]+= (key_length[i] + 1); // +1 for the delimeter
     numkeys[serverkey]+= 1;
     if (numkeys[serverkey] > MEMCACHED_COLL_MAX_BOP_SMGET_KEY_COUNT)
     {
       DEALLOCATE_ARRAY(ptr, key_to_serverkey);
       return memcached_set_error(*ptr, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
                                  memcached_literal_param("key size for a server should be <= MEMCACHED_COLL_MAX_BOP_SMGET_KEY_COUNT"));
     }
   }
  • java-client
  private static final int MAX_SMGET_COUNT = 1000; // server configuration is 2000.

  /**
   * Max smget key chunk size per request
   */
  public static final int DEFAULT_MAX_SMGET_KEY_CHUNK_SIZE = 500;

KEY_COUNT(500) 초과 시 잘라서 보냄.

파일 및 함수 분리를 통한 collection code 모듈화

문제점

  • LIST / SET / MAP / BTREE API 가 모두 하나의 파일 (collection.cc) 에 정의되어 있다.
  • 중복 코드를 최소화하기 위해 아래 코드와 같이 내부적으로 함수를 같이 쓰고 있다. collection type 에 따라 if~else 처리가 있으며, 이러한 형태는 코드 가독성과 유지보수성을 떨어트린다.
 static memcached_return_t do_coll_delete(memcached_st *ptr,
                                          const char *key, size_t key_length,
                                          memcached_coll_query_st *query,
                                          size_t count,
                                          bool drop_if_empty,
                                          memcached_coll_action_t verb)
 {
...
   /* Query header */
   if (verb == LOP_DELETE_OP)
   {
...
   }
   else if (verb == SOP_DELETE_OP)
   {
...
   }
   else if (verb == MOP_DELETE_OP)
   {
...
   }
   else if (verb == BOP_DELETE_OP)
   {
...
   }
}

수정방안

  • collection type 별로 파일을 분리. (list.cc / set.cc / map.cc / btree.cc)
  • 함수 모듈화 (do_coll_delete -> do_list_delete / do_set_delete ..)

cache list 변경 없다면, memcached_pool_repolulate 수행을 생략.

아래 2가지를 확인하고, 문제가 있으면 처리한다.

  • cache list 변경이 없는 시에도 do_arcus_update_cachelist() 함수가 호출되는가 ?
    • 호출된다면, 그 이유를 확인할 필요 있음.
  • cache list 변경이 없을 시에 do_arcus_update_cachelist() 함수가 호출되더라도
    • memcached_pool_repolulate()는 수행되지 않아야 함.
    • large pool에 대해 큰 부담을 발생시키고 있음.

response return에 따른 에러 처리 검토.

현재 response를 read하는 함수의 return으로,

MEMCACHED_UNKNOWN_READ_FAILURE or
MEMCACHED_PROTOCOL_ERROR or
MEMCACHED_CLIENT_ERROR or
MEMCACHED_MEMORY_ALLOCATION_FAILURE

에 대해 io reset을 진행 한다.
io reset은 현재 connection을 끊고, 2초 wait 뒤 reconnect 하는 과정을 거친다.

아래의 return에 대해서는 wait할 필요가 없지만 현재는 wait 하도록 되어 있다.

UNKNOWN_READ_FAILURE or
PROTOCOL_ERROR or
CLIENT_ERROR

에 대해서 즉시 reconnect 하도록 변경이 필요하다.
quit_server() 동작에서 몇가지 검토가 필요하다.

  • 즉시 reconnect 하도록 변경할 때 정상 동작 여부
  • ascii protocol 일 때 quit명령으로 telnet을 종료 하는데, 위 동작의 필요성.

검토 후 문제없이 동작 가능 하다면 즉시 reconnect 하도록 변경 한다.

mgets 지원

mgets 를 지원하도록 합니다.

  • mget command request function : memcached_mget_by_key() 함수 참고.
  • backward compatibility 보장을 위해 캐시 서버 버전 확인 필요. mget_command_is_supported() 함수 참고.
  • mgets test code 추가
  • command document 에 mgets 내용 추가

[TEST] piped 연산의 유닛 테스트 추가

파이프 연산시, 응답으로 PIPE_ERROR와 CLIENT_ERROR를 리턴하는 케이스에 대한 테스트 코드를 추가한다.

  • PIPE_ERROR bad error or PIPE_ERROR command overflow
    • 둘 다 추가하는 것이 쉬우면, 둘 다 추가하였으면 함.
  • CLIENT_ERROR

memcached pool scalability 이슈 해결.

memcached pool size가 큰 경우, 아래 작업은 pool의 모든 memcached에 대해 clone 작업으로 반영하며 pool size에 비례하여 긴 시간이 소요되며, 그 동안에 서비스가 불가한 상태가 된다.

  • behavior 변경
  • cahce list 변경 (by ZK notification)

Cache list 변경은 자주 발생할 수 있어서, memcached pool scalability 이슈가 실 사용에 문제 된다.
Cache list 변경에서 pool의 모든 memcached에 반영할 사항은 2가지가 있다.

  • server list (모든 cache node와의 연결 정보 유지)
  • hash ring (각 요청을 어떤 cache node로 보내야 할 지를 결정하는 매커니즘에서 필요한 정보)

위의 2가지 중에 hashring 문제는
pool에 하나의 shared hashring을 두고, 모든 memcached가 공유하여 사용하도록 수정하여
해결한 상태이다.

server list 문제는 남아 있는 상태이며,
현재 모든 server들을 close한 후에 새로운 server들을 생성하여 추가하는 방식으로 처리하고 있다.
이에 대해 아래 방식으로 개선이 필요하다.

  • 전체 server 목록 중, 변경된 server들만 빠르게 찾고,
  • 변경만 server들만 교체

캐시 서버가 mget, mgets 를 지원하는 경우에도 get, gets 를 사용하는 버그

캐시 서버가 mget, mgets 을 지원하는 버전인 경우에도 get, gets 명령으로 요청을 보낼 수 있는 버그가 있다.
아래 코드는 다수 아이템 조회 명령 처리하는 로직인데, enable_mget 이 true 인 경우에 mget, mgets 명령을 사용한다.
mget_command_is_supported()에서 캐시 서버 버전을 확인하고, 캐시 서버 버전 등록은 캐시 서버와 최초 연결할 때 수행한다. (in memcached_connect())
아직 memcached_connect() 호출하지 않은 캐시 서버는 버전이 등록되있지 않아 enable_mget 은 false 가 되어 해당 instance 에게 mget, mgets 명령을 사용하지 못한다.

   for (uint32_t x= 0; x < number_of_keys; x++)
   {
     memcached_server_write_instance_st instance;
     uint32_t server_key;

     if (is_group_key_set)
     {
       server_key= master_server_key;
     }
     else
     {
       server_key= key_to_serverkey[x];
     }

     instance= memcached_server_instance_fetch(ptr, server_key);
     bool enable_mget= mget_command_is_supported(ptr, instance);
     if (enable_mget)
     {
       if (hosts_failed[server_key])
       {
         /* The command protocol for that instance is broken. */
         continue;
       }
     }

     struct libmemcached_io_vector_st vector[]=
     {
       { 0, NULL },
       { memcached_array_size(ptr->_namespace), memcached_array_string(ptr->_namespace) },
       { key_length[x], keys[x] }
     };
     size_t veclen= 3;

     if (memcached_server_response_count(instance) == 0)
     {
       rc= memcached_connect(instance);

       if (memcached_failed(rc))
       {
         if (rc != MEMCACHED_ERRNO)
         {
           memcached_set_error(*instance, rc, MEMCACHED_AT);
         }
         hosts_failed[server_key]= true;
         continue;
       }
       hosts_connected++;

       if (enable_mget)
       {
         const char *command= (ptr->flags.support_cas ? "mgets" : "mget");
         char command_buffer[80];
         vector[0].length= snprintf(command_buffer, 80, "%s %u %u\r\n",
                                    command,
                                    lenkeys[server_key]-1,
                                    numkeys[server_key]); // -1 for the space-less first key
         vector[0].buffer= command_buffer;

         /* Sending the request header */
         write_result= memcached_io_writev(instance, vector, veclen, false);
       }
       else
       {
         const char *command= (ptr->flags.support_cas ? "gets " : "get ");
         vector[0].length= strlen(command);
         vector[0].buffer= command;

         /* Sending the request header */
         write_result= memcached_io_writev(instance, vector, veclen, false);
       }
  • 캐시 서버 버전을 확인하여 다르게 처리하는 부분인 space_separated_keys_is_supported() 쪽도 수정이 필요. do_coll_get() 에서 mop get 처리시에 mkey list string 을 미리 만들어두기 때문에 캐시 버전이 등록되지 않은 상태일 때 comma separated 방식으로 keylist 생성할 수 있음.

해결방안:

  • memcached_connect() 이후 enable_mget= mget_command_is_supported() 호출하는 코드 추가

unit test 관련:

  • 명령 처리 자체는 정상적으로 되었기 때문에 unit test 통과하였음.
  • 내부적으로 실제로 어떻게 명령을 보내었는 지까지 unit test 에서 검사해야 함.
    => memcached_st 구조체에 처리한 명령을 모두 저장해야 하므로 맞지 않아 보임.

eflag filter 값을 command string 변환 최적화

do_coll_mget() 함수에 포함된 아래 코드를 보면,

  • 별도의 buffer인 filter_str에 eflag filter의 command string 생성한 후에,
  • 원래의 command buffer에 snprintf()로 다시 copy하고 있다.
    if (query->eflag_filter)
    {
      const size_t filter_length= MEMCACHED_COLL_MAX_FILTER_STR_LENGTH+1;
      char filter_str[filter_length];
      if (memcached_coll_eflag_filter_to_str(query->eflag_filter, filter_str, filter_length) < 0)
      {
        return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
                                   memcached_literal_param("snprintf(MEMCACHED_COLL_MAX_FILTER_STR_LENGTH)"));
      }

      write_length+= snprintf(buffer+write_length, buffer_length-write_length, "%s", filter_str);
    }

한 번에 command buffer에 copy하는 것이 유리하다.

arcus_connect() 함수 호출 시 SIGABRT

arcus_connect() 함수 호출 시
아래와 같은 로그만 남기고 연결이 안됩니다.
WARN@do_arcus_zk_connect@616: Initiating zookeeper client
src/mt_adaptor.c:162: start_threads: Assertion `"pthread_create() failed for the IO thread"&&!rc' failed.

gdb로 문제가 발생한 지점에서 backtrace 해보니 아래와 같습니다. (주소와 포트는 보안상 한글로 수정)

Program received signal SIGABRT, Aborted.
0xffffe410 in __kernel_vsyscall ()
(gdb) bt full
#0  0xffffe410 in __kernel_vsyscall ()
No symbol table info available.
#1  0x00b19df0 in raise () from /lib/libc.so.6
No symbol table info available.
#2  0x00b1b701 in abort () from /lib/libc.so.6
No symbol table info available.
#3  0x00b1326b in __assert_fail () from /lib/libc.so.6
No symbol table info available.
#4  0x08226ff3 in start_threads (zh=0x97d0dd0) at src/mt_adaptor.c:162
        rc = <value optimized out>
        adaptor = 0x97d17f8
        __func__ = "start_threads"
        __PRETTY_FUNCTION__ = "start_threads"
#5  0x08227163 in adaptor_init (zh=0x97d0dd0) at src/mt_adaptor.c:203
        recursive_mx_attr = {__size = "\001\000\000", __align = 1}
        __func__ = "adaptor_init"
#6  0x0821ed1c in zookeeper_init (host=0x97d9764 "아커스서버주소:포트", 
    watcher=0x8203a9e <do_arcus_zk_watcher_global(zhandle_t*, int, int, char const*, void*)>, recv_timeout=15000, clientid=0x97d974c, 
    context=0x97ee538, flags=0) at src/zookeeper.c:793
        errnosave = <value optimized out>
        zh = 0x97d0dd0
        index_chroot = 0x0
        __func__ = "zookeeper_init"
#7  0x08202c45 in do_arcus_zk_connect (mc=0x97ee538) at libmemcached/arcus.cc:626
        arcus = 0x97d9748
        __func__ = "do_arcus_zk_connect"
#8  0x08202f31 in do_arcus_connect (mc=0x97ee538, pool=0x0, ensemble_list=0x97ce644 "아커스서버주소:포트", svc_code=0x97ce62c "SVC코드")
    at libmemcached/arcus.cc:312
        arcus = 0x97ee538
        rc = ARCUS_SUCCESS
        __func__ = "do_arcus_connect"
#9  0x082031f3 in arcus_connect (mc=0x97ee538, ensemble_list=0x97ce644 "아커스서버주소:포트", svc_code=0x97ce62c "SVC코드")
    at libmemcached/arcus.cc:99

zookeeper에서 pthread_create() 실행시 반환한 rc 값이 비정상이라 assrt 실패 된걸로 보입니다.

void start_threads  (zhandle_t * zh)
{
    struct adaptor_threads* adaptor=zh->adaptor_priv;
    pthread_cond_init(&adaptor->cond,0);
    pthread_mutex_init(&adaptor->lock,0);
    adaptor->threadsToWait=2;  // wait for 2 threads before opening the barrier

    // use api_prolog() to make sure zhandle doesn't get destroyed
    // while initialization is in progress
    api_prolog(zh);
    LOG_DEBUG(("starting threads..."));
    int rc=pthread_create(&adaptor->io, 0, do_io, zh);
    assert("pthread_create() failed for the IO thread"&&!rc); // 이부분에서 에러
    rc=pthread_create(&adaptor->completion, 0, do_completion, zh);
    assert("pthread_create() failed for the completion thread"&&!rc); 
    wait_for_others(zh);
    api_epilog(zh, 0);    
}

arcus 1.7.0 에서 arcus_connect() 함수 관련 질문

오픈소스가 아닌 1.6.3 버전에서 arcus.h 에 선언된 arcus_connect() 함수의 인자는 아래처럼 3개이고 pool 정보를 인자로 받지 않습니다만,
arcus_return_t arcus_connect(memcached_st *mc, const char *ensemble_list, const char *svc_code)

오픈소스인 1.7 버전에서 arcus.h에 선언된 arcus_connect() 함수의 인자는 4개이고 pool 정보를 인자로 받습니다.
arcus_return_t arcus_connect(memcached_st *mc, memcached_pool_st *pool, const char *ensemble_list, const char *svc_code)

왜 1.7 부터는 single thread 용인 arcus_connect() 함수에 pool 정보를 인자로 받고 있는지 궁금하며 (pool은 multi threaded/process 환경용 아닌가요?)

single thread 용인 arcus_connect() 함수에서 pool을 사용하지 않으려면 어떻게 사용해야하는지 예시를 부탁드립니다.

bop smget 명령의 매뉴얼 수정.

bop smget 명령에 대한 매뉴얼 내용이 잘못되어 있으므로, 수정이 필요합니다.
예를 들어, 다음과 같은 함수가 변경되어야 합니다.

  • memcached_bop_smget_result_get_key() => memcached_coll_smget_result_get_key()

io_flush() 에서 purge 실패 시 delayed reconnect 처리.

purge 실패는 io issue 와 관련된 것이므로, io_flush() 내에서 memcached_quit_server() 호출해 delayed reconnect 를 수행하도록 한다.

purge 함수에서 발생하는 에러 :

/* Force a flush of the buffer to ensure that we don't have the n-1 pending
  requests buffered up.. */
if (memcached_io_write(ptr, NULL, 0, true) == -1)
{
   memcached_set_purging(root, true);

   return memcached_set_error(*ptr, MEMCACHED_WRITE_FAILURE, MEMCACHED_AT);
}
memcached_return_t rc= memcached_read_one_response(ptr, buffer,
                                                        sizeof (buffer),
                                                        result_ptr);
/*
* Purge doesn't care for what kind of command results that is received.
* The only kind of errors I care about if is I'm out of sync with the
* protocol or have problems reading data from the network..
*/
if (rc == MEMCACHED_PROTOCOL_ERROR || rc == MEMCACHED_UNKNOWN_READ_FAILURE)
{
  WATCHPOINT_ERROR(rc);
  ret= rc;
  memcached_set_error(*ptr, rc, MEMCACHED_AT);
}

Compiler errors

I was trying to compile the source code, which led me to running into some compiler errors like following:

./libtest/comparison.hpp:39:18: error: ISO C++ forbids comparison between pointer and integer [-fpermissive]
   if (__expected == false)
       ~~~~~~~~~~~^~~~~~~~
make[1]: *** [Makefile:5419: libtest/libtest_unittest-unittest.o] Error 1
make[1]: Leaving directory '/home1/irteam/shawn/arcus-c-client'
make: *** [Makefile:1850: all] Error 2

I have made a quick fix and pushed it into this repository, which ended up in permission denial.
I would like you to apply this patch:

diff --git a/clients/memflush.cc b/clients/memflush.cc
index 81bb257..ac3e1d0 100644
--- a/clients/memflush.cc
+++ b/clients/memflush.cc
@@ -37,7 +37,7 @@ int main(int argc, char *argv[])
 {
   options_parse(argc, argv);

-  if (opt_servers == false)
+  if (opt_servers == NULL)
   {
     char *temp;

diff --git a/clients/memstat.cc b/clients/memstat.cc
index 8036ea1..779b73f 100644
--- a/clients/memstat.cc
+++ b/clients/memstat.cc
@@ -86,7 +86,7 @@ int main(int argc, char *argv[])
   options_parse(argc, argv);
   initialize_sockets();

-  if (opt_servers == false)
+  if (opt_servers == NULL)
   {
     char *temp;
     if ((temp= getenv("MEMCACHED_SERVERS")))
diff --git a/libtest/comparison.hpp b/libtest/comparison.hpp
index eb240d4..7366f61 100644
--- a/libtest/comparison.hpp
+++ b/libtest/comparison.hpp
@@ -36,7 +36,7 @@ namespace libtest {
 template <class T_comparable, class T_hint>
 bool _compare_truth_hint(const char *file, int line, const char *func, T_comparable __expected, const char *assertation_label,  T_hint __hint)
 {
-  if (__expected == false)
+  if (!__expected)
   {
     libtest::stream::make_cerr(file, line, func) << "Assertation  \"" << assertation_label << "\" failed, hint: " << __hint;
     return false;

io_flush() 에서 poll timeout 발생에 대한 처리 검토

c-client 는 synchronous io 방식이기 때문에 write 시에 timeout 발생은
한번에 socket buffer 를 넘는 command 를 보내지 않는 이상 발생할 확률이 거의 없겠지만,
code 안전성을 높이기 위해 handling 하도록 하고, 관련해서 아래 사항을 검토한다.

  • single line command(set, get ..) 와 multi line command(mget) 에서 command string 일부만 보내진 상황에서
    • 남은 write length 가 io write buffer 를 넘어서는 경우
    • timeout 이 발생하여 이전에 send 된 데이터와 증가된 response count 로 인한 이슈

최종적으로는 timeout 발생 시에 send 한 데이터 유무에 관계없이 connection 을 다시 맺도록 해주는 것이 가장 나아 보여 delayed reconnect 처리할 것으로 생각된다.

Ketama hash collision 시의 owner 결정

Hash collision 발생 시의 owner 결정 방식으로
hostname 비교하여 작은 hostname의 cache node가 owner가 되도록 한다.

현재 cache server, java-client는 모두 반영된 상태이고,
c-client만 남아있는 상태이다.

수백개 이상의 캐시 인스턴스를 가지는 대규모 클러스터인 경우
ketama hash collision이 발생하므로,
이러한 대규모 클러스터에서 사용하기 위해서는 수정해야 한다.

smget에서 operation timeout 시의 처리

Smget 수행에서 특정 cache node로의 요청이 operation timeout난 경우,
그 cache node로 보낸 key들을
(1) misssed keys로 처리하거나 (2) timeout keys로
그 결과를 응용에게 전달해 주는 것에 대해 검토가 필요함

[CLEANUP] arcus 구조체의 exist 확인과 제거 상황 정리

현재 arcus 구조체를 사용하는 대부분의 코드에서는 lock_arcus putex lock을 획득한 상태에서
mc의 server_manager()를 통해 가져오는 arcus에 대해 not arcus를 확인하고
not arcus인 경우 각 코드에 맞는 핸들링을 하고 있다.

c client 구동 중 arcus 구조체가 제거될 가능성이 있는 상황을 정리하고
제거될 가능성이 없는 부분 이라면 not arcus 확인과 handling 작업을 제거하는게
코드 관리와 향후 arcus 구조체를 이용한 코드 수정에 대한 복잡성을 낮출 수 있을 것으로 보인다.

[FIX] zk_global_watcher의 CONNECTED state 동작 검토

현재 zookeeper와의 session event 처리를 위한 do_arcus_zk_watcher_global()에서는
ZOO_CONNECTED_STATE 일 때 아래와 같이 항상 client_info 등록과 cachelist watcher 등록을 시도한다.

  if (state == ZOO_CONNECTED_STATE)
  {
    ZOO_LOG_WARN(("SESSION_STATE=CONNECTED, to %s", arcus->zk.ensemble_list));

    const clientid_t *id= zoo_client_id(zh);
    if (arcus->zk.myid.client_id == 0 or arcus->zk.myid.client_id != id->client_id) {
      ZOO_LOG_DEBUG(("Previous sessionid : 0x%llx", (long long) arcus->zk.myid.client_id));
      arcus->zk.myid= *id;
      ZOO_LOG_DEBUG(("Current sessionid  : 0x%llx", (long long) arcus->zk.myid.client_id));
    }
    if (arcus->zk.is_initializing) {
      if (do_arcus_cluster_validation_check(mc, arcus) < 0) {
        pthread_mutex_unlock(&lock_arcus);
        return;
      }
    }
#ifdef ARCUS_ZK_ADDING_CLEINT_INFO
    do_add_client_info(arcus);
#endif
    pthread_mutex_unlock(&lock_arcus);

    do_arcus_zk_watch_and_update_cachelist(mc, do_arcus_zk_watcher_cachelist);
  }

client info 등록은 zoo_exists()를 통해 znode가 없을 경우에만 zoo_create() 하도록 되어 있어
큰 문제는 없지만 cachelist watcher 등록은 항상 수행한다.
session이 expired 되기 전까지 session이 생성한 znode와 watcher는 유지됨이 보장되기 때문에
항상 수행할 필요가 없지만 현재는 항상 시도하는 상태이다.

zk의 session event는 zk client와 server 간 연결이 끊어질 경우 다음 zk server로 연결하는 과정에서CONNECTING -> CONNECTED 순서로 event가 발생할 수 있다.
따라서 arcus->zk 초기화 상태가 아니라면 client_info, cacheList watcher 등록 할 필요가 없다.
관련 부분을 검토하여 수정한다.

참고로, java zk의 경우 CONNECTING event 대신 Disconnected event가 발생한다.

  arcus-java-client : Cache Manager

  public void process(WatchedEvent event) {
    if (event.getType() == Event.EventType.None) {
      switch (event.getState()) {
        case SyncConnected:
          zkInitLatch.countDown();
          getLogger().info("Connected to Arcus admin. (%s@%s)", serviceCode, hostPort);
          if (cacheMonitor != null) {
            getLogger().warn("Reconnected to the Arcus admin. " + getInfo());
          } else {
            getLogger().debug("cm is null, servicecode : %s, state:%s, type:%s",
                    serviceCode, event.getState(), event.getType());
          }
          break;
        case Disconnected:
          getLogger().warn("Disconnected from the Arcus admin. Trying to reconnect. " + getInfo());
          break;
        case Expired:
          // If the session was expired, just shutdown this client to be re-initiated.
          getLogger().warn("Session expired. Trying to reconnect to the Arcus admin." + getInfo());
          if (cacheMonitor != null)
            cacheMonitor.shutdown();
          break;
      }
    }
  }

연결 재시도 설정 관련 질문

document에 보면 아래와 같은 설명이 있습니다.
MEMCACHED_BEHAVIOR_RETRY_TIMEOUT 설정을 하면 timeout 시간 후에 재연결 시도를 하는 것으로 이해됩니다. 이때 재시도 횟수는 몇 회인가요?
별도로 설정이 가능한가요?

+#### TIMEOUT 발생 시 캐시 서버로의 재연결 설정
+
+캐시 명령 수행 시, timeout이 발생한 경우에는 MEMCACHED_SERVER_TEMPORARILY_DISABLED (“SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY”) 오류가 발생하고, RETRY_TIMEOUT 후에 해당 캐시 장비로 재연결을 시도한다. 0을 지정하면 timeout이 발생할 때마다 즉시 재연결을 시도하게 된다.
+
+c +mc = memcached_create(NULL); +memcached_behavior_set(mc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, (uint64_t)timeout); +
+
+timeout 시간은 초(s) 단위이며, 캐시 명령 실행 전에 지정하면 된다. 기본 값은 MEMCACHED_SERVER_FAILURE_RETRY_TIMEOUT (2초) 이다.
+
+libmemcached에서 timeout은 두 가지 형태의 응답코드로 나타난다.
+* MEMCACHED_TIMEOUT (“A TIMEOUT OCCURRED”) : 요청을 보내던 도중 타임아웃이 발생했다.
+* MEMCACHED_IN_PROGRESS (“OPERATION IN PROCESS”) : 요청을 성공적으로 보내고 응답을 받던 도중 타임아웃이 발생했다
+
+

1.7.0에서 memcached.h에서 arcus.h를 include하고 있지 않습니다.

아래 그림의 왼쪽은 arcus 1.6.3이고, 오른쪽은 arcus 1.7.0_3 입니다.
1.7.0에서 memcached.h 헤더에 arcus.h를 include하고 있지 않아 아커스 함수들을 못찾고 있습니다.
1.6.3에서는 memcached.h 만 include하면 되었는데
1.7.0에서는 arcus.h를 직접 include 해줘야 하나요?
2014-10-27 11 31 00

ARCUS cluster size 제약 코드의 제거.

bop mget 또는 bop smget 처리에서 cluster size는 200보다 작다는 가정하에 구현된 로직이 있다.
아래의 상수 값으로 배열 크기를 정하여 처리하는 부분이 있으므로, 이를 수정해야 합니다.

  • MAX_SERVERS_FOR_COLLECTION_MGET : 200
  • MAX_SERVERS_FOR_BOP_SMGET : 200

최근에 cluster size가 300 ~ 400인 경우가 존재하기 시작하므로,
이러한 규모의 cluster에서도 동작할 수 있어야 합니다.

memcached_io_get_readable_server()에서 특정 cache node의 return 시점 검토.

memcached_io_get_readable_server() 함수에서
readable server를 return하는 경우는 두 가지가 있습니다.

  • cursor_active인 server가 하나인 경우, read 가능 여부와 관계없이 그 server를 바로 리턴
  • cursor_active인 server가 둘 이상인 경우, polling을 통해 read 가능 상태의 server를 리턴

위와 같이 서로 다르게 처리하고 있는 데,
후자의 방식으로 통일하는 게 좋지 않을 까 생각합니다.

long key support

java-client 는 long key (32000) support 를 하고 있는 상태이고, c-client 도 이를 적용하기 위한 이슈이다.
변경이 필요한 부분은 key 저장 방식, command response 모듈이다.

key 저장 방식

  • key string 저장 필드는 static array (250 size) 로 정의되어 있고, 이 필드는 result, collection_result 구조체에 들어있다.
  • result 구조체들은 memcached_st 구조체에 들어있어서, key array 크기를 늘리면 memcached_st 구조체 크기가 꽤 커진다.
  • pool 을 구성할 경우 max_pool_size 만큼 memcached_st 가 생성될 것을 고려하면, 비효율적으로 메모리를 차지할 것이다.
  • key 저장 방식을 value 와 같이 문자열 관리 구조체를 사용해서 malloc 하여 heap 에 저장하도록 변경하면 위 문제가 없지만,
    • key field type 변경이 필요해져서 응용 코드와 기존 c-client 코드 수정이 필요하다. (array -> struct // malloc, free)

command request 모듈

  • key string 을 copy 없이 바로 io 수행하므로 key 크기와 상관없다.

command response 모듈

  • key string 을 copy 하므로 static 버퍼의 크기는 key string 최대 길이에 맞게 늘려야 한다.
    • 키 제한을 2K ~ 4K 정도로 두면 버퍼의 크기를 늘리는 것으로 간단하게 해결된다.
    • 1MB 이상으로 둔다면 key string copy 없이 바로 result 구조체의 key field 에 저장하도록 처리 한다. (value 처리하듯)
      • value 와 다르게 key 는 사전에 길이를 알 수 없으므로, malloc & realloc(부족 시) 방식으로 처리 한다.

switchover peer 못 찾을 시에 handling 검토

삼중화 기능이 추가됨에 따라 SWITCHOVER/REPL_SLAVE 응답에 switchover peer 정보가 포함되는데,
어떤 문제로 인해 memcached_rgroup_switchover() 에서 peer 정보를 찾지 못하여도
do_action 으로 goto 되어 계속 요청을 보내게 됩니다.
memcached_rgroup_switchover() 에서 peer 정보를 찾지 못하면 에러 메세지 출력 및 -1 를 리턴하고,
이 경우에 SWITCHOVER or REPL_SLAVE 에러코드를 응용에 리턴해주는 것이 안전해보입니다.

 #ifdef ENABLE_REPLICATION
 do_action:
 #endif
   /* Send command header */
   rc= memcached_vdo(instance, vector, 6, to_write);

   if (rc == MEMCACHED_SUCCESS)
   {
     if (to_write == false)
     {
       rc= MEMCACHED_BUFFERED;
     }
     else if (ptr->flags.no_reply or ptr->flags.piped)
     {
       rc= MEMCACHED_SUCCESS;
     }
     else
     {
       char result[MEMCACHED_DEFAULT_COMMAND_SIZE];
       rc= memcached_coll_response(instance, result, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL);
 #ifdef ENABLE_REPLICATION
       if (rc == MEMCACHED_SWITCHOVER or rc == MEMCACHED_REPL_SLAVE)
       {
         ZOO_LOG_INFO(("Switchover: hostname=%s port=%d error=%s",
                       instance->hostname, instance->port, memcached_strerror(ptr, rc)));
         memcached_rgroup_switchover(ptr, instance);
         instance= memcached_server_instance_fetch(ptr, server_key);
         goto do_action;
       }
 #endif
       memcached_set_last_response_code(ptr, rc);

memcached_io_get_readable_server()에서 NULL 리턴 경우의 처리 수정

memcached_io_get_readable_server()에서 NULL 리턴하는 경우는 두 가지 입니다.

  • cursor_active인 cache node가 없는 경우
  • cursor_active인 cache node가 둘 이상인 상황에서 read event에 대한 polling이 timeout 되는 경우

전자 경우는 코드의 구현 로직 상 발생하지 않아야 하는 합니다.
따라서, 전자는 assert()로 처리하는 것이 좋을 것 같습니다.

후자 경우는 결국 operation timeout 난 경우인 데,
이 경우, MEMCACHED_TIMEOUT 오류를 명시적으로 리턴하는 것이 좋을 것 같습니다.

fetch_value_header() 리턴 값 처리의 오류 수정

fetch_value_header()는 response message를 receive하는 버퍼에서
아래 2가지 문자 중 하나가 나올 때가지의 연속된 문자열을 읽어 string으로 변환하여 가져오고
2가지를 구분하여 아래 값을 리턴한다.

  • ' ' : space 발견하면
    • MEMCACHED_SUCCESS 리턴
  • "\r\n" : end of line를 발견하면
    • MEMCACHED_END 리턴

예를 들어, b+tree element 값이 아래 response message로 리턴된다고 가정하자.

<bkey> <bytes> <data>\r\n

이 경우, 각 token string을 fetch_value_header() 함수로 얻을 떄 리턴되는 값은 아래와 같이 다르다.

  • bkey와 bytes string 얻는 경우, MEMCACHED_SUCCESS 리턴
  • data string 얻는 경우, MEMCACHED_END 리턴

MEMCACHED_SUCCESS가 리턴되어야 할 경우에서 MEMCACHED_END가 리턴되거나
MEMCACHED_END가 리턴되어야 할 경우에서 MEMCAHCED_SUCCESS가 리턴된다면,
이는 오류이다.

이러한 오류 상태를 검사하고 잘못된 결과가 리턴된다면
MEMCACHED_PROTOCOL_ERROR를 리턴하는 것이 올바를 처리이다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.