분류

멤캐시드용 스토리지 엔진 직접 작성하기, 2부

이전 블로그 게시물에서 엔진 초기화와 소멸에 대해 설명했습니다. 이번 블로그 게시물에서는 엔진 인터페이스의 메모리 할당 모델을 다룹니다.

멤캐시드 코어는 연결에 필요한 모든 메모리(송수신 버퍼 등)를 할당하고 엔진은 항목을 추적하는 데 필요한 모든 메모리를 할당(및 해제)할 책임이 있습니다. 엔진은 코어가 할당(및 사용)하는 메모리에 신경 쓸 필요가 없지만, 코어는 엔진이 관리하는 메모리에 액세스합니다.

멤캐시드 코어가 새 항목을 저장하려고 할 때 해당 항목의 데이터를 저장할 버퍼를 (현재 기준으로는 계속) 확보해야 합니다. 코어는 이 버퍼를 할당하려고 할 때 할당 함수를 호출할 수 있습니다. 이제 예제 코드를 직접 구현한 할당 함수를 추가합니다. 가장 먼저 해야 할 일은 반환하는 엔진 설명자에 이 함수를 추가하는 것입니다. create_instance. 오늘의 항목에 여러 가지 함수를 추가할 예정이므로 이왕 하는 김에 모든 함수를 매핑해 보겠습니다:

멤캐시드_공개_API
ENGINE_ERROR_CODE create_instance(uint64_t 인터페이스,
                                  GET_SERVER_API GET_SERVER_API,
                                  ENGINE_HANDLE **핸들)
{
   [ ... 컷 ... ]
  /*
   * API 진입점을 이를 구현하는 함수에 매핑합니다.
   */
   h->engine.initialize = fs_initialize;
   h->engine.destroy = fs_destroy;
   h->engine.get_info = fs_get_info;
   h->engine.allocate = fs_allocate;
   h->engine.remove = fs_item_delete;
   h->engine.release = fs_item_release;
   h->engine.get = fs_get;
   h->engine.get_stats = fs_get_stats;
   h->engine.reset_stats = fs_reset_stats;
   h->engine.store = fs_store;
   h->engine.flush = fs_flush;
   h->engine.unknown_command = fs_unknown_command;
   h->engine.item_set_cas = fs_item_set_cas;
   h->engine.get_item_info = fs_get_item_info;

     

다음으로 해야 할 일은 필요한 정보를 보관할 데이터 구조를 만드는 것입니다. 이 튜토리얼의 목적은 메모리 효율적인 구현을 만드는 것이 아니라 API를 실습하는 것입니다. 따라서 다음 구조체를 만들어 보겠습니다:

구조체 fs_item {
   void *key;
   size_t nkey;
   void *데이터;
   size_t ndata;
   int 플래그;
   REL_TIME_T EXPTIME;
};
     

다음을 구현합니다. 할당 가 어떻게 보일까요?

 

static ENGINE_ERROR_CODE fs_allocate(ENGINE_HANDLE* handle,
                                     const void* 쿠키,
                                     항목 **항목,
                                     const void* 키,
                                     const size_t nkey,
                                     const size_t n바이트,
                                     const int 플래그,
                                     const rel_time_t exptime)
{
   구조체 fs_item *it = malloc(sizeof(구조체 fs_item));
   if (it == NULL) {
      엔진_이름을 반환합니다;
   }
   it->flags = 플래그;
   IT->EXPTIME = EXPTIME;
   it->nkey = nkey;
   it->ndata = n바이트;
   it->key = malloc(nkey);
   it->data = malloc(nbytes);
   if (it->key == NULL || it->data == NULL) {
      free(it->key);
      FREE(IT->DATA);
      free(it);
      엔진_이름을 반환합니다;
   }
   memcpy(it->key, key, nkey);
   *아이템 = 그것;
   엔진_성공을 반환합니다;
}
     

위의 구현을 보면 데이터 저장을 위한 실제 메모리에 대한 포인터를 멤캐시드 코어에 반환하지 않았음을 알 수 있습니다. 해당 주소를 얻으려면 memcached는 get_item_info 를 사용할 수 있습니다. 이제 이를 구현해 보겠습니다:

static void fs_get_item_info(ENGINE_HANDLE *handle, const void *cookie,
                             const item* item, item_info *item_info)
{
   구조체 fs_item* it = (구조체 fs_item*)item;
   if (item_info->nvalue < 1) {
      거짓을 반환합니다;
   }

   item_info->cas = 0; /* 지원되지 않음 */
   item_info->clsid = 0; /* 지원되지 않음 */
   item_info->exemptime = it->exemptime;
   item_info->flags = it->flags;
   item_info->key = it->key;
   item_info->nkey = it->nkey;
   item_info->nbytes = it->ndata; /* 아이템 데이터의 총 길이 */
   item_info->nvalue = 1; /* 사용된 조각 수 */
   item_info->value[0].iov_base = it->데이터; /* 조각 1에 대한 포인터 */
   item_info->value[0].iov_len = it->ndata; /* 조각 1의 길이 */

   참을 반환합니다;
}
     

그리고 get_item_info 함수가 중요하고 더 많은 정보를 제공할 필요가 있다고 생각했습니다. 엔진 API를 살펴보면 "항목"은 보이드 포인터로 정의되어 있으며, 항목별로 필요한 정보를 추적하기 위해 자체 항목 구조를 정의했습니다. 그러나 멤캐시드 코어는 다음 사항을 알아야 합니다.
키에 대한 메모리를 읽거나 쓸 위치 및 캐비닛과 주고받는 데이터의 위치를 지정합니다. 이렇게 하려면 get_item_info. 의 구현을 자세히 살펴보면 fs_get_item_info 내가 가장 먼저 하는 일은 item_info->nvalue 1 이상 포함
요소입니다. 지금은 항상 하나로 통합하는 것이 아니라, 분산된 IO를 지원하겠다는 취지입니다.

코어가 유선으로 받은 데이터를 아이템으로 옮기는 작업이 완료되면, 엔진에 아이템을 저장하려고 시도합니다. store. 이제 간단한 구현을 만들어 보겠습니다(튜토리얼의 뒷부분에서 확장해 보겠습니다):

static ENGINE_ERROR_CODE fs_store(ENGINE_HANDLE* handle,
                                  const void *쿠키,
                                  항목* 항목,
                                  uint64_t *cas,
                                  엔진_스토어_운영 작업,
                                  UINT16_T VBUCKET)
{
   구조체 FS_ITEM* IT = 항목;
   문자 fname[it->nkey + 1];
   memcpy(fname, it->key, it->nkey);
   fname[it->nkey] = '
   FILE *fp = fopen(fname, "w");
   if (fp == NULL) {
      ENGINE_NOT_STORED를 반환합니다;
   }
   size_t nw = fwrite(it->data, 1, it->ndata, fp);
   fclose(fp);
   if (nw != it->ndata) {
      remove(fname);
      ENGINE_NOT_STORED를 반환합니다;
   }

   *cas = 0;
   엔진_성공을 반환합니다;
}
     

위의 구현을 보면 다음과 같은 경우 올바른 시맨틱을 구현하지 않는다는 것을 알 수 있습니다. 추가/대체/set 등을 사용하면 파일 IO를 수행하는 동안 멤캐시가 차단됩니다. 이 부분은 나중에 다시 설명할 테니 지금은 신경 쓰지 마세요.

코어가 할당된 아이템 사용을 완료하면, 할당된 아이템이 완료되면 -release 함수를 호출할 수 있습니다. 현재 엔진은 아이템 저장소를 다른 용도로 재사용할 수 있습니다. 따라서 -release 구현:

static void fs_item_release(ENGINE_HANDLE* handle,
                            const void *쿠키,
                            항목* 항목)
{
   구조체 fs_item *it = item;
   free(it->key);
   FREE(IT->DATA);
   free(it);
}
     

이제 엔진에 항목을 성공적으로 저장하기 위한 코드를 모두 만들었지만 그 중 어떤 것도 다시 읽을 수 없습니다. 따라서 get

static ENGINE_ERROR_CODE fs_get(ENGINE_HANDLE* handle,
                                const void* 쿠키,
                                항목** 항목,
                                const void* 키,
                                const int nkey,
                                UINT16_T VBUCKET)
{

   문자 fname[nkey + 1];
   memcpy(fname, key, nkey);
   fname[nkey] = '

   구조체 stat st;
   if (stat(fname, &st) == -1) {
      ENGINE_NOT_STORED를 반환합니다;
   }

   구조체 fs_item* it = NULL;
   엔진 오류 코드 ret = fs_allocate(핸들, 쿠키, (void**)&it, 키, nkey),
                                       st.st_size, 0, 0);
   if (ret != ENGINE_SUCCESS) {
      엔진_이름을 반환합니다;
   }

   FILE *fp = fopen(fname, "r");
   if (fp == NULL) {
      fs_release(handle, cookie, it);
      엔진_실패 반환;
   }

   size_t nr = fread(it->data, 1, it->ndata, fp);
   fclose(fp);
   if (nr != it->ndata) {
      fs_release(handle, cookie, it);
      엔진_실패 반환;
   }

   *아이템 = 그것;
   엔진_성공을 반환합니다;
}
     

