(KO) CUBRID 아키텍처 개요 — 프로세스 모델, 계층 스택, 세부 문서로 가는 지도
목차
- 이 문서의 독자
- 프로세스 모델
- 계층 스토리지 스택
- 질의 파이프라인
- 동시성, 로깅, 복구
- 분산 계층
- PL 패밀리
- 모든 계층을 가로지르는 인프라
- 어디서 읽기 시작할 것인가
- 서브카테고리 지도
이 문서의 독자
섹션 제목: “이 문서의 독자”이 문서는 CUBRID 코드 분석 트리의 정문이다. CUBRID 소스를 한 줄도 읽지 않은 독자가 큰 그림부터 잡고 들어오라고 만들었다. 어떤 상주 프로세스가 도는지, 그 안의 계층 스택이 어떻게 쌓여 있는지, 질의 하나가 어떤 경로로 흐르는지, 동시성과 내구성을 받치는 하위 시스템이 무엇인지, 단일 노드를 클러스터로 묶는 분산 계층이 어떻게 짜여 있는지를 한 번에 보여 주고, 그 위에서 약 70개의 세부 문서 가운데 어느 것을 펼칠지 정할 수 있게 한다. 절마다 일부러 얕게 적었다. 깊은 코드 분석은 모듈별 세부 문서에 따로 있고, 본문에서는 그 파일 경로(cubrid-X.md)를 가능한 자리마다 짚어 준다. 절이 얇아 보이는 것은 의도된 깊이다. 이 문서는 길잡이지 세부 문서를 한 번 더 옮겨 적은 사본이 아니다.
두 번째 독자는 세부 문서 몇 편을 이미 읽었고 그것들을 한 줄기로 엮고 싶은 엔지니어다. “SELECT 하나가 broker에서 heap까지 어떻게 다녀오는가”, “cub_master가 이 노드를 새 HA 마스터로 선언하면 무엇이 깨어나는가”, “페이지 버퍼는 왜 flush 직전에 로그 매니저를 호출하는가” 같은 질문은 여러 서브시스템의 경계를 한꺼번에 넘는다. 이 문서의 다이어그램은 그 경계에 이름을 붙여 두므로, 함께 읽어야 할 세부 문서의 짝을 고를 때 도움이 된다.
세 번째 독자는 CUBRID가 왜 지금 같은 모양인지 궁금한 사람이다. CUBRID는 객체 관계형 엔진이지만 뿌리는 OODB(UniSQL → CUBRID)에 있고, HA를 합의 프로토콜 없이 cub_master로만 풀고, broker 계층은 CAS 워커를 미리 fork한 뒤 파일 디스크립터를 넘겨 주는 방식으로 동작하며, PL은 서버에 박힌 형태가 아니라 형제 JVM 프로세스에서 돈다. 하나하나가 트레이드오프가 또렷한 설계 결정이다. 결정의 근거는 세부 문서가 다루고, cubrid-design-philosophy.md에 모아 두었다. 이 개요는 그 결정에 이름을 붙이고, 근거를 풀어 놓은 문서로 안내하는 역할만 한다.
프로세스 모델
섹션 제목: “프로세스 모델”CUBRID는 다중 프로세스 엔진이다. 상주 프로세스 네 종류와 단기 유틸리티 한 부류만 알면 운영 시나리오는 거의 다 덮인다. 지금 이 데이터베이스가 띄우고 있는 프로세스 묶음이 머릿속에 가장 먼저 그려 두면 좋은 그림이다.
cub_master: 호스트 단위 감독자. 호스트마다 하나만 돈다. 마스터 공유 메모리 앵커를 들고 있고, Unix 도메인 소켓으로 호스트 내 프로세스 등록을 받아들인다(서버, copylogdb, applylogdb, broker가 모두 여기로 등록한다). 지정된 포트에서 원격 클라이언트의 인바운드 TCP 연결을 받은 뒤, 그 파일 디스크립터를 자기 UDS 너머로 알맞은cub_server에게 넘겨 준다. 동료cub_master들과는 UDP 생사 신호를 주고받으며 HA를 거든다. failover/failback FSM 역시 이 프로세스가 돌린다. 데이터베이스 상태 자체를 들고 있지는 않고 감독자 역할에 머문다. 세부:cubrid-heartbeat.md,cubrid-network-protocol.md.cub_server: 데이터베이스 엔진. 보통 데이터베이스마다 하나가 돈다. 볼륨, 페이지 버퍼, 로그 파일, 락 테이블, catalog, 옵티마이저, XASL 실행기, MVCC 장부, vacuum, 스토리지 스택 전체가 여기 모인다. 클라이언트 연결은 결국 모두 이쪽에서 끝난다. 세부:cubrid-boot.md,cubrid-network-protocol.md,cubrid-thread-worker-pool.md,cubrid-thread-manager-ng.md.cub_pl: PL용 JVM 프로세스. Java/PL 실행이 켜져 있는 한cub_server마다 한 개가 따라붙는다. 서버가 띄워 주고, Java 저장 프로시저와 PL/CSQL 바이트코드를 실행하며, 서버 세션과는 다른 경로와 똑같은 CSS 프레이밍을 쓰는 Unix 도메인 소켓으로 통신한다. 세부:cubrid-pl-javasp.md,cubrid-pl-plcsql.md.cub_broker+cub_cas: broker 계층.cub_broker는cub_cas워커 풀을 정해진 크기만큼 미리 fork해 둔 부모 프로세스다. JDBC/CCI/ODBC 클라이언트용 TCP 리스너를 하나 열어 두고, 받은 클라이언트 소켓을 Unix 도메인 채널의SCM_RIGHTS로 비어 있는 CAS에 넘기며, 풀 상태는 SysV 공유 메모리 영역에서 관리한다.cub_cas는 클라이언트 세션 수명만큼 사는 워커로, CSS 프레임 트래픽을 그대로cub_server로 흘려 보낸다. 세부:cubrid-broker.md.- SA 모드 유틸리티: 단명 프로세스(
cubrid loaddb,cubrid unloaddb,cubrid backupdb,cubrid compactdb,cubrid restoredb등).libcubridsa.so로 엔진 전체를 자기 프로세스 안에 끌어들여, 살아 있는cub_server없이도 디스크 위 데이터베이스에 직접 손을 댄다. 같은 소스 트리가 세 가지 모드(서버 / SA / CS)로 빌드되고, 어느 모드를 쓸지는 런타임의dlopen이 정한다. 세부:cubrid-sa-cs-runtime.md,cubrid-loaddb.md,cubrid-backup-restore.md,cubrid-compactdb.md.
flowchart LR
subgraph CLIENT["Client side"]
APP["JDBC / CCI / ODBC / Python / PHP app"]
CSQL["csql (CS-mode)"]
UTILS["cubrid loaddb / backupdb / unloaddb<br/>(SA-mode utility)"]
end
subgraph BROKER["cub_broker host"]
BPARENT["cub_broker<br/>(TCP listener)"]
CAS1["cub_cas #1"]
CAS2["cub_cas #2"]
CASN["cub_cas #N"]
BSHM["SysV shm<br/>(broker control)"]
end
subgraph DBHOST["cub_server host"]
MASTER["cub_master<br/>(supervisor)"]
SERVER["cub_server<br/>(engine)"]
PL["cub_pl<br/>(JVM)"]
end
subgraph PEERS["Peer hosts (HA cluster)"]
PEER["cub_master @ peer"]
end
APP -- TCP --> BPARENT
BPARENT -. SCM_RIGHTS over UDS .-> CAS1
BPARENT -. SCM_RIGHTS over UDS .-> CAS2
BPARENT -. SCM_RIGHTS over UDS .-> CASN
BPARENT --- BSHM
CAS1 --- BSHM
CAS2 --- BSHM
CASN --- BSHM
CSQL -- TCP --> MASTER
CAS1 -- TCP --> MASTER
CAS2 -- TCP --> MASTER
CASN -- TCP --> MASTER
MASTER -. UDS / SCM_RIGHTS .-> SERVER
SERVER -- UDS --> PL
MASTER <-- UDP heartbeat --> PEER
UTILS -. dlopen libcubridsa .-> SERVER
UTILS -- direct volume I/O --> SERVER
다이어그램이 보기보다 단순해 보이려면 세 가지를 짚어 두면 된다.
(a) TCP 리스너는 cub_master에 있지 cub_server에 있지 않다. 들어오는 연결은 csql이든, CAS든, 다른 도구든 모두 cub_master의 지정된 포트에 먼저 닿는다. 마스터가 거기서 목적지 데이터베이스 이름을 꺼내 자기가 쥐고 있는 프로세스 등록 표에서 알맞은 cub_server를 골라, 그 파일 디스크립터를 그쪽으로 넘긴다. 서버는 갓 받아 든 소켓에서 프로토콜 헤더를 읽고 그대로 일을 이어 간다. 이 우회 한 단계 덕분에 한 호스트 위에 cub_server(데이터베이스마다 하나)가 여럿 돌더라도 각자 따로 포트를 쥐고 있을 필요가 없다.
(b) broker는 서버와 한 층 떨어져 있다. 자기 호스트에서 따로 돌거나(같은 호스트에 같이 두기도 하지만 논리적으로는 분리된 계층이다), 풀 크기, 연결 라우팅, ACL, 모니터링을 자기 안에서 따로 관리한다. CAS에서 서버로 가는 구간도 TCP 연결이며, 보통 같은 데이터센터 안에서 끝난다. broker 여럿이 서버 하나 또는 여럿에 흘려 넣지 못할 이유는 없다.
(c) cub_pl은 cub_master가 아니라 cub_server의 자식이다. Java SP와 PL/CSQL은 자기 JVM 안에서 돌며, 서버 쪽 SP 호출은 인자를 직렬화한 뒤 세션마다 따로 둔 UDS로 JVM에 넘기고, JVM이 결과를 돌려줄 때까지 기다린다. 서버 입장에서 JVM은 원격 서비스가 아니라 옆에서 함께 도는 보조 프로세스다.
프로세스 모델에 관한 길잡이: CAS 풀, 랑데부 프로토콜, broker 제어 평면은 cubrid-broker.md. 마스터 사이의 UDP 가십은 cubrid-heartbeat.md. JVM IPC는 cubrid-pl-javasp.md. CSS 프레이밍과 NRP 디스패치는 cubrid-network-protocol.md. 세 갈래 빌드 변종과 유틸리티 분류는 cubrid-sa-cs-runtime.md.
계층 스토리지 스택
섹션 제목: “계층 스토리지 스택”cub_server 안의 스토리지 스택은 엄격한 계층 구조다. 각 계층은 아래 계층을 추상화로만 다루고, 위쪽으로는 더 좁은 추상화를 내보낸다. 이 층의 구조를 잡아 두지 않으면 어떤 읽기/쓰기 경로도 제대로 따라갈 수 없다.
flowchart TB
subgraph CLIENT_WORKSPACE["Client-side workspace"]
WSP["MOP table<br/>(in-memory OID -> object cache)"]
SMC["SM_CLASS graph<br/>(in-memory schema)"]
end
subgraph SERVER["cub_server"]
LOC["locator_∗_force<br/>(server-side bridge)"]
CAT["catalog_manager<br/>(_db_class, _db_attribute, ...)"]
HEAP["heap_manager<br/>(slotted pages, MVCC headers)"]
BTREE["btree<br/>(latch-coupled B+Tree)"]
EHASH["extendible_hash<br/>(directory + buckets)"]
OFLOW["overflow_file<br/>(big-record / overflow-OID chain)"]
PB["page_buffer<br/>(BCB array, three-zone LRU)"]
DWB["double_write_buffer<br/>(torn-write protection)"]
DM["disk_manager<br/>(volumes, sectors, files, pages)"]
end
VOLS[("On-disk volumes<br/>(_dbname / _dbname_t / _lgar*)")]
WSP --> LOC
SMC --> LOC
LOC --> HEAP
LOC --> BTREE
LOC --> CAT
CAT --> HEAP
CAT --> BTREE
CAT --> EHASH
HEAP --> OFLOW
BTREE --> OFLOW
HEAP --> PB
BTREE --> PB
EHASH --> PB
OFLOW --> PB
CAT --> PB
PB --> DWB
DWB --> DM
PB --> DM
DM --> VOLS
아래에서 위로 훑어 본다.
disk_manager가 볼륨, 섹터, 파일, 페이지를 관리한다. 볼륨 하나는 OS 파일 하나에 대응하고, 섹터는 64개의 연속한 페이지로 묶이는 할당 단위이며, 파일은 섹터 묶음, 페이지는 I/O 단위다. 디스크 매니저는 섹터를 떼어 주고 거둬들이며, 디스크 캐시가 자리 부족을 알려 오면 볼륨을 늘린다. 영구 용도와 임시 용도를 갈라 두어 임시 파일이 영구 공간을 잠식하지 못하게 한다. 세부:cubrid-disk-manager.md.page_buffer와double_write_buffer가 그 바로 위에 앉는다. 페이지 버퍼는(VPID → BCB → 메모리 프레임)매핑을 버킷별 해시로 관리하고, 스레드별 프라이빗 리스트(쿼터 조정 가능)와 공유 리스트로 나뉜 세 구역 LRU를 굴린다. 락프리 큐로 victim을 잠든 대기자에게 곧장 넘기며, BCB마다 따로 짠 read/write/flush 래치로 보호한다. 더티 페이지는 홈 위치에 닿기 전에 모두 DWB를 거치므로, 홈 페이지 쪽에서 찢긴 쓰기가 일어나도 DWB 사본으로 되살릴 수 있다. 세부:cubrid-page-buffer-manager.md,cubrid-double-write-buffer.md.heap_manager,btree,extendible_hash는 페이지 버퍼 위에 자리 잡은 세 가지 on-page 레코드 조직이다.heap_manager는 슬롯 페이지에 가변 길이 사용자 레코드를 MVCC 헤더(insert MVCCID, delete MVCCID, 이전 버전 사슬)와 함께 적는다.btree는 latch-coupling B+Tree이고, key‖OID 결합과 OID 접미부 단위의 unique 제약 검증을 함께 한다.extendible_hash는 디렉터리가 두 배씩 자라는 Fagin 식 해시 파일로, 클래스 이름 조회, repr-id 조회, 일부 내부 dedup 테이블에 쓰인다. 큰 레코드와 긴 OID 목록은overflow_file사슬로 빠진다. 세부:cubrid-heap-manager.md,cubrid-btree.md,cubrid-extendible-hash.md,cubrid-overflow-file.md,cubrid-tde.md(버퍼와 디스크 사이에서 페이지 내용을 암복호화한다).catalog_manager는 클래스별 디스크 표현과 통계를 전용 catalog 파일(CTID로 앵커)에 적고, 사용자에게 보이는 시스템 클래스(_db_class,_db_attribute,_db_index,_db_serial,_db_user,_db_authorization,_db_trigger)를 그 옆에 둔다. 시스템 클래스는 정해진 루트 클래스 OID에서 부트스트랩되며,SHOW STATS부터 권한 검사까지 모두 catalog를 거쳐 풀린다. 세부:cubrid-catalog-manager.md,cubrid-statistics.md,cubrid-authentication.md,cubrid-serial.md.SM_CLASS(class-object)는 워크스페이스가 catalog 정보를 메모리에서 펼쳐 만든 스키마 그래프다. 속성, 메서드, 파티션, 제약, 트리거, 파티션 규칙 디스크립터가 모두 여기에 담긴다. OODB 뿌리가 이 자리에서 드러난다. 스키마는 평평한 관계가 아니라 객체 그래프이고, DDL은 디스크에 쓰기 전에 그 그래프를 먼저 손본다. 세부:cubrid-class-object.md,cubrid-ddl-execution.md,cubrid-trigger.md,cubrid-partition.md.locator는 메모리 쪽과 디스크 쪽을 잇는 다리다. 클라이언트 워크스페이스가 더티 객체를LC_COPYAREA버퍼에 모아 보내면, 서버 쪽locator_*_force패밀리가 단일 진입점에서 heap, btree, lock, log, FK, 복제 경로로 분배한다. 트리거와 무결성 규칙 발화도 여기서 일어난다. 세부:cubrid-locator.md.- 클라이언트 워크스페이스가 가장 위층이다. MOP 테이블이 OID에서 객체로 가는 메모리 캐시 역할을 하며, 더티 비트, pin 카운트, 애플리케이션이 보는
SM_CLASS그래프를 모두 들고 있다. ESQL/임베드 SQL, DBI/CCI 클라이언트, broker의 CAS는 모두 이 워크스페이스를 거쳐 읽고 쓴다. 세부:cubrid-class-object.md,cubrid-dbi-cci.md.
구조에서 가장 중요한 사실은 한 가지다. 레코드 조직 계층은 페이지 버퍼만 거쳐 동작하고, 디스크 매니저에는 절대 직접 접근하지 않는다. 그 덕에 버퍼 매니저가 WAL 순서를 강제할 수 있다. heap 페이지를 바꾸려면 먼저 로그 레코드가 prior list를 거쳐 로그 매니저로 흘러 들어가야 하고, 짝지어진 로그 LSA가 내구해질 때까지 버퍼 매니저는 그 더티 페이지를 flush하지 않는다. B+Tree, ehash, catalog 변경도 모두 같은 약속을 따른다.
질의 파이프라인
섹션 제목: “질의 파이프라인”SELECT나 DML 한 문장은 텍스트에서 결과 행까지 긴 파이프라인을 따라 흐른다. 파이프라인은 클라이언트와 서버로 나뉘고 정해진 자리에서 와이어 위로 직렬화되지만, 개념 단계 자체는 그대로다.
flowchart LR SQL["SQL text"] PARSER["parser<br/>(Flex+Bison -> PT_NODE)"] SC["semantic_check<br/>(name resolve, type check, CNF)"] REW["query_rewrite<br/>(LIMIT lowering, view inlining,<br/>subquery flatten, predicate reduce)"] OPT["query_optimizer<br/>(QO_ENV graph, DP join enum,<br/>System R cost model)"] XGEN["xasl_generator<br/>(QO_PLAN -> XASL_NODE tree)"] XCACHE["xasl_cache<br/>(SHA-1 keyed plan cache)"] XEXEC["query_executor<br/>(Volcano open/next/close)"] SCAN["scan_manager<br/>(SCAN_ID dispatch)"] AM["access methods<br/>(heap / btree / list / set /<br/>value / json-table / show / dblink)"] EVAL["query_evaluator<br/>(PRED_EXPR, regu_variable)"] POST["post_processing<br/>(group by / aggregates /<br/>window / order by)"] LF["list_file<br/>(QFILE_LIST_ID, spill to FILE_TEMP)"] CUR["cursor / dbi-cci<br/>(client fetch handle)"] ROWS["result rows"] SQL --> PARSER --> SC --> REW --> OPT --> XGEN --> XCACHE XCACHE --> XEXEC XEXEC --> SCAN --> AM XEXEC --> EVAL AM --> EVAL XEXEC --> POST POST --> LF AM --> LF XEXEC --> LF LF --> CUR --> ROWS REW -. cache hit shortcut .-> XCACHE
이 파이프라인에는 이름 붙여 둘 만한 구조적 사실이 여섯 가지 있다.
(a) 컴파일은 한 번, 실행은 여러 번. 1~5단계(파싱 → 의미 검사 → 재작성 → 최적화 → XASL 생성)가 만들어 낸 직렬화된 XASL 트리는 서버 전역 xasl_cache에 들어간다. 키는 재작성된 SQL의 SHA-1이다. 같은 SQL이 두 번째로 들어오면 컴파일은 통째로 건너뛰고 캐시에서 XASL을 꺼낸다. 캐시는 참조한 클래스의 OID도 함께 추적하므로, 어떤 참조 클래스에 DDL이 떨어지면 그것에 기댄 항목이 무효화된다. 세부: cubrid-xasl-cache.md.
(b) 옵티마이저의 plan과 실행기의 plan은 서로 다른 IR이다. 최적화는 QO_NODE/QO_SEGMENT/QO_TERM 그래프 객체로 짠 QO_PLAN 트리 위에서 돈다. 비용 모델은 System R 방식, 즉 고정 CPU/IO와 행당 가변 CPU/IO의 합이다. 살아남은 QO_PLAN은 XASL 생성기가 한 단계 더 내려 재귀적인 XASL_NODE 트리로 만든다. 거기에는 aptr/dptr/scan_ptr 슬롯, 값 산출용 REGU_VARIABLE IR, 스캔 파라미터용 ACCESS_SPEC, 결과 행용 OUTPTR_LIST가 들어간다. 와이어 너머로 직렬화되고 캐시에 자리 잡는 것은 XASL뿐이다. 세부: cubrid-query-optimizer.md, cubrid-xasl-generator.md.
(c) 실행기는 Volcano 모델이다. qexec_execute_main_block이 xasl->type으로 분기해서, SCAN_ID 연산자 위에 한결같은 open/next/close 루프를 굴린다. 스캔 매니저는 다형 접근 방법 카탈로그다. heap, B+Tree, list-file, set, value, JSON-table, dblink, show, parallel-heap, method 스캔이 모두 같은 프로토콜로 자기를 내보인다. 세부: cubrid-query-executor.md, cubrid-scan-manager.md.
(d) 술어는 따로 분리된 엔진에서 평가된다. 끌어 올린 튜플마다 eval_pred가 T_PRED boolean 노드와 T_EVAL_TERM 잎으로 짠 PRED_EXPR 트리를 세 값 논리로 걷는다. 잎마다 fetch_peek_dbval이 REGU_VARIABLE::type(상수, 속성 페치, list-file 위치, 산술식, 함수 호출, host variable, OID, list-id)에 따라 경로별 풀이로 분기한다. regu-variable 엔진이 부르는 산술·문자열 연산자는 그 자체가 한 층 아래의 연산자 프리미티브 계층이다. 세부: cubrid-query-evaluator.md, cubrid-scalar-functions.md.
(e) materialization은 list-file로 통일된다. 서브쿼리 결과, 정렬 출력, 해시 빌드 쪽, group-by 누산기, 최종 질의 결과까지 모두 같은 QFILE_LIST_ID 추상화 한 층을 쓴다. 그 아래에는 질의마다 따로 둔 QMGR_TEMP_FILE(membuf로 시작하다 한도를 넘기면 FILE_TEMP로 옮겨 가는)이 깔린다. 연산자는 같은 open/add/scan/close 약속으로 위에서 읽고 아래로 쓴다. 세부: cubrid-list-file.md.
(f) 특수화된 후처리 패스. group-by, 집계, 분석/window 함수는 자기 패스에서 sort 기반 GROUP BY와 hash 기반 GROUP BY 중 하나를 런타임에 고르고, 해시 테이블이 예산을 넘기면 외부 정렬로 떨어진다. hash join도 같은 모양이다. Build/Probe 드라이버 하나에 세 가지 테이블 배치 전략과 grace 식 spilling이 따라붙는다. 병렬 질의 역시 전역 병렬 질의 워커 풀 위에 관리자 한 층을 얹은 형태다. 세부: cubrid-post-processing.md, cubrid-hash-join.md, cubrid-parallel-query.md, cubrid-external-sort.md, cubrid-runtime-memoization.md.
클라이언트 쪽 끝은 cursor다. db_query_first_tuple / db_query_get_tuple_value(cubrid-dbi-cci.md)와 cubrid-cursor.md가 broker 쪽과 DBI 쪽의 손잡이를 맡는다. 그 손잡이가 서버의 list file에 자기를 걸어 두고, 한 번에 네트워크 페이지 한 장씩 튜플을 가져온다.
동시성, 로깅, 복구
섹션 제목: “동시성, 로깅, 복구”CUBRID는 MVCC 엔진이고, 쓰기 충돌은 row 단위 락으로 푼다. 크래시 후 재시작은 ARIES 세 패스로 처리한다. 세 갈래의 시간선이 동시에 흐른다. MVCCID와 락이 흐르는 트랜잭션 시간선, WAL 레코드와 LSA가 흐르는 물리 시간선, 더티 버퍼 슬롯과 flush가 흐르는 페이지 시간선이다. 복구가 이 셋을 한 줄로 잇는다.
flowchart LR TX["TDES (transaction descriptor)<br/>cubrid-transaction.md"] MVCC["MVCC table<br/>(active MVCCIDs, snapshots)<br/>cubrid-mvcc.md"] LOCK["lock manager<br/>(per-OID multi-granularity)<br/>cubrid-lock-manager.md"] PRIOR["prior_list<br/>(per-tx WAL queue)<br/>cubrid-prior-list.md"] LOG["log_manager<br/>(LSA, append page, archive)<br/>cubrid-log-manager.md"] CHKPT["checkpoint<br/>(fuzzy ARIES, redo-LSA hint)<br/>cubrid-checkpoint.md"] RECOV["recovery_manager<br/>(analysis / redo / undo)<br/>cubrid-recovery-manager.md"] VAC["vacuum<br/>(reclaim dead versions via WAL replay)<br/>cubrid-vacuum.md"] PB["page_buffer + DWB"] HEAP["heap / btree / catalog"] TX --> MVCC TX --> LOCK TX --> PRIOR PRIOR --> LOG LOG --> PB HEAP -- mvcc header writes --> MVCC HEAP -- WAL records --> PRIOR CHKPT -- LOG_START_CHKPT / LOG_END_CHKPT --> LOG CHKPT -- redo-LSA hint --> PB RECOV -- replays --> LOG RECOV -- restores --> PB VAC -- forward log walk --> LOG VAC -- physical reclaim --> PB VAC --> MVCC
트랜잭션 코어는 TDES(transaction descriptor)다. 활성 트랜잭션마다 하나가 서버 전역 trantable에 자리를 잡는다. 트랜잭션의 MVCCID, 격리 수준, 세이브포인트 더미, 락 목록, WAL 레코드의 꼬리를 모두 들고 다닌다. MVCC와 락 매니저가 모두 TDES를 색인 삼아 동작한다. 격리 수준(READ COMMITTED, REPEATABLE READ, SERIALIZABLE)은 mvcctable::build_mvcc_info의 스냅샷 구성과 lock_manager의 락 모드 매핑 두 갈래로 분기한다. 세부: cubrid-transaction.md, cubrid-mvcc.md, cubrid-lock-manager.md.
WAL 파이프라인은 일부러 두 단으로 분리해 두었다. 레코드 변경 하나하나는 트랜잭션 자기 prior list 위에 LOG_PRIOR_NODE 한 장을 만들어 단방향 큐의 꼬리에 끼워 넣는다. 큐는 짧게 잡았다 풀어 주는 단일 뮤텍스로 보호되고, 로그-flush 데몬이 주기적으로 큐를 비운다. 그룹 커밋은 큐에 모이는 패턴에서 자연스럽게 얻어지는 효과다. 트랜잭션이 커밋하면 커밋 레코드도 같은 prior list에 들어가고, 다음 비움 한 번이 로그 임계 구역 한 번을 들면서 그 묶음을 통째로 흘려 보낸다. 세부: cubrid-prior-list.md, cubrid-log-manager.md.
체크포인트는 fuzzy-ARIES 방식이다. 주기 데몬이 LOG_START_CHKPT / LOG_END_CHKPT 짝을 적는다. 그 짝에는 활성 트랜잭션 스냅샷과, 페이지 버퍼의 더티 목록에서 뽑아낸 redo-LSA 힌트가 담긴다. 힌트가 log_Gl.hdr.chkpt_lsa를 앞으로 밀어 두므로, 다음 analysis 패스는 그 LSA 아래 부분을 통째로 건너뛸 수 있다. 체크포인트가 더티 페이지를 모두 강제로 flush하지는 않는다. 스냅샷만 잡아 두고, bgwriter와 페이지 버퍼 victim 경로가 그 뒤를 따라잡는다는 전제로 굴러간다. 세부: cubrid-checkpoint.md.
복구는 세 패스다. analysis 패스는 chkpt_lsa부터 앞으로 훑으며 활성 트랜잭션 표를 다시 짠다. redo 패스는 영향을 받은 페이지마다 페이지 변경 레코드를 다시 적용한다. 페이지별 워커 풀로 일감을 병렬로 분배한다. undo 패스는 활성 트랜잭션의 WAL을 역방향으로 걸으며 보상 로그 레코드를 적용한다. 레코드 종류별 디스패치는 RV_fun[] 표를 거친다. 레코드마다 RVCODE가 박혀 있고, 그 코드에 대응하는 복구 핸들러가 정적으로 등록되어 있다. 세부: cubrid-recovery-manager.md, cubrid-2pc.md(in-doubt 트랜잭션이 analysis 패스에서 드러난다).
Vacuum은 MVCC 회수 엔진이다. 가장 오래된 가시 MVCCID 워터마크 아래에서 WAL을 정해진 크기의 블록 단위로 앞으로 훑어 가며, 마스터가 워커 풀로 블록별 일감을 흘려 보낸다. 워터마크 아래의 delete-MVCCID로 표시된 죽은 버전이 heap 페이지와 B+Tree 잎에서 물리적으로 제거된다. 삭제된 파일은 따로 추적해 두어, vacuum이 어느 클래스에도 속하지 않는 페이지를 쫓아가지 않도록 막는다. 세부: cubrid-vacuum.md.
Double-write buffer가 내구성 이야기를 마무리한다. 더티 페이지는 모두 순차 구조의 정해진 크기 DWB 볼륨에 올라가 fsync된 다음에야 홈 쓰기가 떨어진다. 홈 페이지가 찢긴 경우, 재시작 시 로그 재실행이 시작되기 전에 DWB 사본으로 페이지를 복원한다. 세부: cubrid-double-write-buffer.md.
이 절을 가로지르는 작은 사실이 셋 더 있다. (i) MVCCID 발급은 읽기 전용 작업에서는 일어나지 않는다. 쓰기만 전역 카운터에서 MVCCID를 끌어다 쓰므로, 읽기 부하가 무거워도 활성 MVCCID 집합 크기는 일정 수준에서 유지된다. (ii) 락과 페이지 래치는 일부러 분리되어 있다. 페이지 래치는 짧고 BCB 안에 묻혀 있으며 격리와는 무관하다. cubrid-page-buffer-manager.md의 “Lock vs latch separation” 절을 함께 보면 좋다. (iii) 백업, 복원, flashback이 모두 복구 매니저가 다시 도는 같은 WAL을 발판으로 삼는다(cubrid-backup-restore.md, cubrid-flashback.md).
분산 계층
섹션 제목: “분산 계층”CUBRID의 분산 계층은 단일 cub_server를 master/standby HA 클러스터의 한 식구로 묶고, 변경 스트리밍과 flashback을 같은 WAL 위에서 노출하며, XA 주도 2PC를 받쳐 준다.
flowchart TB
subgraph CLUSTER["HA cluster"]
M["master cub_master + cub_server"]
S1["slave cub_master + cub_server"]
S2["slave cub_master + cub_server"]
M <-- UDP gossip --> S1
M <-- UDP gossip --> S2
S1 <-- UDP gossip --> S2
end
subgraph REPLICATION["Logical-log replication"]
CL["copylogdb<br/>(ships log volumes master->slave)"]
AL["applylogdb<br/>(la_apply_log_file)"]
REPLD["LOG_REPLICATION_DATA / _STATEMENT"]
M -- WAL --> CL
CL -- log archives --> S1
S1 -- reads archives --> AL
AL -- per-record dispatch --> S1
M -- emits --> REPLD
REPLD -. carried in WAL .-> AL
end
subgraph CDC["Change Data Capture"]
CDCAPI["cdc_∗ API<br/>(LOG_SUPPLEMENTAL_INFO walker)"]
CDCCLIENT["downstream consumer"]
M -- log_reader --> CDCAPI
CDCAPI --> CDCCLIENT
end
subgraph TWOPC["XA 2PC"]
COORD["coordinator<br/>(XA tm or internal)"]
TPCFSM["LOG_2PC_PREPARE / _COMMIT_DECISION"]
INDOUBT["in-doubt recovery<br/>(analysis pass)"]
COORD --> TPCFSM
TPCFSM --> M
INDOUBT -. surfaces from .-> TPCFSM
end
subgraph BACKUP["Backup / Restore / Flashback"]
BKP["backupdb<br/>(start_lsa marker + log archive)"]
RST["restoredb<br/>(volume restore + log replay)"]
FB["flashback<br/>(per-tx summary + replay)"]
M -- volumes --> BKP
M -- WAL --> BKP
BKP --> RST
M -- WAL --> FB
end
분산 결정은 CUBRID의 “정족수 합의보다는 노드 자기 판단”이라는 자세를 따른다.
- Heartbeat는 노드마다 따로 돈다.
cub_master가 자기 타이머에 맞춰 동료 표에 점수를 매기고, 가장 작은 점수가 마스터가 된다. failover는TO_BE_MASTER를 거치며, 점수를 한 번 더 매겨 거른다. 합의 프로토콜은 없다. 노드마다 자기 동료 표만 보고 그 자리에서 결정한다. witness 호스트(ha_ping_hosts)가 split-brain을 막는 보조 장치 역할을 한다. 세부:cubrid-heartbeat.md. - HA 복제는 논리 로그 기반이다. master 엔진이 DML 중에 물리 WAL과 함께
LOG_REPLICATION_DATA/LOG_REPLICATION_STATEMENT레코드를 적는다. 별도의copylogdb데몬이 아카이브된 로그 볼륨을 slave로 보내고, slave 쪽에서는applylogdb가la_apply_log_file으로 그것을 앞으로 훑어 가며 레코드 종류별로 알맞은 자리에 분배해서 직렬적이고 트랜잭션 일관된 재실행을 만들어 낸다. 세부:cubrid-ha-replication.md. - CDC는 같은 WAL을 앞으로 훑되, 보는 대상은
LOG_SUPPLEMENTAL_INFO레코드다. 경로는log_reader다. 새cdc_*API가 이 경로를 외부 소비자에게 내주고, 기존la_*HA applier도 내부 구조는 동일하다. 세부:cubrid-cdc.md. - 2-phase commit은 prepare 상태의 로그 레코드가 크래시를 넘기는 형태로 짜여 있다. 코디네이터와 참여자 FSM이
LOG_2PC_*레코드에 기록되고, in-doubt 트랜잭션은 복구의 analysis 패스에서 드러난 뒤 기록된 결정에 따라 커밋 또는 abort된다. XA가 끌어가는 분산 트랜잭션과 내부 중첩 코디네이터가 같은 기계를 공유한다. 세부:cubrid-2pc.md. - Flashback은 “시간 A에서 B 사이에 트랜잭션 T1..Tn이 한 일은 무엇인가”라는 물음에 답한다. 로그를 두 단계로 앞으로 훑는다. 트랜잭션별 요약 한 패스, 그 다음 트랜잭션별 자세한 log-info 한 패스다. 항목 형식은 CDC와 같지만, 대상은 아카이브된 로그 볼륨이다. 세부:
cubrid-flashback.md. - 백업/복원은 온라인 물리 백업이다. 데이터 볼륨의 스냅샷 한 장에,
start_lsa마커와 다음 체크포인트가 양쪽 끝을 잡는 로그 레코드 묶음이 따라붙는다. 복원은 사용자가 정한 정지 시각까지 로그를 앞으로 다시 돌려 시점 복구를 만들어 낸다. 세부:cubrid-backup-restore.md.
한 줄로 묶으면 이렇다. WAL은 CUBRID의 유일한 이벤트 로그다. 복구, vacuum, 복제, CDC, flashback, 백업이 모두 같은 레코드 흐름을 읽는다. 차이는 어떤 레코드 종류에 주목하고 어느 방향으로 걷느냐뿐이다.
PL 패밀리
섹션 제목: “PL 패밀리”CUBRID의 절차적 확장(Java 저장 프로시저와 PL/CSQL)은 서버에 박힌 채 돌지 않고 형제 JVM 프로세스에서 돈다. PL을 따로 떼어 놓은 이 설계 덕분에 CUBRID 서버는 JVM의 멈춤, GC 일시 정지, 사용자 코드의 크래시로부터 영향을 받지 않는다.
flowchart LR
subgraph CSERVER["cub_server (C)"]
SP_CALL["SP call site<br/>(qexec_execute_proc)"]
SP_BRIDGE["sp_bridge / sp_send_call_info"]
SP_CTL["sp_pl_socket<br/>(per-session UDS)"]
end
subgraph CUBPL["cub_pl (JVM)"]
LISTENER["listener thread<br/>(accepts session UDS)"]
DISPATCH["dispatch handler"]
JAVASP["JavaSP runtime<br/>(reflection on user JAR)"]
PLCSQL["PL/CSQL runtime<br/>(compiled to Java AST -> JAR)"]
JDBC["server-side JDBC bridge<br/>(callbacks to cub_server)"]
CLASSLOADER["classloader hierarchy<br/>(parent: system, child: SP-class)"]
end
CATALOG[("_db_stored_procedure<br/>_db_stored_procedure_code")]
SP_CALL --> SP_BRIDGE --> SP_CTL
SP_CTL -- UDS / CSS framing --> LISTENER
LISTENER --> DISPATCH
DISPATCH --> JAVASP
DISPATCH --> PLCSQL
JAVASP --> CLASSLOADER
PLCSQL --> CLASSLOADER
JAVASP -- queries / DML --> JDBC
PLCSQL -- queries / DML --> JDBC
JDBC -- CSS frames --> SP_CTL
CSERVER -- catalog rows --> CATALOG
CUBPL -. reads via JDBC .-> CATALOG
PL 패밀리의 윤곽을 그리는 사실 셋이 있다.
(a) 언어는 둘, 런타임은 하나. Java SP와 PL/CSQL이 모두 같은 cub_pl JVM에서 돈다. PL/CSQL은 pl_server 안에서 ANTLR 4 그래머로 파싱되고, CUBRID 고유의 Java AST(DeclProgram / StmtBlock / ExprBinaryOp / loopOpt)로 변환된 뒤 visitor가 Java 소스로 풀어 낸다. 그 소스를 인프로세스 javax.tools.JavaCompiler로 컴파일하고, JAR(Base64)로 묶어 C 쪽 compile_handler에 돌려 보낸다. 그러면 sp_add_stored_procedure_code가 그 JAR을 Java SP catalog 행 옆에 함께 기록한다. 호출 시점에 두 경로는 디스패치 핸들러 입장에서 동일해 보인다. 세부: cubrid-pl-plcsql.md, cubrid-pl-javasp.md.
(b) JVM은 JDBC를 거쳐 서버로 돌아온다. 저장 프로시저가 질의나 DML을 던질 때, JVM은 C 쪽 서버를 인프로세스로 부르지 않는다. SP 호출이 들어왔던 그 세션의 UDS를 거쳐, 서버 쪽 JDBC 드라이버로 다시 들어온다. 엔진 입장에서 PL은 권한이 부여된 클라이언트 한 명이다. 같은 CSS 프레이밍, 같은 NRP 디스패치, 같은 prepared-statement 캐시를 그대로 쓴다.
(c) 클래스로더 계층은 JavaSP만의 영역이다. PL/CSQL 클래스는 CUBRID가 직접 만든 것이라 신뢰된다. 반면 사용자 JAR은 자식 클래스로더 아래에 올리고, SP 코드가 할 수 있는 일을 제한하는 보안 매니저를 함께 단다. 사용자 JAR에 대한 리플렉션 디스패치는 JavaSP의 몫이다.
세부 문서는 둘이다. Java SP의 와이어 프로토콜과 JVM IPC는 cubrid-pl-javasp.md. ANTLR 그래머, AST, 이미터, JAR 패키징은 cubrid-pl-plcsql.md.
모든 계층을 가로지르는 인프라
섹션 제목: “모든 계층을 가로지르는 인프라”이 서브시스템들은 어느 한 층에도 깔끔하게 끼지 않는다. 모든 층이 다 같이 건드리는 부품이기 때문이다. 여기서는 한 단락씩만 짚고 넘어간다. 자세한 내용은 각자의 세부 문서에 있다.
- Boot.
cubrid-boot.md는cub_server의 시작을 다룬다. 최초createdb는 볼륨을 포맷하고 root-class catalog를 부트스트랩한다. 재시작은log_recovery의 세 패스 다시-돌리기로 넘어간다. 클라이언트 쪽은boot_restart_client가 네트워크 너머의xboot_register_client에 연결된다. boot은 모든 서브시스템의 init 함수가 차례로 깨어나는 자리이고, 스레드 풀, 페이지 버퍼, 로그 매니저, 락 매니저, catalog가 모두 이때 올라온다. - 서버 세션.
cubrid-server-session.md는 클라이언트별 서버측 상태 그릇을 다룬다. 정수 session id를 키로 latch-free 해시에 들어가는SESSION_STATE다. 연결 항목에 캐시되어 있어 요청 조회가 O(1)이고, 스레드별 TDES에 묶여 있어 모든 서버 요청이 알맞은 트랜잭션 디스크립터, prepared-statement 캐시, 파라미터 묶음 위에 떨어진다. - 스레드 + 워커 풀(레거시).
cubrid-thread-worker-pool.md는 서버 안의 모든 실행 스레드가 어떻게 짜여 있는지 정리한다. 스레드별cubthread::entry컨텍스트, 질의/vacuum/loaddb/병렬-redo를 굴리는worker_pool템플릿(코어 → 워커 → 작업 큐), 주기적인 백그라운드 flush와 detect를 굴리는daemon+looper패턴, 락 매니저와 페이지 버퍼가 공유하는 lock-free 해시맵, 스레드별 추적기를 내장한 무거운csectRW 프리미티브가 차례로 정리된다. - Thread manager NG (CBRD-26177).
cubrid-thread-manager-ng.md는 connection/worker 풀의 재설계 결과를 다룬다. 한도가 그어진 epoll 기반 connection worker, 재배치와 자동 확장을 중개하는 코디네이터, 송수신 예산, 워커별 컨텍스트 freelist, 원자 카운터 없는 통계가 핵심이다. 기존의 연결당 스레드 +max_clientstask-worker 구조를 대체한다. - 네트워크 프로토콜.
cubrid-network-protocol.md는 모든 서버 진입점을 다룬다. 진입점은NET_SERVER_*옵코드 하나로 묶여, 정적(action_attribute, handler)표를 거쳐 흘러 들어간다. 연결은cub_master가 받고,master::connector가 Unix 도메인 소켓 너머로 그것을cub_server워커에 넘긴다. 그 다음 epoll 기반 connection worker가 이어받아 CSS 프레임 패킷을 읽고, 양쪽 짝을 이루는or_pack_*/or_unpack_*마샬링으로 요청을 풀어낸다. - Broker.
cubrid-broker.md는 CAS 풀을 다룬다.cub_broker부모가cub_cas워커 풀을 정해진 크기만큼 fork해 두고, TCP 리스너를 하나 열어 두고, 받은 클라이언트 소켓은SCM_RIGHTS파일 디스크립터 전달로 Unix 도메인 랑데부 채널을 거쳐 비어 있는 CAS에게 넘긴다. CAS는 CSS 프레임 트래픽을 그대로cub_server로 흘려 보낸다. 전 과정이 SysV 공유 메모리 구역 하나로 조율된다. - 에러 관리.
cubrid-error-management.md는 전역ER_*enum, 스레드별cuberr::context(기본er_message와 중첩 에러 더미), printf 형식 컴파일을 가진er_set패밀리, NetBSD/FreeBSDnl_catd형식의 지역화 카탈로그(cubrid.msg,csql.msg,utils.msg), 크기 기반 회전이 붙는cubrid_*.err로그 파일, 그리고 에러를OR_INT세 필드 + 메시지 문자열로 평탄화해서 클라이언트와 서버 사이를 건너가게 하는 와이어 포맷을 다룬다. - 시스템 파라미터.
cubrid-system-parameters.md는prm_Def[]등록부, 섹션 선택을 가진cubrid.confINI 파서, 환경 변수 덮어쓰기,db_set_system_parametersSQL 경로, 세션별SESSION_PARAM배열을 다룬다. 다른 모든 서브시스템이prm_get_*_value로 읽어 오는 단일 해석 흐름이다. - 모니터링.
cubrid-monitoring.md는 두 층의 카운터 시스템을 다룬다. 통계 묶음을 등록하고 트랜잭션별 시트를 받쳐 주는 C++ 템플릿 기반cubmonitor라이브러리, 그리고SHOW STATS와statdump가 쓰는 기존 C 방식perf_monitor/pstat_Metadata배열이 양 축이다. 여기에 서브시스템 단위 모니터(예: vacuum 워커별 overflow-page 임계값 추적기)가 추가로 붙는다. - DBI / CCI.
cubrid-dbi-cci.md는 클라이언트 API를 다룬다.boot_cl과network_cl위에 얹힌 단일db_*C API가 모든 구문을DB_SESSION안의 네 단계 FSM(Initial → Compiled → Prepared → Executed)으로 끌고 가고, broker 쪽 와이어 드라이버(ux_database_connect,ux_prepare,ux_execute,ux_fetch,ux_end_tran)는 평평한server_fn_table로 분기된다. 그 덕에 JDBC, CCI, ODBC, Python, PHP가 모두 같은db_*코어로 엔진에 닿는다. - SA / CS 런타임.
cubrid-sa-cs-runtime.md는 같은 소스 트리가 세 가지 모드(cub_server(SERVER_MODE),libcubridsa(SA_MODE),libcubridcs(CS_MODE))로 빌드되는 방식을 다룬다. 그 덕에 관리 도구는 엔진 전체를 자기 프로세스에 끌어들여 디스크 데이터베이스에 직접 손대거나, 따로 도는 데몬과 CSS로 통신할 수 있다. 어느 경로로 갈지는 도구별 분류(SA_ONLY / CS_ONLY / SA_CS)와 런타임dlopen(libcubridsa.so또는libcubridcs.so)이 결정한다.
이 면을 마저 채우는 가로지르는 문서가 더 있지만 범위는 좁다. 코드셋 변환과 로케일 인식 비교는 cubrid-charset-collation.md, IANA tzdata 컴파일과 DATETIMETZ/TIMESTAMPTZ 변환은 cubrid-timezone.md, 자기 들여다보기를 위한 가상 스캔은 cubrid-show-commands.md, 한 패턴을 공유하는 세 종류의 런타임 캐시 묶음은 cubrid-runtime-memoization.md다.
베이스 / 인프라 받침대(커스텀 메모리 할당기와 락프리 자료구조)는 자기만의 서브카테고리(cubrid-overview-base-infra.md)를 따로 갖는다. AREA(고정 크기 객체용 슬랩 풀), 스레드별 Lea-힙 private 할당기, 락프리 6개 문서(overview, transactional reclamation, bitmap, freelist, hashmap, circular queue)가 모두 그쪽에 모여 있다. 위쪽 모든 계층이 의존하기보다 합성에 같이 쓰는 부품이다.
어디서 읽기 시작할 것인가
섹션 제목: “어디서 읽기 시작할 것인가”목표가 다르면 트리에 들어가는 자리도 다르다. 아래 표가 첫 문서와 그 뒤를 잇는 두세 문서를 짚어 준다.
| 목표 | 시작 | 그 다음 |
|---|---|---|
| SELECT 한 문장 끝까지 따라가기 | cubrid-rpath-select.md | cubrid-broker.md -> cubrid-network-protocol.md -> cubrid-parser.md -> cubrid-semantic-check.md -> cubrid-query-rewrite.md -> cubrid-query-optimizer.md -> cubrid-xasl-generator.md -> cubrid-query-executor.md -> cubrid-scan-manager.md -> cubrid-list-file.md -> cubrid-cursor.md |
| COMMIT(쓰기 경로) 따라가기 | cubrid-rpath-write.md | cubrid-locator.md -> cubrid-heap-manager.md -> cubrid-btree.md -> cubrid-mvcc.md -> cubrid-lock-manager.md -> cubrid-prior-list.md -> cubrid-log-manager.md -> cubrid-double-write-buffer.md -> cubrid-page-buffer-manager.md |
| 서버 재시작 따라가기 | cubrid-rpath-recovery.md | cubrid-boot.md -> cubrid-checkpoint.md -> cubrid-recovery-manager.md -> cubrid-2pc.md -> cubrid-vacuum.md |
| 클래스에 대한 DDL 따라가기 | cubrid-ddl-execution.md | cubrid-class-object.md -> cubrid-catalog-manager.md -> cubrid-locator.md -> cubrid-xasl-cache.md -> cubrid-trigger.md -> cubrid-partition.md |
| HA failover 따라가기 | cubrid-heartbeat.md | cubrid-ha-replication.md -> cubrid-cdc.md -> cubrid-2pc.md -> cubrid-flashback.md -> cubrid-backup-restore.md |
| 저장 프로시저 호출 따라가기 | cubrid-pl-javasp.md | cubrid-pl-plcsql.md -> cubrid-network-protocol.md -> cubrid-server-session.md -> cubrid-dbi-cci.md |
| 백업 / 복원 따라가기 | cubrid-backup-restore.md | cubrid-log-manager.md -> cubrid-checkpoint.md -> cubrid-recovery-manager.md -> cubrid-flashback.md |
| 대량 적재 따라가기 | cubrid-loaddb.md | cubrid-locator.md -> cubrid-heap-manager.md -> cubrid-btree.md -> cubrid-statistics.md -> cubrid-sa-cs-runtime.md |
| CCI / JDBC 클라이언트 따라가기 | cubrid-dbi-cci.md | cubrid-broker.md -> cubrid-network-protocol.md -> cubrid-cursor.md -> cubrid-server-session.md |
| CUBRID가 왜 지금 같은 모양인지 알기 | cubrid-design-philosophy.md | 이 개요 → 관심 가는 서브시스템 |
서브카테고리 지도
섹션 제목: “서브카테고리 지도”cubrid 트리는 여덟 개의 서브카테고리로 나뉜다. 세부 문서마다 frontmatter에 subcategory:를 적어 두며, 아래 표는 그 분류를 그대로 보여 주는 길잡이다. 요약 칸은 각 문서의 frontmatter summary:에서 그대로(또는 약간 줄여서) 가져왔다.
server-architecture (프로세스 단위 모양)
섹션 제목: “server-architecture (프로세스 단위 모양)”| 문서 | 요약 |
|---|---|
cubrid-boot.md | 서버 시작, 첫 createdb, 재시작과 복구 디스패치, 클라이언트 연결 — 모든 서브시스템의 init이 차례대로 깨어난다. |
cubrid-broker.md | cub_broker 부모와 cub_cas 워커 풀, SCM_RIGHTS 파일 디스크립터 랑데부, SysV 공유 메모리 제어 평면, ACL, 모니터링. |
cubrid-dbi-cci.md | 통합된 db_* 클라이언트 API(DB_SESSION 안의 Initial -> Compiled -> Prepared -> Executed FSM), 그리고 server_fn_table로 분기되는 broker 쪽 와이어 드라이버(ux_*). |
cubrid-error-management.md | ER_* enum, 스레드마다의 cuberr::context, 중첩 에러 더미, er_set 패밀리, nl_catd 메시지 카탈로그, _latest-symlink가 붙는 회전 에러 로그, 와이어 전파 모양. |
cubrid-locator.md | OID 워크스페이스에서 서버 쪽 locator_*_force 다리로 묶여 가는 일괄 insert/update/delete, 거기서 heap/btree/lock/log/FK/replication으로 흩어지는 길. |
cubrid-loaddb.md | 대량 적재기 — CUBRID 형식 객체 파일을 토큰화하고, Bulk-Update 락 아래 서버 쪽 워커 풀로 묶어 보내며, direct-path locator_multi_insert_force로 쓰고, 적재가 끝난 뒤 통계를 다시 짠다. |
cubrid-monitoring.md | C++ cubmonitor 라이브러리와, SHOW STATS / statdump용 옛 perf_monitor / pstat_Metadata, 그리고 서브시스템마다의 즉석 모니터들. |
cubrid-network-protocol.md | NET_SERVER_* 옵코드 표, cub_master의 accept와 master::connector의 UDS 인계, epoll 기반 connection worker, 양쪽이 짝을 이루는 or_pack_* 마샬링. |
cubrid-sa-cs-runtime.md | 같은 소스 트리에서 세 가지 빌드(SERVER_MODE / SA_MODE / CS_MODE), 도구마다의 분류 SA_ONLY / CS_ONLY / SA_CS, 런타임 dlopen(libcubridsa.so 또는 libcubridcs.so). |
cubrid-server-session.md | 정수 session id로 키 잡힌 latch-free 해시 위의 SESSION_STATE, 연결 항목에 캐시되어 있고, 스레드마다의 TDES에 묶인다. |
cubrid-system-parameters.md | prm_Def[] 등록부, 섹션 선택이 있는 cubrid.conf INI 파싱, 환경 변수 덮어쓰기, db_set_system_parameters SQL 길, 세션마다의 SESSION_PARAM. |
cubrid-thread-worker-pool.md | cubthread::entry 컨텍스트, worker_pool 템플릿, daemon+looper 무늬, lock-free 해시맵, 무거운 csect RW 프리미티브. |
cubrid-thread-manager-ng.md | CBRD-26177로 새로 짠 모양 — 한도가 그어진 epoll connection worker, 코디네이터가 끄는 재배치, 보내기/받기 예산, 워커마다의 freelist. |
storage-engine (계층 스토리지 스택)
섹션 제목: “storage-engine (계층 스토리지 스택)”| 문서 | 요약 |
|---|---|
cubrid-disk-manager.md | 볼륨 / 섹터 / 파일 / 페이지, 두 단계 섹터 예약, 영구 vs 임시 디스크 캐시, 자라는 볼륨, 세 가지 확장형 데이터 표. |
cubrid-page-buffer-manager.md | BCB 배열, 세 구역 LRU(프라이빗 + 공유, 쿼터 있음), lock-free 큐로 victim을 곧장 넘기는 길, BCB마다 따로 짠 read/write/flush 래치. |
cubrid-double-write-buffer.md | 홈 쓰기 전에 fsync되는 순차 staging 볼륨 — 페이지 버퍼와 데이터 파일 사이의 찢긴 쓰기 보호. |
cubrid-heap-manager.md | 슬롯 페이지, 아홉 가지 레코드 종류, INSERT/UPDATE/DELETE/READ 흐름, 레코드 헤더 안쪽의 MVCC 버전 관리, 핫 패스 캐시. |
cubrid-btree.md | 슬롯 페이지 노드, key |
cubrid-extendible-hash.md | EHID에 닻을 내린 디렉터리 파일(포인터 수가 두 배씩), 이진 검색을 가진 슬롯 버킷 페이지, 시스템-op 괄호로 묶인 split/merge, RVEH_* WAL 레코드. |
cubrid-overflow-file.md | heap 큰 레코드와 B+Tree overflow OID 페이지 사슬, FILE_MULTIPAGE_OBJECT_HEAP / FILE_BTREE_OVERFLOW_KEY / 트리마다의 OID overflow, 크래시 안전을 위한 WAL 규율. |
cubrid-lob.md | 데이터 볼륨 바깥의 파일로 저장되는 BLOB/CLOB, locator-URI 이름, TDES 위에 트랜잭션마다의 red-black 트리, 커밋/롤백 시 파일 시스템 맞춤. |
cubrid-tde.md | 두 단계 키 계층, 페이지마다의 nonce를 가진 AES-256-CTR 또는 ARIA-256-CTR, flush 시 암호화 / read 시 복호화 훅, 따로 둔 <db>_keys 마스터 키 파일, 파일마다의 TDE 깃발. |
base-infra (커스텀 할당기 + 락프리 자료구조)
섹션 제목: “base-infra (커스텀 할당기 + 락프리 자료구조)”| 문서 | 요약 |
|---|---|
cubrid-private-allocator.md | 스레드별 Lea-힙 아레나(Doug Lea의 dlmalloc을 customheaps 아래 자체 사본으로 두고 THREAD_ENTRY마다 한 개씩 등록). db_private_alloc / _free / _realloc 매크로가 SERVER_MODE에서는 스레드의 힙으로, CS_MODE에서는 워크스페이스로, SA_MODE에서는 PRIVATE_MALLOC_HEADER 태그 디스패치로 갈래를 나눈다. C++ STL 래퍼 cubmem::private_allocator<T>, private_unique_ptr<T>, PRIVATE_BLOCK_ALLOCATOR. |
cubrid-common-area.md | 슬랩 식 풀 할당자. 256블록을 묶은 BLOCKSET 배열을 사슬로 잇고, 블록마다 락프리 비트맵을 앞에 두고, 흔한 경우는 힌트 포인터 한 개로 단축한다. DB_VALUE, TP_DOMAIN, OBJ_TEMPLATE, set 객체 등을 받친다. |
cubrid-lockfree-overview.md | CUBRID 락프리 자료구조 지도 — legacy C lock_free.{h,c} 계열과 신형 C++ lockfree::* 네임스페이스가 한 개의 트랜잭션 회수 척추 위에 어떻게 올라타는지. |
cubrid-lockfree-transaction.md | system / table / descriptor / address-marker 회수. 자료구조마다 트랜잭션 id 한 개, 스레드별 descriptor가 읽기 구간을 묶고, 주기적으로 가장 작은 살아 있는 id를 스캔해 freelist에 어느 노드를 풀어 줘도 되는지 알려준다. |
cubrid-lockfree-bitmap.md | 청크 단위 어토믹 비트맵. std::atomic<unsigned int> 청크들, 두 가지 청크 스타일, CAS 비트 플립, SERVER_MODE에서 get_entry마다 어토믹으로 진행하는 라운드로빈 시작점 힌트. |
cubrid-lockfree-circular-queue.md | 두 개의 커서 어토믹과 슬롯별 블록 플래그 워드를 가진 고정 용량 MPMC 링. vacuum 로그 블록 분배, 페이지 버퍼 victim 핸드오프, CDC 로그 정보 전달에 쓰인다. |
cubrid-lockfree-freelist.md | 타입드 freelist<T>. 사용 가능 스택 한 개와 lazy로 갈아 끼우는 백버퍼 블록, 페이로드 타입의 on_reclaim 훅, 백버퍼 시간으로 ABA 윈도우가 명시적으로 제한된 pop 경로. |
cubrid-lockfree-hashmap.md | Harris–Michael 체인 해시 — 옵션으로 엔트리별 뮤텍스, legacy C lf_hash_*와 신형 C++ lockfree::hashmap<K,T> 두 구현이 cubthread::lockfree_hashmap<K,T> 다리로 묶이고, m_type ∈ {OLD, NEW}은 init 시점에 PRM_ID_ENABLE_NEW_LFHASH가 결정한다. |
query-processing (parse → execute → return)
섹션 제목: “query-processing (parse → execute → return)”| 문서 | 요약 |
|---|---|
cubrid-parser.md | Flex/Bison 파이프라인 — 단일 버퍼 YY_INPUT, GLR Bison 그래머, parser_new_node, 함수 포인터 배열 셋이 달린 다형 태그 PT_NODE, PARSER_CONTEXT마다의 블록 할당기. |
cubrid-semantic-check.md | pt_check_with_info 드라이버가 네 패스(이름 해석, where 절 집계 검사, host variable 치환, 구문 인식 semantic_check_local)를 잇고, 그 뒤에 pt_cnf로 술어 CNF를 만든다. |
cubrid-query-rewrite.md | INST_NUM/ORDERBY_NUM/GROUPBY_NUM으로 LIMIT을 끌어내리기, 뷰 인라인, 서브쿼리 평탄화, 술어 축소, 자동 매개변수화, plan-time 다중 범위 LIMIT 최적화. |
cubrid-query-optimizer.md | QO_ENV 질의 그래프(QO_NODE / QO_SEGMENT / QO_TERM), 2^N join_info 벡터 위에서 부분 → 전체로 가는 동적 프로그래밍 조인 열거, System R 식 고정 cpu/io + 가변 cpu/io 비용 모델, QO_PLAN 마무리. |
cubrid-xasl-generator.md | QO_PLAN -> XASL_NODE 트리, 재귀적인 gen_outer/gen_inner 걷기, aptr/dptr/scan_ptr 슬롯, REGU_VARIABLE / ACCESS_SPEC / OUTPTR_LIST 하위 IR, xts_* 오프셋 표 직렬화. |
cubrid-xasl-cache.md | 재작성된 SQL의 SHA-1과 time_stored를 키로 잡은 서버 전역 latch-free 해시맵, 단일 32비트 cache_flag로 도는 참조 카운트, 재컴파일 임계(RT) 어긋남 보호, DDL 시 클래스마다의 OID 의존 목록 무효화. |
cubrid-query-executor.md | Volcano 식 XASL 인터프리터 — qexec_execute_main_block이 xasl->type으로 분기하고, SCAN_ID 연산자 위에서 한결같은 open/next/close를 굴리며, 결과를 XASL마다의 list file로 밀어 넣는다. |
cubrid-scan-manager.md | 다형 SCAN_ID 손잡이와 open/start/next/end/close 약속, SCAN_TYPE마다 분기되는 heap, B+Tree, list-file, set, value, JSON-table, dblink, show, parallel-heap, method 스캔. |
cubrid-query-evaluator.md | eval_pred가 T_PRED / T_EVAL_TERM의 PRED_EXPR 트리를 세 값 논리 아래에서 걷고, fetch_peek_dbval이 REGU_VARIABLE::type으로 분기하며, eval_fnc가 자주 등장하는 단일 모양의 술어를 미리 빠른 길로 만들어 둔다. |
cubrid-scalar-functions.md | 연산자 프리미티브 계층 — arithmetic.c, numeric_opfunc.c, string_opfunc.c, query_opfunc.c, crypt_opfunc.c, string_regex_*. BCD 수, collation-aware 문자열, RE2와 std::regex 사이의 전환. |
cubrid-list-file.md | QFILE_LIST_ID 사슬 페이지 추상화, 질의마다의 QMGR_TEMP_FILE membuf-에서-FILE_TEMP로 가는 바닥, 모든 풀려 나온 튜플 흐름이 함께 쓰는 한결같은 open/add/scan/close 약속. |
cubrid-post-processing.md | qexec_groupby와 qexec_execute_analytic — sort 기반과 hash 기반 GROUP BY를 런타임에 고르고, 해시 테이블이 max_agg_hash_size를 넘기면 외부 정렬로 옮겨간다. |
cubrid-hash-join.md | query_hash_join.c의 Build/Probe 드라이버가 HASH_LIST_SCAN을 다시 쓰고, 세 가지 테이블 배치(인메모리 mht_hls, 메모리+파일 혼합, 확장형 FHS), spill 시 grace 식 동치 해시 분할이 따라온다. |
cubrid-parallel-query.md | 전역 병렬 질의 워커 풀 하나, 페이지 수에서 길어 올린 compute_parallel_degree(), 연산자마다 따로 둔 세 가지 관리자(병렬 heap-scan, hash-join 빌드/실행, query-execute 펴기). |
cubrid-external-sort.md | 두 단계 — 교체-선택 식 run 생성기(sort_inphase_sort)와 FILE_TEMP run 위에서 도는 균형 k-way 병합(sort_exphase_merge) — 단일한 콜백 진입점 하나로 가린다. |
cubrid-cursor.md | 서버 쪽 QFILE_LIST_ID 위에 얹히는 클라이언트 쪽 CURSOR_ID, qfile_get_list_file_page 페이징, 길이 접두 식 팩 행 풀이, OID 미리 가져오기, 세션 범위의 holdable 커서 목록을 거친 holdability. |
cubrid-runtime-memoization.md | 같은 패턴(DB_VALUE 배열 해시 키, 가득 차면 실패하는 예산, hit-ratio 보호)을 쓰는 세 가지 따로 도는 캐시가 세 가지 수명 범위에서 — XASL마다의 sq_cache, BTID마다의 fpcache, XASL마다의 memoize::storage. |
cubrid-partition.md | 마스터 클래스 + 자식 클래스 N개, 마스터의 SM_PARTITION 위에 파티션 규칙(범위 / 해시 / 목록), optimize-time pruning + execute-time route + 파티션마다의 스캔 디스패치를 위한 서버 쪽 PRUNING_CONTEXT. |
cubrid-serial.md | 시퀀스마다 한 줄씩 들어가는 _db_serial, 옵션의 클라이언트 쪽 캐시를 곁들인 단독 OID 락 진전, 그리고 <class>_ai_<attr> 시리얼로 합성되는 AUTO_INCREMENT 컬럼. |
txn-recovery (동시성, 로깅, 복구)
섹션 제목: “txn-recovery (동시성, 로깅, 복구)”| 문서 | 요약 |
|---|---|
cubrid-transaction.md | 서버 전역 trantable의 TDES 디스크립터, 격리 수준 분기(SI / 락 기반), 시스템 ops를 거친 세이브포인트 주도의 중첩된 부분 롤백 경계. |
cubrid-mvcc.md | MVCCID 발급, mvcctable::build_mvcc_info에서 만들어 내는 트랜잭션마다의 스냅샷, 활성 MVCCID 추적, vacuum과의 손맞춤. |
cubrid-lock-manager.md | OID마다 다중 단위(multi-granularity) 락 부여/전환/회수, 데드락 잡기를 위한 트랜잭션 wait-for 그래프. |
cubrid-prior-list.md | 단방향 사슬의 LOG_PRIOR_NODE 생산자 큐, 짧게 잡았다 푸는 단일 뮤텍스, 로그 임계 구역 안에서 도는 로그-flush 데몬의 비움, 묶어서 흘려 보내는 그룹 커밋. |
cubrid-log-manager.md | WAL 레코드 모양, LSA 이름, 메모리 안의 prior-list / append-page 파이프라인, 아카이브 볼륨, ACID-D 약속의 받침대. |
cubrid-checkpoint.md | fuzzy-ARIES 체크포인트 — 활성 트랜잭션 스냅샷과 redo-LSA 힌트를 함께 적은 주기 데몬의 LOG_START_CHKPT/LOG_END_CHKPT 짝, log_Gl.hdr.chkpt_lsa의 전진. |
cubrid-recovery-manager.md | 가장 최근 체크포인트 LSA에 닻을 내린 세 패스 재시작, RV_fun[]을 거친 레코드 종류별 분기, 페이지마다의 워커 풀로 도는 병렬 redo. |
cubrid-vacuum.md | 가장 오래된 가시 MVCCID 워터마크 아래에서 정해진 크기의 블록 단위로 도는 앞으로 WAL 걷기, 마스터에서 워커로 가는 일 흩어 보내기, 사라진 파일 추적기. |
cubrid-2pc.md | LOG_2PC_EXECUTE를 통한 코디네이터와 참여자 FSM, prepare 상태 로그 레코드가 크래시를 넘기고, ARIES analysis 패스에서 in-doubt 트랜잭션이 떠오른다. |
cubrid-backup-restore.md | 온라인 물리 백업 — start_lsa와 다음 체크포인트로 양 끝이 잡히는 데이터 볼륨 스냅샷 + 로그 레코드 묶음, 복원 시점에 시점 복구를 위한 앞으로 다시 돌리기. |
ddl-schema (catalog, 스키마 그래프, 인증, 통계)
섹션 제목: “ddl-schema (catalog, 스키마 그래프, 인증, 통계)”| 문서 | 요약 |
|---|---|
cubrid-catalog-manager.md | 전용 catalog(CTID) 안의 클래스마다의 디스크 표현 + 통계, 정해진 root-class OID에서 부트스트랩되는 짝지어진 시스템 클래스(_db_class, _db_attribute, _db_index, …). |
cubrid-class-object.md | 클라이언트 쪽 워크스페이스 안의 메모리 SM_CLASS 그래프 — 속성 / 메서드 / 파티션 / 제약 / 트리거. catcls_*가 이 그래프와 디스크 catalog 레코드 사이를 잇는다. |
cubrid-ddl-execution.md | do_statement 디스패치가 do_create_entity / do_alter / do_drop으로, SM_TEMPLATE 짜기, sm_finish_class -> update_class -> install_new_representation, locator_add_class + catcls_insert_catalog_classes, sm_bump_local_schema_version. |
cubrid-trigger.md | SQL-99 ECA 능동 규칙 — _db_trigger 인스턴스와 SM_CLASS 위의 TR_TRIGGER 캐시, tr_prepare_class / tr_before_object / tr_after_object 세 짝, OID 더미로 짜인 구문 단위 재귀 보호. |
cubrid-statistics.md | xstats_update_statistics가 heap과 B+Tree를 훑어 카디널리티 / NDV / 잎-페이지 수 / 부분 키 펼침 폭을 만들어 낸다. 이 값은 가장 새 디스크 표현에 적힌다. 클라이언트 쪽 qo_get_attr_info가 그것을 길어 올려 qo_iscan_cost / qo_sscan_cost / qo_equal_selectivity / qo_range_selectivity로 흘려 보낸다. |
cubrid-authentication.md | MOP 키 행으로 들어 있는 사용자 / 비밀번호 / 객체마다의 권한 (db_user, db_password, _db_auth, db_authorization). au_login / au_fetch_class / 클래스마다의 AU_CLASS_CACHE가 SELECT 시점의 권한 조회를 한 번의 비트마스크 검사로 줄여 준다. |
replication-ha (분산, 로그 스트리밍, 변경 잡기)
섹션 제목: “replication-ha (분산, 로그 스트리밍, 변경 잡기)”| 문서 | 요약 |
|---|---|
cubrid-heartbeat.md | UDP 가십으로 도는 클러스터 생사 신호, 노드마다 따로 도는 calc_score로 마스터 뽑기, slave -> to-be-master -> master FSM, 워커 스레드 넷이 도는 잡 큐, witness 호스트(ha_ping_hosts)로 split-brain 막기. |
cubrid-ha-replication.md | 마스터 엔진이 물리적 WAL과 함께 LOG_REPLICATION_DATA / LOG_REPLICATION_STATEMENT를 적는다. copylogdb가 아카이브를 보내고, applylogdb(la_apply_log_file)가 그것을 앞으로 훑어 가며 레코드 종류마다 스토리지 계층의 알맞은 자리로 흩어 보낸다. |
cubrid-cdc.md | cdc_* API가 log_reader로 LOG_SUPPLEMENTAL_INFO 레코드를 훑는다. 옛날 la_* HA applier도 안쪽 모양은 같은 레코드 형식을 함께 쓴다. |
cubrid-flashback.md | 두 단계로 앞으로 도는 로그 걷기(트랜잭션마다의 요약, 그 다음 트랜잭션마다의 자세한 log-info 가져오기), CDC 항목 모양을 함께 쓰며 대상은 아카이브된 로그 볼륨이다. |
pl-language (JVM 안의 절차적 확장)
섹션 제목: “pl-language (JVM 안의 절차적 확장)”| 문서 | 요약 |
|---|---|
cubrid-pl-javasp.md | cub_pl JVM 안의 JavaSP 런타임 — 사용자 JAR에 대한 리플렉션 디스패치, 클래스로더 계층, 보안 sandbox. catalog 행과 전송 채널을 PL/CSQL과 함께 쓴다. |
cubrid-pl-plcsql.md | pl_server JVM에서 ANTLR 4로 파싱되는 PL/CSQL이 CUBRID Java AST로 끌어내려지고, Java 소스로 풀려 나오며, 인프로세스 javax.tools.JavaCompiler로 컴파일되어 Base64 JAR로 묶이고, JavaSP catalog 행 옆에 같이 적힌다. |
i18n-specialty (특수한 일들)
섹션 제목: “i18n-specialty (특수한 일들)”| 문서 | 요약 |
|---|---|
cubrid-charset-collation.md | 네 가지 코드셋(binary, ISO-8859-1, EUC-KR, UTF-8), UCA 가중치 공유 라이브러리로 컴파일되는 LDML locale 규칙, B+Tree, 정렬, 문자열 연산자가 함께 쓰는 함수 포인터 식 LANG_COLLATION vtable. |
cubrid-timezone.md | IANA tzdata를 만들어 낸 timezones.c + libcubrid_timezones.so로 묶어 두고, (zone, gmt-offset-rule, ds-rule) 세 짝을 32비트 TZ_ID에 담으며, LOCAL_STD/LOCAL_WALL/UTC 한정자와 함께 tz_datetime_utc_conv로 벽시계 시각을 풀어낸다. |
cubrid-json-table.md | C++ 스캐너의 커서 더미가 파서가 짜 둔 cubxasl::json_table::node 트리를 걷고, NESTED PATH마다 db_json_iterator_*로 입력 JSON을 펼치며, 잎에서 행을 내보낸다. SCAN_TYPE은 scan_next_scan이 받아 쓴다. |
cubrid-show-commands.md | SHOW <name>이 SELECT * FROM (PT_SHOWSTMT)으로 다시 적히고, S_SHOWSTMT_SCAN으로 분기되며, 튜플은 SHOWSTMT_TYPE마다 따로 둔 start/next/end 함수 포인터에서 그때그때 만들어진다. |
cubrid-compactdb.md | 오프라인 다지기 — 클래스마다 heap을 훑어 매달린 OID 참조를 NULL로 비우고, 빈 heap 페이지를 거두어 들이고, 지난 catalog 표현을 떨궈 내고, heap 파일을 다지는 일. 클라이언트가 끄는 일이고, 클래스 단위로 범위가 잡히며, 번호 붙은 세 패스로 돈다. |