You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
변경 연산에 대한 명령 로그가 디스크에 확실히 반영된 후에 (fsync()) 그 변경 연산의 수행을 완료
데이터 일관성 보장
매번 fsync()를 호출해야하므로 비동기 모드보다는 낮은 성능
하나씩 write/fsync 했을 때의 오버헤드를 줄이기 위해, group commit 제공
비동기 (asynchronous) 모드
변경 연산에 대한 명령 로그를 로그 버퍼에 기록하지만 디스크에 반영하는 것은 보장하지 않은 상태로 그 변경 연산의 수행을 완료
캐시 노드가 비정상적으로 종료되는 경우, 일부 데이터 손실 가능
성능을 우선시하는 경우에 유용
명령 로깅 메타데이터
log_global 구조체를 사용해 로깅에 관한 메타정보 관리
cmdlogbuf.c
/* log global structure */structlog_global {
log_FILE log_file; /* active한 로그 파일 정보 */
log_BUFFER log_buffer; /* 로그 버퍼 정보 */
log_FLUSHER log_flusher; /* 로그 플러셔 정보 */
LogSN nxt_write_lsn; /* 로그 버퍼에 기록할 다음 로그 레코드의 LSN */
LogSN nxt_flush_lsn; /* 로그 파일에 기록할 다음 로그 레코드의 LSN (로그 버퍼에 있는 첫 번째 로그 레코드의 LSN) */
LogSN nxt_fsync_lsn; /* 로그 파일에 fsync 요청해야할 로그 레코드의 LSN (마지막 fsync() 호출 후, 그 시점의 nxt_flush_lsn) */pthread_mutex_t log_write_lock;
pthread_mutex_t log_flush_lock;
pthread_mutex_t log_fsync_lock;
pthread_mutex_t flush_lsn_lock;
pthread_mutex_t fsync_lsn_lock;
pthread_cond_t log_flush_cond;
bool async_mode; /* sync or async 모드; Default는 sync */volatilebool initialized;
};
명령 로그 레코드
메타 정보를 가지는 header와 변경 연산에 대한 데이터를 가지는 body로 구성
cmdlog.h
typedefstruct_loghdr {
uint8_t logtype; /* 로그 레코드 유형 */uint8_t updtype; /* 업데이트 유형 */uint8_t reserved_8[2];
uint32_t body_length; /* 로그 레코드 body 길이 */
} LogHdr;
typedefstruct_logrec {
LogHdr header; /* 로그 레코드 헤더 */char *body; /* 로그 레코드 데이터 */
} LogRec;
명령 로그 버퍼
cmdlogbuf.c
/* log buffer structure */typedefstruct_log_buffer {
/* log buffer */char *data; /* log buffer pointer */uint32_t size; /* log buffer size */uint32_t head; /* the head position in log buffer */uint32_t tail; /* the tail position in log buffer */int32_t last; /* the last position in log buffer *//* flush request queue */
log_FREQ *fque; /* flush request queue pointer */uint32_t fqsz; /* flush request queue size */uint32_t fbgn; /* the queue index to begin flush */uint32_t fend; /* the queue index to end flush */int32_t dw_end; /* the queue index to end dual write */
} log_BUFFER;
명령 로그 버퍼는 circular queue 구조이며, 이 버퍼에서는 크게 아래의 두 작업이 발생한다:
Worker thread에 의한 로그 레코드 write
Log flush thread에 의한 로그 레코드 flush
Worker thread에 의한 로그 레코드 write
cmdlogbuf.c: do_log_buff_write()
static LogSN do_log_buff_write(LogRec *logrec, bool dual_write)
{
log_BUFFER *logbuff = &log_gl.log_buffer;
...
uint32_t total_length = sizeof(LogHdr) + logrec->header.body_length;
...
/* 1. log_write_lock 획득 */pthread_mutex_lock(&log_gl.log_write_lock);
/* 2. log write 할 위치 탐색 *//* 초기화 시, head/tail = 0; last = -1; */while (1) {
if (logbuff->head <= logbuff->tail) {
assert(logbuff->last == -1);
/* logbuff->head == logbuff->tail: empty state (NO full state) */if (total_length < (logbuff->size - logbuff->tail)) {
/* 로그 레코드 쓸 공간이 남아있을 경우, break; */break; /* enough buffer space */
}
if (logbuff->head > 0) {
logbuff->last = logbuff->tail;
logbuff->tail = 0;
/* increase log flush end pointer * to make to-be-flushed log data contiguous in memory.*/if (logbuff->fque[logbuff->fend].nflush > 0) {
if ((++logbuff->fend) == logbuff->fqsz) logbuff->fend = 0;
}
if (total_length < logbuff->head) {
/* 로그 레코드 쓸 공간이 남아있을 경우, break; */break; /* enough buffer space */
}
}
} else { /* logbuff->head > logbuff->tail */assert(logbuff->last != -1);
if (total_length < (logbuff->head - logbuff->tail)) {
/* 로그 레코드 쓸 공간이 남아있을 경우, break; */break; /* enough buffer space */
}
}
/* 로그 레코드 쓸 여유 공간이 없을 경우, 로그 버퍼의 데이터를 직접 flush *//* Lack of log buffer space: force flushing data on log buffer */pthread_mutex_unlock(&log_gl.log_write_lock);
pthread_mutex_lock(&log_gl.log_flush_lock);
(void)do_log_buff_flush(false);
pthread_mutex_unlock(&log_gl.log_flush_lock);
pthread_mutex_lock(&log_gl.log_write_lock);
}
/* 3. logbuff->tail에 로그 레코드 write *//* write log record at the found location of log buffer */lrec_write_to_buffer(logrec, &logbuff->data[logbuff->tail]);
/* 4. 로그 레코드 크기만큼 logbuff->tail 위치 조정 */
logbuff->tail += total_length;
/* 5. 로그 레코드 write할 다음 위치 업데이트 *//* update nxt_write_lsn */
current_lsn = log_gl.nxt_write_lsn;
log_gl.nxt_write_lsn.roffset += total_length;
/* 6. 로그 flush 요청 업데이트 *//* update log flush request */if (logbuff->fque[logbuff->fend].nflush > 0 &&
logbuff->fque[logbuff->fend].dual_write != dual_write) {
if ((++logbuff->fend) == logbuff->fqsz) logbuff->fend = 0;
}
while (total_length > 0) {
/* check remain length */
spare_length = CMDLOG_FLUSH_AUTO_SIZE - logbuff->fque[logbuff->fend].nflush;
if (spare_length >= total_length) spare_length = total_length;
logbuff->fque[logbuff->fend].nflush += spare_length;
logbuff->fque[logbuff->fend].dual_write = dual_write;
if (logbuff->fque[logbuff->fend].nflush == CMDLOG_FLUSH_AUTO_SIZE) {
if ((++logbuff->fend) == logbuff->fqsz) logbuff->fend = 0;
}
total_length -= spare_length;
}
/* 7. log_write_lock 해제 */pthread_mutex_unlock(&log_gl.log_write_lock);
/* 8. Flush 해야하는데 flusher가 자고 있는 경우 flusher thread wakeup *//* wake up log flush thread if flush requests exist */if (logbuff->fbgn != logbuff->fend) {
if (log_gl.log_flusher.sleep == true) {
do_log_flusher_wakeup(&log_gl.log_flusher);
}
}
return current_lsn;
log_write_lock 획득
log write 할 위치 탐색
로그 레코드 쓸 공간이 남아있을 경우, while 문 break; 필요시 logbuff->tail 위치 조정
로그 레코드 쓸 여유 공간이 없을 경우, 로그 버퍼의 데이터를 직접 flush
logbuff->tail에 로그 레코드 write
로그 레코드 크기만큼 logbuff->tail 위치 조정
로그 flush 요청 업데이트
log_write_lock 해제
Flush 해야하는데 flusher가 자고 있는 경우 flusher thread wakeup
Log flush thread에 의한 로그 레코드 flush
Log flush thread는 flush할 로그 레코드가 있으면 계속해서 flush를 수행