나머지 API에 대한 더미 구현을 추가하고 엔진을 로드하고 테스트해 보겠습니다:

static const engine_info* fs_get_info(ENGINE_HANDLE* handle)
{
   정적 엔진_정보 정보 = {
      .description = "파일 시스템 엔진 v0.1",
      .num_features = 0
   };

   반환 &info;
}

static ENGINE_ERROR_CODE fs_item_delete(ENGINE_HANDLE* handle,
                                        const void* 쿠키,
                                        const void* 키,
                                        const size_t nkey,
                                        UINT64_t CAS,
                                        UINT16_T VBUCKET)
{
   엔진_키_엔오엔트를 반환합니다;
}

static ENGINE_ERROR_CODE fs_get_stats(ENGINE_HANDLE* handle,
                                      const void* 쿠키,
                                      const char* stat_key,
                                      int nkey,
                                      ADD_STAT 추가_스탯)
{
   엔진_성공을 반환합니다;
}

static ENGINE_ERROR_CODE fs_flush(ENGINE_HANDLE* handle,
                                  const void* cookie, time_t when)
{

   엔진_성공을 반환합니다;
}

static void fs_reset_stats(ENGINE_HANDLE* handle, const void *cookie)
{

}

static ENGINE_ERROR_CODE fs_unknown_command(ENGINE_HANDLE* handle,
                                            const void* 쿠키,
                                            프로토콜_바이너리_요청_헤더 *요청,
                                            ADD_RESPONSE 응답)
{
   엔진_엔오츠업을 반환합니다;
}

static void fs_item_set_cas(ENGINE_HANDLE *handle, const void *cookie,
                            item* item, uint64_t val)
{
}

     

이제 엔진을 사용해 보겠습니다:

trond@opensolaris> /opt/memcached/bin/memcached -E .libs/fs_engine.so
     

다른 터미널에서 입력 중입니다:

트론드@오픈솔라리스> 텔넷 로컬호스트 11211
시도 중 ::1...
오픈솔라리스에 연결되었습니다.
이스케이프 문자는 '^]'입니다.
추가 테스트 0 0 4
테스트
저장됨
테스트 받기
VALUE 테스트 0 4
테스트
END
종료
외국 호스트에 의해 스톰 연결이 차단되었습니다.
     

를 눌러 멤캐시드를 종료합니다. ctrl-c를 클릭하고 현재 디렉토리를 찾습니다:

트론드@오픈솔라리스> ls -l 테스트
-rw-r-r- 1 트론 사용자 6 Oct 8 12:56 테스트
트론드@오픈솔라리스> 고양이 테스트
테스트
     

여기까지입니다.

이 문서 공유하기
받은 편지함에서 카우치베이스 블로그 업데이트 받기
이 필드는 필수 입력 사항입니다.

작성자

게시자 트론드 노비, 선임 개발자, Couchbase

트론드 노비는 카우치베이스의 소프트웨어 아키텍트입니다. Couchbase 및 Memcached 프로젝트의 핵심 기여자입니다. C/C++ 및 node.js Couchbase 클라이언트 라이브러리를 만들었습니다.

댓글 남기기

카우치베이스 카펠라를 시작할 준비가 되셨나요?

구축 시작

개발자 포털에서 NoSQL을 살펴보고, 리소스를 찾아보고, 튜토리얼을 시작하세요.

카펠라 무료 사용

클릭 몇 번으로 Couchbase를 직접 체험해 보세요. Capella DBaaS는 가장 쉽고 빠르게 시작할 수 있는 방법입니다.

연락하기

카우치베이스 제품에 대해 자세히 알고 싶으신가요? 저희가 도와드리겠습니다.