Skip to content

[CBRD-27075] Fix CDC timestamp lookup on continuation-only log pages#7478

Merged
H2SU merged 2 commits into
CUBRID:release/11.4_hotfixfrom
H2SU:CBRD-27075
Jul 16, 2026
Merged

[CBRD-27075] Fix CDC timestamp lookup on continuation-only log pages#7478
H2SU merged 2 commits into
CUBRID:release/11.4_hotfixfrom
H2SU:CBRD-27075

Conversation

@H2SU

@H2SU H2SU commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

http://jira.cubrid.org/browse/CBRD-27075

Purpose

CDC 또는 Flashback이 시간 기준으로 시작 LSA를 찾을 때, archive/active log volume의 첫 page가 이전 page에서 시작된 log record의 continuation data만 포함할 수 있다.

이 page에는 새로운 LOG_RECORD_HEADER가 없으므로 LOG_PAGE_HEADER::offsetNULL_OFFSET이다. 기존 cdc_get_start_point_from_file()은 이를 확인하지 않고 page data를 LOG_RECORD_HEADER로 해석하여, payload 일부를 forw_lsa.pageid로 사용할 수 있었다.

그 결과 비정상적인 logical page ID를 logpb_fetch_page()에 전달하면서 ER_LOG_PAGE_CORRUPTED가 기록되고, 시간 기반 LSA 조회가 빈 응답으로 끝날 수 있다.

Implementation

src/transaction/log_manager.ccdc_get_start_point_from_file()

  • log volume의 첫 page를 fetch한 직후 hdr.offset을 확인한다.
  • hdr.offset == NULL_OFFSET이면 해당 page를 continuation-only page로 판단하고, 실제 log record header가 시작되는 다음 page까지 이동한다.
  • page를 이동할 때마다 logical_pageidoffset을 새 page header 기준으로 갱신한다.
  • 탐색 중 NXIO 경계에 도달하면 page data를 잘못 해석하지 않고 ER_CDC_LSA_NOT_FOUND를 반환한다.
  • 기존 log record 순회 로직은 유효한 header offset을 확보한 뒤에만 시작한다.

정상적인 시작 page에서는 기존 경로를 그대로 사용하며 추가 page fetch는 발생하지 않는다.

Test

4KB log page와 작은 active log volume을 사용하고, 압축되지 않는 20,000 byte BIT VARYING 값을 반복 갱신하여 log record가 여러 page 및 archive volume 경계를 넘도록 했다. 동시에 시간 기반 LSA 조회를 60회 반복했다.

검증 항목 수정 전 수정 후
UPDATE 완료 수 3,000 3,000
시간 기반 LSA 조회 성공 50 / 60 60 / 60
빈 응답 10 0
cdc_get_start_point_from_file() 오류 stack 10 0
log page corruption 메시지 10 0
서버 생존 정상 정상

추가로 cdc_logging_debug 비활성화, archive 무제한 보관, 조회 대상 table에 committed row가 존재하는 조건에서도 조회 60회가 모두 정상 완료되고 storage 오류가 발생하지 않는 것을 확인했다.

Remarks

  • 이 문제는 archive 삭제 경쟁이 아니라 log volume 시작 page가 continuation-only 상태일 때 발생한다.
  • CDC API 및 wire format 변경은 없다.
  • 정상 page에서는 NULL_OFFSET 비교만 추가되며, continuation-only page일 때만 다음 page를 fetch한다.

A volume may begin with continuation-only pages whose NULL offset cannot identify a log record header. Advance only to a valid recorded page and stop at the first unflushed LSA boundary.

Constraint: Timestamp lookup must not read at or beyond the NXIO boundary.
Rejected: Guarding archive deletion alone | The defect reproduces without archive deletion.
Confidence: high
Scope-risk: narrow
Directive: Preserve NULL_OFFSET handling before LOG_GET_LOG_RECORD_HEADER at log-volume start points.
Tested: debug cmc/bd build-install; baseline reproduction; four fixed stress variants; GNU indent 2.2.11; git diff --check; cppcheck 2.13.0
Not-tested: Physical archive deletion was not observed; no CTest targets were configured.
@H2SU H2SU self-assigned this Jul 15, 2026
@H2SU
H2SU requested a review from hornetmj as a code owner July 15, 2026 14:18
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "[CBRD-27075] Prevent CDC time lookup fro..." | Re-trigger Greptile

Comment thread src/transaction/log_manager.c
Comment thread src/transaction/log_manager.c Outdated
*/
while (process_lsa.offset == NULL_OFFSET)
{
LOG_LSA nxio_lsa = log_Gl.append.get_nxio_lsa ();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

archive 경계 검사 고려

--- a/src/transaction/log_manager.c
+++ b/src/transaction/log_manager.c
@@ cdc_get_start_point_from_file (...)
   LOG_LSA cur_log_lsa = LSA_INITIALIZER;
+  /* First pageid past the target archive (exclusive bound for the continuation-skip loop).
+   * Taken from the header of the very archive being processed, where npages == the number of
+   * data pages actually written by logpb_archive_active_log, so it is exact for this volume. */
+  LOG_PAGEID arv_last_pageid = NULL_PAGEID;
@@ (arv_num != -1, 이미 마운트된 archive — LOG_ARCHIVE_CS 안, arv_num 일치 보장)
        process_lsa.pageid = log_Gl.archive.hdr.fpageid;
        process_lsa.offset = 0;
+       arv_last_pageid = log_Gl.archive.hdr.fpageid + log_Gl.archive.hdr.npages;
      }
@@ (arv_num != -1, 파일 헤더를 갓 읽은 경로 — dismount 전에 캡처)
                process_lsa.pageid = arv_hdr->fpageid;
                process_lsa.offset = 0;
+               arv_last_pageid = arv_hdr->fpageid + arv_hdr->npages;

                fileio_dismount (thread_p, vdes);
@@ (연속-only 페이지 skip 루프)
   while (process_lsa.offset == NULL_OFFSET)
     {
-      LOG_LSA nxio_lsa = log_Gl.append.get_nxio_lsa ();
+      /* Active volume: never cross the durable (flushed) frontier nxio_lsa.
+       * Archive volume: never read past this archive's own page range (from its header). */
+      LOG_PAGEID limit_pageid =
+     (arv_num == -1) ? log_Gl.append.get_nxio_lsa ().pageid : arv_last_pageid;

       cdc_log ("%s : skip continuation-only log page %lld", __func__, (long long int) process_lsa.pageid);
       process_lsa.pageid++;
-      if (process_lsa.pageid >= nxio_lsa.pageid)
+      if (process_lsa.pageid >= limit_pageid)
      {
        ctime_r (time, ctime_buf);
        er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_CDC_LSA_NOT_FOUND, 1, ctime_buf);
        return ER_CDC_LSA_NOT_FOUND;
      }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정하였습니다.

@H2SU
H2SU merged commit a7df191 into CUBRID:release/11.4_hotfix Jul 16, 2026
6 of 7 checks passed
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "[CBRD-27075] Limit timestamp lookup to o..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants