Slot 탐색 및 Page 저장은 Slot 탐색을 진행하고, Page를 해당 순서의 Slot에 저장하는 메커니즘이다.
Block의 index는 순환적이다.
해당 케이스는 slot의 index를 찾기 전에 해당 block에 다른 thread가 write 작업을 진행 중일 때 나타나는 현상이다.
여기서 write 작업이란 다른 thread가 해당 block에 대해 slot 탐색 ~ DWB flush 사이의 작업이 진행중임을 의미한다.
Flush 순서 및 대기
wait_queue
에 work thread를 저장시키고 대기dwb_aquire_next_slot()
함수 내부 start
goto phase부터 다시 진행position_with_flags
에서 현재 block의 상태를 writing으로 바꾼 다음 다시 slot의 index 찾기 작업 진행dwb_add_page
/*
* dwb_add_page () : DWB에 페이지 컨텐츠 추가
*
* 반환 : Error code
* thread_p (인자) : Thread entry
* io_page_p(인자) : 페이지의 현재 내용이 있는 메모리 내 주소
* vpid (인자) : 페이지 식별자
* p_dwb_slot (인자) : 페이지 콘텐츠를 추가해야 하는 DWB slot
*
* ※ Flush thread를 사용할 수 없거나 독립 실행형(stand alone)인 경우 thread가 block을 flush할 수 있다.
*/
int dwb_add_page(THREAD_ENTRY *thread_p, FILEIO_PAGE *io_page_p, VPID *vpid, DWB_SLOT **p_dwb_slot)
{
unsigned int count_wb_pages; // write buffer에 추가된 페이지 수
int error_code = NO_ERROR; // #define NO_ERROR 0
bool inserted = false; // DWB에 slot이 잘 추가되었는지 확인하는 bool 타입 변수
DWB_BLOCK *block = NULL; // block 포인터
DWB_SLOT *dwb_slot = NULL; // slot 포인터
bool needs_flush; // flush가 필요한지 확인하는 bool 타입 변수 (count_wb_pages >= DWB 각 block의 page 수 → flush)
assert(p_dwb_slot != NULL && (io_page_p != NULL || (*p_dwb_slot)->io_page != NULL) && vpid != NULL);
// 위 조건에 만족하지 않으면 crash
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info();
// 인자로 thread_p가 제대로 들어오지 않았다면 위 함수를 통해 자체적으로 thread entry 구함
}
if (*p_dwb_slot == NULL) // *p_dwb_slot이 NULL이라면
{
error_code = dwb_set_data_on_next_slot(thread_p, io_page_p, true, p_dwb_slot);
// 가능한 경우, next DWB slot에 데이터 set / error code 설정
// dwb_acquire_next_slot()에서 position_with_flags에서 현재 block num, 다음 slot의 위치를 구함
// 현재 block의 slot pointer + 구한 slot의 index로 slot의 pointer를 구하고 p_dwb_slot에 대입
// dwb_set_slot_data()에서 p_dwb_slot에 io_page_p를 입력
if (error_code != NO_ERROR)
{
return error_code;
// 위 함수 안에서 에러 발생 시 add_page 중단 / error_code 반환
}
if (*p_dwb_slot == NULL)
{
return NO_ERROR;
// 위 함수를 거치고도 아직 *p_dwb_slot이 NULL이라면 add_page 중단 / NO_ERROR 반환
}
}
dwb_slot = *p_dwb_slot;
함수 내에서 선언한 dwb_slot 포인터 변수에 *p_dwb_slot 대입
assert(VPID_EQ(vpid, &dwb_slot->vpid));
// #define VPID_EQ(vpid_ptr1, vpid_ptr2) ((vpid_ptr1) == (vpid_ptr2) || ((vpid_ptr1)->pageid == (vpid_ptr2)->pageid && (vpid_ptr1)->volid == (vpid_ptr2)->volid))
// vipd와 slot->vpid를 비교하는 이유는 dwb_set_slot_data() 함수에서 입력 과정에서 error가 발생됐는지 check하기 위함으로 추측됨
// vpid와 &dwb_slot->vpid가 일치하지 않으면 crash
if (!VPID_ISNULL(vpid)) // vpid가 NULL이 아니면
{
error_code = dwb_slots_hash_insert(thread_p, vpid, dwb_slot, &inserted);
// slots hash에 삽입 진행 / error code 설정
if (error_code != NO_ERROR)
{
return error_code;
// 위 함수 안에서 에러 발생 시 add_page 중단 / error_code 반환
}
if (!inserted) // 삽입 진행 후에도 inserted 변수가 아직 false라면
{
VPID_SET_NULL(&dwb_slot->vpid);
// 동일한 데이터를 두 번 flush하지 않도록 slot을 NULL로 세팅해 무효화 시킴
fileio_initialize_res(thread_p, dwb_slot->io_page, IO_PAGESIZE);
// LSA를 NULL로 세팅 / io_page 구조체 내의 변수들 default값으로 초기화
}
}
dwb_log("dwb_add_page: added page = (%d,%d) on block (%d) position (%d)\n", vpid->volid, vpid->pageid,
dwb_slot->block_no, dwb_slot->position_in_block);
/*
* #define dwb_log(...) \
* if (dwb_Log) \
* _er_log_debug(ARG_FILE_LINE, "DWB: " __VA_ARGS__)
*/
// dwb_Log가 설정되어있으면 error log file에 인자로 넘긴 메세지 출력
block = &dwb_Global.blocks[dwb_slot->block_no];
// 함수 내에서 선언한 block 포인터 변수에 DWB 전역 구조체 내의 블록 배열의 slot의 block 번호 주소 대입
count_wb_pages = ATOMIC_INC_32(&block->count_wb_pages, 1);
// 함수 내에서 선언한 count_wb_pages에 ATOMIC_INC_32로 &block->count_wb_pages 대입
// write_buffer의 page가 1개 추가되었으므로 +1 해서 대입
assert_release(count_wb_pages <= DWB_BLOCK_NUM_PAGES);
// count_wb_pages가 DWB 각 block의 page 수보다 크면 crash
// assert()와 assert_release()의 차이는..?
if (count_wb_pages < DWB_BLOCK_NUM_PAGES) // count_wb_pages가 DWB 각 block의 page 수보다 작으면
{
needs_flush = false;
// flush 불필요
}
else
{
needs_flush = true;
// flush 필요
}
if (needs_flush == false)
{
return NO_ERROR;
// flush 할 필요 없으면 add_page 중단 / NO_ERROR 반환
}
/*
* 궁금한점
* 위의 과정을 진행하는 이유는 block이 가득찼을 경우에만 flush하는 것인데 왜 needs_flush를 사용해야 할까?
* 그냥 첫 if문에서 page 수보다 작으면 return (NO_ERROR)로 빠져나오면 될텐데 굳이 needs_flush 변수를 사용해야 할까?
* needs_flush가 다른 부분에서 사용되면 모르겠는데 여기서만 사용됨
*/
/*
* Block은 일관된 데이터를 갖기 위해 채워진 순서대로 flush 되어야 한다.
* Flush block thread는 채워진 순서대로 block을 flush하는 방법을 알고 있다.
* 따라서 여기에선 flushing 순서에 대해 더 이상 신경 쓰지 않는다.
* 최초에는 이전 block이 flush되지 않은 경우 여기에서 기다렸었다. 이러한 접근 방식으로 인해 지연이 발생됐었다.
*/
// SEVER_MODE로 실행됐을 경우에만 진행
#if defined(SERVER_MODE)
/*
* 현재 block을 flush하기 위해 flush block thread를 깨운다.
* 현재 block은 이전 block을 flush한 후 flush된다.
*/
if (dwb_is_flush_block_daemon_available()) // flush block daemon이 가능한 상태이면
{
dwb_flush_block_daemon->wakeup();
// block을 flush할 thread를 깨움
return NO_ERROR;
}
#endif /* SERVER_MODE */
error_code = dwb_flush_block(thread_p, block, false, NULL);
// 현재 block에서 모든 pages flush / error code 설정
if (error_code != NO_ERROR)
{
dwb_log_error("Can't flush block = %d having version %lld\n", block->block_no, block->version);
return error_code;
// flush 실패 시 log에 에러 메세지 남기고 error code 반환
}
dwb_log("Successfully flushed DWB block = %d having version %lld\n", block->block_no, block->version);
return NO_ERROR;
// flush 성공 시 해당 메세지를 log에 성공 메세지 남기고 함수 종료
}
thread_get_thread_entry_info
inline cubthread::entry *
thread_get_thread_entry_info (void)
{
cubthread::entry &te = cubthread::get_entry ();
return &te;
}
typedef struct double_write_buffer DOUBLE_WRITE_BUFFER;
struct double_write_buffer
{
unsigned int num_blocks;
> 블록의 전체 개수
unsigned int num_pages;
> 페이지의 전체 개수
unsigned int num_block_pages;
> 블록당 페이지 수
unsigned int log2_num_block_pages;
> log2(블록당 페이지 수)
pthread_mutex_t mutex;
> wait queue 변경 시 사용될 lock
DWB_WAIT_QUEUE wait_queue;
> wait queue
UINT64 volatile position_with_flags;
}
num_blocks(블록의 전체 개수) 와 num_pages(페이지의 전체 개수) 는 모두 2^n
IO_PAGESIZE = 16 KB 기준
32 <= num_pages <= 2048
32 <= 2^(5~11) <= 2048
1 <= num_blocks <= 32
1 <= 2^(0~5) <= 32
num_block_pages = num_pages(2^a) / num_blocks(2^b)
값이 64인 변수를 예로 들면
log2 64 = 6
64:
0100 0000
64 >> log2 64 = 0000 0001
dwb_set_data_on_next_slot (thread_p, io_page_p, can_wait, p_dwb_slot)
=> dwb_add_page
=> pgbuf_bcb_flush_with_wal 또는 fileio_write_or_add_to_dwb 또는 dwb_flush_force
dwb_set_data_on_next_slot (thread_p, io_page_p, can_wait, p_dwb_slot)
=> pgbuf_bcb_flush_with_wal
현재 슬롯을 반환해 값을 넣고 다음 슬롯이 있다면 다음 슬롯을 물려줌
return : 에러 코드
io_page_p : 슬롯에 놓게 될 데이터
can_wait : 처리 도중 대기가 허용되는 지. true이면 허용
p_dwb_slot : 슬롯 포인터의 포인터
int
dwb_set_data_on_next_slot (THREAD_ENTRY * thread_p, FILEIO_PAGE * io_page_p, bool can_wait, DWB_SLOT ** p_dwb_slot)
{
int error_code;
assert (p_dwb_slot != NULL && io_page_p != NULL);
error_code = dwb_acquire_next_slot (thread_p, can_wait, p_dwb_slot);
> 페이지를 놓을 슬롯을 찾아 *p_dwb_slot 에 넣어줍니다
if (error_code != NO_ERROR)
{
return error_code;
}
assert (can_wait == false || *p_dwb_slot != NULL);
> can_wait가 false 이거나,
> can_wait가 true이면서 *p_dwb_slot이 NULL은 아니어야 함
if (*p_dwb_slot == NULL)
{
return NO_ERROR;
}
> can_wait가 false이고, *p_dwb_slot이 NULL인 경우 처리에 사용할 슬롯을 찾을 수 없었던 것이므로 종료
dwb_set_slot_data (thread_p, *p_dwb_slot, io_page_p);
> 찾은 슬롯에 페이지 놓기
return NO_ERROR;
}
값을 넣을 슬롯을 가져옵니다
return : 에러 코드
can_wait : 처리 도중 대기가 허용되는 지. true이면 허용
p_dwb_slot : 슬롯 포인터의 포인터
STATIC_INLINE int
dwb_acquire_next_slot (THREAD_ENTRY * thread_p, bool can_wait, DWB_SLOT ** p_dwb_slot)
{
UINT64 current_position_with_flags, current_position_with_block_write_started, new_position_with_flags;
unsigned int current_block_no, position_in_current_block;
int error_code = NO_ERROR;
DWB_BLOCK *block;
assert (p_dwb_slot != NULL);
*p_dwb_slot = NULL;
start:
> 시작점
current_position_with_flags = ATOMIC_INC_64 (&dwb_Global.position_with_flags, 0ULL);
> current_position_with_flags = dwb_Global.position_with_flags
if (DWB_NOT_CREATED_OR_MODIFYING (current_position_with_flags))
> dwb가 만들어지지 않았고, 만들어지는 중이라면
{
/* Rarely happens. */
if (DWB_IS_MODIFYING_STRUCTURE (current_position_with_flags))
> 만들어지는 중이라면
{
if (can_wait == false)
> (dwb가 만들어질 때까지) 대기가 불가능하다면
{
return NO_ERROR;
> 종료
}
error_code = dwb_wait_for_strucure_modification (thread_p);
> dwb 가 만들어질 때까지 대기
if (error_code != NO_ERROR)
> 에러 발생
{
if (error_code == ER_CSS_PTHREAD_COND_TIMEDOUT)
{
goto start;
> 타임아웃일 경우 위에서부터 재시도
}
return error_code;
> 다른 에러일 경우 에러 반환
}
goto start;
> 다시 제일 위 조건부터 플래그 검증
}
else if (!DWB_IS_CREATED (current_position_with_flags))
> 만들어지지 않았으면
{
if (DWB_IS_ANY_BLOCK_WRITE_STARTED (current_position_with_flags))
> 블록이 하나라도 WRITE_STARTED 상태라면 (0~31 번째 비트 중 하나라도 SET 이라면)
{
/* Someone deleted the DWB, before flushing the data. */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DWB_DISABLED, 0);
return ER_DWB_DISABLED;
> 에러처리
}
/* Someone deleted the DWB */
return NO_ERROR;
> DWB 가 사용불가하므로 종료
}
else
{
assert (false);
}
}
current_block_no = DWB_GET_BLOCK_NO_FROM_POSITION (current_position_with_flags);
> current_block_no = (current_position_with_flags & DWB_POSITION_MASK) >> dwb_Global.log2_num_block_pages
position_in_current_block = DWB_GET_POSITION_IN_BLOCK (current_position_with_flags);
> position_in_current_block = current_position_with_flags & DWB_POSITION_MASK & (dwb_Global.num_block_pages - 1)
ex)
num_block_pages: 64
log2_num_block_pages: 6
current_position_with_flags & DWB_POSITION_MASK 을 통해 block 번호와 slot index를 알아낼 수 있음
해당 값이 0 이면 블록 번호 0 // 64 = 0, 슬롯 인덱스 0 % 64 = 0
해당 값이 65 이면 블록 번호 65 // 64 = 1, 슬롯 인덱스 65 % 64 = 1
이 과정을 비트로 나타내면 아래와 같습니다
current_position_with_flags & DWB_POSITION_MASK => 1이라고 가정 => 00 0000 0000 0000 0000 0000 0000 0001
블록 번호: 0000 0001 >> 6 = 0
슬롯 인덱스: 0000 0001 & 63 = 0000 0001 & 0011 1111 = 1
current_position_with_flags & DWB_POSITION_MASK => 65라고 가정 => 00 0000 0000 0000 0000 0000 0100 0001
블록 번호: 0100 0001 >> 6 = 1
슬롯 인덱스: 0100 0001 & 0011 1111 = 1
assert (current_block_no < DWB_NUM_TOTAL_BLOCKS && position_in_current_block < DWB_BLOCK_NUM_PAGES);
if (position_in_current_block == 0)
> 처리하게 될 슬롯이 0번째 슬롯일 때
{
/* This is the first write on current block. Before writing, check whether the previous iteration finished. */
if (DWB_IS_BLOCK_WRITE_STARTED (current_position_with_flags, current_block_no))
> current_position_with_flags의 MSB에서 current_block_no 번째 비트가 SET 되어 있으면 =
> current_block_no 블록이 쓰기 작업 중이면
{
if (can_wait == false)
{
return NO_ERROR;
> 대기가 불가능하면 종료
}
dwb_log ("Waits for flushing block=%d having version=%lld) \n",
current_block_no, dwb_Global.blocks[current_block_no].version);
error_code = dwb_wait_for_block_completion (thread_p, current_block_no);
> 버퍼가 덮어씌워지는 것을 피하기 위해서 이전 쓰기 작업이 끝날 때까지 대기
> dwb_wait_for_strucure_modification 와 체크 플래그만 다르고 나머진 동일
if (error_code != NO_ERROR)
{
if (error_code == ER_CSS_PTHREAD_COND_TIMEDOUT)
{
goto start;
> timeout의 경우 처음부터 재시도
}
dwb_log_error ("Error %d while waiting for flushing block=%d having version %lld \n",
error_code, current_block_no, dwb_Global.blocks[current_block_no].version);
return error_code;
> 다른 에러의 경우 에러처리
}
goto start;
> 대기가 끝난 이후 플래그 검토를 위해 처음부터 시작
}
assert (!DWB_IS_BLOCK_WRITE_STARTED (current_position_with_flags, current_block_no));
current_position_with_block_write_started =
DWB_STARTS_BLOCK_WRITING (current_position_with_flags, current_block_no);
> current_position_with_block_write_started = current_position_with_flags의 MSB에서 current_block_no 번째 비트를 SET
new_position_with_flags = DWB_GET_NEXT_POSITION_WITH_FLAGS (current_position_with_block_write_started);
> new_position_with_flags = slot의 index가 (num_pages - 1) 과 같다면 0, 아니라면 slot을 1 증가하고 플래그를 반환합니다
> 위 방식으로 슬롯을 순회합니다
}
else
{
assert (DWB_IS_CREATED (dwb_Global.position_with_flags));
assert (!DWB_IS_MODIFYING_STRUCTURE (dwb_Global.position_with_flags));
new_position_with_flags = DWB_GET_NEXT_POSITION_WITH_FLAGS (current_position_with_flags);
> new_position_with_flags = slot의 index가 (num_pages - 1) 과 같다면 0, 아니라면 slot을 1 증가하고 플래그를 반환합니다
> 위 방식으로 슬롯을 순회합니다
}
if (!ATOMIC_CAS_64 (&dwb_Global.position_with_flags, current_position_with_flags, new_position_with_flags))
> 만약 다른 스레드에서 처리가 이뤄져 플래그가 바뀌었다면, 다시 처음부터 시작합니다.
> 그게 아니라면 dwb_Global.position_with_flags에 새 플래그를 대입합니다
{
goto start;
}
block = dwb_Global.blocks + current_block_no;
> block = 포인터 dwb_Global.blocks에 현재 블록 번호를 더한 포인터
*p_dwb_slot = block->slots + position_in_current_block;
> *p_dwb_slot = 해당 블록의 슬롯 포인터에 슬롯 위치를 더한 포인터
/* Invalidate slot content. */
VPID_SET_NULL (&(*p_dwb_slot)->vpid);
assert ((*p_dwb_slot)->position_in_block == position_in_current_block);
return NO_ERROR;
>
}
STATIC_INLINE DWB_WAIT_QUEUE_ENTRY *
dwb_make_wait_queue_entry (DWB_WAIT_QUEUE * wait_queue, void *data)
{
DWB_WAIT_QUEUE_ENTRY *wait_queue_entry;
assert (wait_queue != NULL && data != NULL);
if (wait_queue->free_list != NULL)
{
wait_queue_entry = wait_queue->free_list;
wait_queue->free_list = wait_queue->free_list->next;
wait_queue->free_count--;
}
> freelist 가 존재한다면 freelist에서 가져와서 사용
else
{
wait_queue_entry = (DWB_WAIT_QUEUE_ENTRY *) malloc (sizeof (DWB_WAIT_QUEUE_ENTRY));
> 없다면 직접 할당
if (wait_queue_entry == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DWB_WAIT_QUEUE_ENTRY));
return NULL;
}
> 에러처리
}
wait_queue_entry->data = data;
> data는 스레드 포인터
wait_queue_entry->next = NULL;
return wait_queue_entry;
> 블록 초기화 및 반환
}
STATIC_INLINE DWB_WAIT_QUEUE_ENTRY *
dwb_block_add_wait_queue_entry (DWB_WAIT_QUEUE * wait_queue, void *data)
{
DWB_WAIT_QUEUE_ENTRY *wait_queue_entry = NULL;
assert (wait_queue != NULL && data != NULL);
wait_queue_entry = dwb_make_wait_queue_entry (wait_queue, data);
> wait queue에 들어갈 대기열 생성
if (wait_queue_entry == NULL)
{
return NULL;
> 할당 오류
}
if (wait_queue->head == NULL)
{
wait_queue->tail = wait_queue->head = wait_queue_entry;
> wait_queue가 비어있으면 head = tail = wait_queue_entry
}
else
{
wait_queue->tail->next = wait_queue_entry;
wait_queue->tail = wait_queue_entry;
> tail->next에 해당 블록을 넣고 tail을 마지막 블록으로 정리
}
wait_queue->count++;
> count + 1
return wait_queue_entry;
}
STATIC_INLINE int
dwb_wait_for_strucure_modification (THREAD_ENTRY * thread_p)
{
#if defined (SERVER_MODE)
int error_code = NO_ERROR;
DWB_WAIT_QUEUE_ENTRY *double_write_queue_entry = NULL;
UINT64 current_position_with_flags;
int r;
struct timeval timeval_crt, timeval_timeout;
struct timespec to;
bool save_check_interrupt;
(void) pthread_mutex_lock (&dwb_Global.mutex);
> wait queue의 수정에 앞서 lock
current_position_with_flags = ATOMIC_INC_64 (&dwb_Global.position_with_flags, 0ULL);
> current_position_with_flags = dwb_Global.position_with_flags
if (!DWB_IS_MODIFYING_STRUCTURE (current_position_with_flags))
> 불필요한 대기를 피하기 위해 DWB 가 생성중이 아니라면
{
pthread_mutex_unlock (&dwb_Global.mutex);
return NO_ERROR;
> lock을 해제하고 종료
}
thread_lock_entry (thread_p);
> 해당 스레드도 lock
double_write_queue_entry = dwb_block_add_wait_queue_entry (&dwb_Global.wait_queue, thread_p);
> wait queue에 대기열 추가
if (double_write_queue_entry == NULL)
{
/* allocation error */
thread_unlock_entry (thread_p);
pthread_mutex_unlock (&dwb_Global.mutex);
ASSERT_ERROR_AND_SET (error_code);
return error_code;
> 실패했으면 에러처리
}
pthread_mutex_unlock (&dwb_Global.mutex);
> 스레드 unlock
save_check_interrupt = logtb_set_check_interrupt (thread_p, false);
gettimeofday (&timeval_crt, NULL);
timeval_add_msec (&timeval_timeout, &timeval_crt, 10);
timeval_to_timespec (&to, &timeval_timeout);
> timeout을 위한 설정
r = thread_suspend_timeout_wakeup_and_unlock_entry (thread_p, &to, THREAD_DWB_QUEUE_SUSPENDED);
> 실질적 대기, 내부에서 pthread_cond_timedwait 함수를 호출하여 대기가 일어남
> pthread_cond_timedwait는 리눅스 함수이므로, 윈도우의 경우에는 동일 이름으로 함수를 구현하여 제공
(void) logtb_set_check_interrupt (thread_p, save_check_interrupt);
if (r == ER_CSS_PTHREAD_COND_TIMEDOUT)
{
/* timeout, remove the entry from queue */
dwb_remove_wait_queue_entry (&dwb_Global.wait_queue, &dwb_Global.mutex, thread_p, NULL);
return r;
> timeout 처리
}
else if (thread_p->resume_status != THREAD_DWB_QUEUE_RESUMED)
{
assert (thread_p->resume_status == THREAD_RESUME_DUE_TO_SHUTDOWN);
dwb_remove_wait_queue_entry (&dwb_Global.wait_queue, &dwb_Global.mutex, thread_p, NULL);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INTERRUPTED, 0);
return ER_INTERRUPTED;
> 인터럽트가 일어났을 경우 처리
}
else
{
assert (thread_p->resume_status == THREAD_DWB_QUEUE_RESUMED);
return NO_ERROR;
> 종료
}
#else /* !SERVER_MODE */
return NO_ERROR;
#endif /* !SERVER_MODE */
}
slot이 가리키는 위치에 데이터를 놓습니다
return : 에러 코드
dwb_slot : slot 위치가 되는 포인터
io_page_p : 데이터 페이지
STATIC_INLINE void
dwb_set_slot_data (THREAD_ENTRY * thread_p, DWB_SLOT * dwb_slot, FILEIO_PAGE * io_page_p)
{
assert (dwb_slot != NULL && io_page_p != NULL);
assert (io_page_p->prv.p_reserve_2 == 0);
if (io_page_p->prv.pageid != NULL_PAGEID)
> 데이터 페이지의 페이지가 유효하다면
{
memcpy (dwb_slot->io_page, (char *) io_page_p, IO_PAGESIZE);
> 슬롯의 페이지 위치에 해당 페이지 복사
}
else
{
/* Initialize page for consistency. */
fileio_initialize_res (thread_p, dwb_slot->io_page, IO_PAGESIZE);
> 슬롯의 페이지 자체를 초기화
}
assert (fileio_is_page_sane (io_page_p, IO_PAGESIZE));
LSA_COPY (&dwb_slot->lsa, &io_page_p->prv.lsa);
VPID_SET (&dwb_slot->vpid, io_page_p->prv.volid, io_page_p->prv.pageid);
> dwb_slot->vpid.volid = io_page_p->prv.volid
> dwb_slot->vpid.pageid = io_page_p->prv.pageid
}
이 글은 42Seoul에서 진행된 CUBRID DB Engine 스터디로 팀원들과 함께 공부한 내용을 포스팅한 것입니다.
https://github.com/innovationacademy-kr/dbstudy