Skip to content

INTERNAL: Extract hash_tree module from coll_set and coll_map#970

Open
zhy2on wants to merge 11 commits into
naver:developfrom
zhy2on:internal/extract-hash-tree-module
Open

INTERNAL: Extract hash_tree module from coll_set and coll_map#970
zhy2on wants to merge 11 commits into
naver:developfrom
zhy2on:internal/extract-hash-tree-module

Conversation

@zhy2on
Copy link
Copy Markdown
Collaborator

@zhy2on zhy2on commented Apr 22, 2026

🔗 Related Issue

⌨️ What I did

insert/update/exist 경로를 hash_tree 모듈로 교체

htree_elem_link 동작

  1. root 초기화: 트리가 비어있으면 root node를 할당하고 space_delta에 반영
  2. leaf 탐색: hcnt[hidx] == -1인 슬롯을 따라 내려가며 삽입할 leaf node를 찾음
  3. duplicate 체크: leaf의 hash chain을 순회하여 동일 key가 있으면 ENGINE_ELEM_EEXISTS 반환
  4. node split: chain 길이가 HTREE_MAX_HASHCHAIN_SIZE(64)에 도달하면 child node를 새로 할당하고 기존 chain을 child로 이전. 삽입 위치도 child 기준으로 업데이트
  5. elem 삽입: 대상 슬롯의 chain 앞에 prepend, root까지의 모든 조상 node의 tot_elem_cnt 증가

map vs set insert 비교

두 collection 모두 htree_elem_link 호출 전에 동일한 순서로 사전 검사를 수행:

map set
sticky check IS_STICKY_COLLFLG + do_item_sticky_overflowed() 동일
overflow check ccnt >= max_map_size ccnt >= max_set_size
duplicate htree_elem_link 내부에서 ENGINE_ELEM_EEXISTS 동일

map은 update 경로(do_map_elem_replace_at)가 추가로 존재

delete 경로를 hash_tree 모듈로 교체

htree_elem_unlink 동작

  1. leaf 탐색: hcnt[hidx] == -1인 슬롯을 따라 내려가며 삭제할 elem이 있는 leaf node를 찾음. 이 때 merge를 위해 par_node와 par_hidx를 함께 추적
  2. elem 탐색: leaf의 hash chain을 순회하여 key가 일치하는 elem을 찾음. 없으면 NULL 반환
  3. elem unlink: chain에서 제거하고 root까지의 모든 조상 node의 tot_elem_cnt 감소
  4. node merge: 삭제 후 node가 비었거나 elem 수가 HTREE_MAX_HASHCHAIN_SIZE / 2 미만인 leaf면 child node를 해제하고 elem들을 par_node의 슬롯에 flat chain으로 복귀

htree_elem_unlink_by_cnt 동작

전체 또는 count개만큼 bulk 삭제:

  1. DFS로 트리를 순회하며 elem을 chain(unlinkelem들의 next 포인터로 연결한 linked list)으로 수집
  2. child node가 sparse해지면(HTREE_MAX_HASHCHAIN_SIZE / 2 미만으로 줄어들면 ) 즉시 merge — child node를 유지할 필요 없이 par_node의 슬롯 하나에 flat chain으로 담을 수 있을 만큼 적어진 것이므로 node를 해제하여 메모리를 절약
  3. 순회 완료 후 root가 비었으면 root node도 해제

map/set get 경로를 hash_tree 모듈로 교체

map vs set get 비교

map set
단건 조회 htree_elem_find (field 기준) htree_elem_find (value 기준, exist 체크용)
다건 조회 htree_elem_find 반복 (field 목록 순회) htree_elem_get_rand / htree_elem_unlink_at_offset (랜덤 N개)
전체 조회 htree_elem_get_by_cnt (count = 0) htree_elem_get_by_cnt (count = 0)

map get 동작

단건 조회 (htree_elem_find)

  • field를 key로 hash chain을 탐색하여 일치하는 elem 반환
  • delete 옵션이 있으면 htree_elem_unlink 기반의 do_map_elem_unlink_by_field로 대체

다건 조회 (htree_elem_find 반복)

  • 지정한 field 목록을 순회하며 각 field에 대해 htree_elem_find 호출
  • delete 옵션이 있으면 do_map_elem_unlink_by_field로 대체

전체 조회 (htree_elem_get_by_cnt)

  • count=0으로 호출하여 트리 전체를 DFS 순회하며 모든 elem 수집
  • delete 옵션이 있으면 unlink=true로 호출하여 수집과 동시에 tree에서 제거

set get 동작

단건 조회 (htree_elem_find)

  • value를 key로 hash chain을 탐색하여 존재 여부 확인 (exist 체크용)

다건 조회 (htree_elem_get_rand / htree_elem_unlink_at_offset)

  • delete 없음: DFS 순회 중 샘플링 확률로 count개를 수집한 뒤 shuffle
  • delete 있음: htree_elem_unlink_at_offset을 count번 반복 — 매 반복마다 랜덤 offset의 elem을 unlink하며 node merge도 함께 진행

전체 조회 (htree_elem_get_by_cnt)

  • count=0, unlink 옵션으로 map과 동일하게 처리

htree_elem_get_by_cnt (unlink=true) vs htree_elem_unlink_by_cnt

htree_elem_get_by_cnt (unlink=true) htree_elem_unlink_by_cnt
반환 방식 elem_array에 포인터 복사 후 반환 elem의 next를 직접 연결해 linked list로 반환
node merge unlink 후 merge 진행 unlink 후 merge 진행
용도 caller가 array 형태로 후처리 필요할 때 array 불필요, caller가 linked list로 순회하며 후처리
  • htree_elem_get_by_cnt(unlink=true): elem_array 인자를 받아 unlink한 elem 포인터를 순서대로 복사해 담음. node merge는 unlink 과정에서 함께 진행
  • htree_elem_unlink_by_cnt: elem_array 인자 없이 unlink한 elem들의 next 포인터를 직접 조작하여 하나의 linked list로 연결한 뒤 head를 반환. caller가 linked list를 순회하며 각 elem을 후처리

do_htree_range — 공통 범위 순회 함수

htree_elem_get_at_offset, htree_elem_unlink_at_offset, htree_elem_unlink_by_cnt, htree_elem_get_by_cnt 모두 내부적으로 do_htree_range를 공통으로 사용:

  • DFS로 hash tree를 순회하며 skip개를 건너뛰고 take개를 수집
  • slot 단위로 tot_elem_cnt와 skip을 비교하여 slot 전체를 한 번에 건너뜀 — 불필요한 탐색 없이 대상 범위로 바로 이동
  • unlink 옵션으로 수집과 동시에 tree에서 제거 여부 결정
  • 수집 결과를 elem_array로 반환해야 하는 경우와 linked list로 반환해야 하는 경우가 달라, collect 콜백(collect_to_array / collect_to_chain)으로 수집 방식을 분리
  • do_htree_range는 public API가 아닌 내부 함수이므로, 다소 복잡하더라도 공통 로직을 최대한 통합하는 데 중점을 둠
함수 skip take unlink collect
htree_elem_get_at_offset offset 1 false collect_to_array
htree_elem_unlink_at_offset offset 1 true collect_to_chain
htree_elem_unlink_by_cnt 0 count or all true collect_to_chain
htree_elem_get_by_cnt 0 count or all 옵션 collect_to_array

@namsic namsic self-requested a review April 22, 2026 11:15
@zhy2on zhy2on changed the title Internal/extract hash tree module INTERNAL: Extract hash_tree module from coll_set and coll_map Apr 22, 2026
Copy link
Copy Markdown
Collaborator

@jhpark816 jhpark816 left a comment

Choose a reason for hiding this comment

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

일부 리뷰

Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/item_base.c
@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch from a614f6b to 28a1b82 Compare April 22, 2026 15:15
@zhy2on
Copy link
Copy Markdown
Collaborator Author

zhy2on commented Apr 23, 2026

내부에서 호출하는 함수만 do_ prefix 가지도록 하고,
> 외부에 제공하는 함수는 htree_ prefix 가지도록 합시다.

  • htree_xxxx()
  • 피드백 반영하여 내부에서만 사용하는 함수들은 do_ prefix를 적용하고 header에서 선언을 제거했습니다.

이런 자료구조가 노출되지 않게 구현할 수 있나요?

  • htree_prev_infodo_htree_elem_find로 elem을 찾은 뒤 그 위치(node, hidx, prev)를 직접 조작해 포인터 교체를 수행하기 위한 구조체였습니다.

  • find와 포인터 교체를 htree_elem_insert 내부에서 한 번에 처리하고, replace가 발생했을 때 교체된 old elem의 포인터만 replaced_out 파라미터로 반환하도록 해 htree_prev_info 사용을 없애고 정의도 없앴습니다. 외부에서 내부 구조를 알 필요가 없게 했습니다.


생각보다 callback 정의가 많네요.
> callback 수를 줄일 수 있으면 좋겠습니다.

  • 더 고민해보니 space accounting은 hash tree 내부로 이동하는 게 맞다고 판단했으며 이에 따라 콜백 수를 줄일 수 있게 됐습니다.

  • 처음에는 space accounting을 hash tree 내부로 가져오면 hash tree가 각 collection의 타입별 처리를 알아야 하게 되어 "collection이 hash tree를 사용한다"는 의존 방향에 맞지 않는다고 판단해 콜백으로 분리했습니다.

  • 그러나 다시 생각해보니 노드와 원소의 삽입·삭제가 Hash Tree 내부에서 수행되는 이상, 그 결과인 공간 변화를 추적하는 것 역시 Hash Tree의 역할로 보는 것이 맞아보였습니다. 아이템 타입만 다를 뿐 카운팅 로직이 완전히 동일하며, coll_meta_info*에서 ITEM_TYPE을 도출해 hash tree 내부에서 처리하는 방식으로 통합할 수 있었습니다.

  • 다만, CLOG 처리를 위한 콜백은 아직 남아있습니다.


추가로 set/map_elem_get_all의 중복된 DFS 구현을 제거하고, 기존 htree_traverse_dfs_bycnt(count=0, delete=false)를 재활용하는 방식으로 단순화했습니다.

남아있는 CLOG 콜백도 hash tree 내부로 가져올 수 있는지 검토하고, 추가로 추출 가능한 부분이 있는지 확인한 뒤 다시 피드백을 요청드리겠습니다.

@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch 2 times, most recently from 9434b3f to 2eef927 Compare April 23, 2026 02:50
namsic

This comment was marked as outdated.

Copy link
Copy Markdown
Collaborator

@jhpark816 jhpark816 left a comment

Choose a reason for hiding this comment

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

일부 리뷰

Comment thread engines/default/item_base.h Outdated
Comment thread engines/default/coll_set.c
Comment thread engines/default/item_base.h Outdated
@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch 3 times, most recently from 234938c to 8d3450e Compare April 28, 2026 08:40
@zhy2on
Copy link
Copy Markdown
Collaborator Author

zhy2on commented Apr 28, 2026

hash_tree 모듈 추출 및 set/map 공통화

set과 map 컬렉션에 중복으로 존재하던 hash tree 자료구조 구현을 hash_tree.h / hash_tree.c로 분리하고, 두 컬렉션이 공통 인터페이스를 통해 사용하도록 리팩토링했습니다.

핵심 설계 원칙

hash_tree는 순수 자료구조 레이어로, 아래 책임만 가집니다:

  • 노드/엘리먼트 할당·해제 (htree_elem_alloc, htree_elem_free)
  • 삽입·갱신·탐색·순회·삭제 연산
  • node collapse (split 역전), root cleanup 내부화

아래는 각 컬렉션(caller) 책임으로 유지합니다:

  • CLOG (단, traverse delete 시 순회 중 CLOG를 남겨야 하므로 unlink_fn을 callback으로 전달)
  • space accounting
  • ccnt
  • item alloc/find, cache lock, log replay: 각 컬렉션 레이어에서 처리

elem 레이아웃 통일

htree_elem_item을 <nkey, nbytes, data[]> 구조로 정의하고 두 타입을 typedef로 통일:

set: nkey == nbytes (data 전체가 key)
map: nkey == field 길이, nbytes == field + value 길이 (data = <field, value>)

추출된 공통 함수

함수 역할 비고
htree_elem_alloc elem 메모리 할당 cache lock 타이밍은 컬렉션이 제어해야 해서 public
htree_elem_free elem 메모리 해제 동일 이유로 public
htree_elem_release refcount 감소, 0이면 free
htree_elem_ntotal elem 전체 크기 반환
htree_elem_insert 삽입 (replace, overflow, sticky 포함)  
htree_elem_update 값 갱신 (in-place 또는 chain replace)  
htree_elem_traverse_dfs_bycnt DFS 순회, count개 조회/삭제  
htree_elem_traverse_dfs_bykey key 기반 단건 탐색/삭제  
htree_elem_traverse_rand 랜덤 선택 (sparse: dedup hash table, dense: reservoir sampling)  

Comment thread engines/default/coll_map.c Outdated
Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/hash_tree.c Outdated
Comment thread engines/default/hash_tree.c Outdated
Comment thread engines/default/item_base.h Outdated
Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/hash_tree.c Outdated
Copy link
Copy Markdown
Collaborator

@jhpark816 jhpark816 left a comment

Choose a reason for hiding this comment

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

일부 리뷰

Comment thread engines/default/hash_tree.h Outdated
Comment thread engines/default/hash_tree.c Outdated
Comment thread engines/default/hash_tree.c
Copy link
Copy Markdown
Collaborator

@jhpark816 jhpark816 left a comment

Choose a reason for hiding this comment

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

일부 리뷰

Comment thread engines/default/coll_set.c Outdated
Comment thread engines/default/coll_map.c Outdated
Comment thread engines/default/hash_tree.c Outdated
Comment thread engines/default/hash_tree.c Outdated
@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch 7 times, most recently from 4d4ec29 to ae839b7 Compare May 12, 2026 10:28
@zhy2on zhy2on closed this May 15, 2026
@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch from 5587932 to b5d548c Compare May 15, 2026 04:41
@zhy2on zhy2on reopened this May 15, 2026
Copy link
Copy Markdown
Collaborator

@namsic namsic left a comment

Choose a reason for hiding this comment

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

insert/update commit 까지 확인

Comment thread engines/default/hash_tree.c Outdated
Comment thread engines/default/hash_tree.h Outdated
@zhy2on
Copy link
Copy Markdown
Collaborator Author

zhy2on commented May 18, 2026

추가 수정 중입니다. 완료된 후 다시 노티 드리도록 하겠습니다.

@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch 2 times, most recently from 3ed4cd1 to 3da6a5b Compare May 18, 2026 03:11
@zhy2on
Copy link
Copy Markdown
Collaborator Author

zhy2on commented May 18, 2026

insert/update/exist 경로를 hash_tree 모듈로 교체

htree_elem_link 동작

  1. root 초기화: 트리가 비어있으면 root node를 할당하고 space_delta에 반영
  2. leaf 탐색: hcnt[hidx] == -1인 슬롯을 따라 내려가며 삽입할 leaf node를 찾음
  3. duplicate 체크: leaf의 hash chain을 순회하여 동일 key가 있으면 ENGINE_ELEM_EEXISTS 반환
  4. node split: chain 길이가 HTREE_MAX_HASHCHAIN_SIZE(64)에 도달하면 child node를 새로 할당하고 기존 chain을 child로 이전. 삽입 위치도 child 기준으로 업데이트
  5. elem 삽입: 대상 슬롯의 chain 앞에 prepend, root까지의 모든 조상 node의 tot_elem_cnt 증가

map vs set insert 비교

두 collection 모두 htree_elem_link 호출 전에 동일한 순서로 사전 검사를 수행:

map set
sticky check IS_STICKY_COLLFLG + do_item_sticky_overflowed() 동일
overflow check ccnt >= max_map_size ccnt >= max_set_size
duplicate htree_elem_link 내부에서 ENGINE_ELEM_EEXISTS 동일

map은 update 경로(do_map_elem_replace_at)가 추가로 존재

delete 경로를 hash_tree 모듈로 교체

htree_elem_unlink 동작

  1. leaf 탐색: hcnt[hidx] == -1인 슬롯을 따라 내려가며 삭제할 elem이 있는 leaf node를 찾음. 이 때 merge를 위해 par_node와 par_hidx를 함께 추적
  2. elem 탐색: leaf의 hash chain을 순회하여 key가 일치하는 elem을 찾음. 없으면 NULL 반환
  3. elem unlink: chain에서 제거하고 root까지의 모든 조상 node의 tot_elem_cnt 감소
  4. node merge: 삭제 후 node가 비었거나 elem 수가 HTREE_MAX_HASHCHAIN_SIZE / 2 미만인 leaf면 child node를 해제하고 elem들을 par_node의 슬롯에 flat chain으로 복귀

htree_elem_unlink_by_cnt 동작

전체 또는 count개만큼 bulk 삭제:

  1. DFS로 트리를 순회하며 elem을 chain(unlinkelem들의 next 포인터로 연결한 linked list)으로 수집
  2. child node가 sparse해지면(HTREE_MAX_HASHCHAIN_SIZE / 2 미만으로 줄어들면 ) 즉시 merge — child node를 유지할 필요 없이 par_node의 슬롯 하나에 flat chain으로 담을 수 있을 만큼 적어진 것이므로 node를 해제하여 메모리를 절약
  3. 순회 완료 후 root가 비었으면 root node도 해제

map/set get 경로를 hash_tree 모듈로 교체

map vs set get 비교

map set
단건 조회 htree_elem_find (field 기준) htree_elem_find (value 기준, exist 체크용)
다건 조회 htree_elem_find 반복 (field 목록 순회) htree_elem_get_rand / htree_elem_unlink_at_offset (랜덤 N개)
전체 조회 htree_elem_get_by_cnt (count = 0) htree_elem_get_by_cnt (count = 0)

map get 동작

단건 조회 (htree_elem_find)

  • field를 key로 hash chain을 탐색하여 일치하는 elem 반환
  • delete 옵션이 있으면 htree_elem_unlink 기반의 do_map_elem_unlink_by_field로 대체

다건 조회 (htree_elem_find 반복)

  • 지정한 field 목록을 순회하며 각 field에 대해 htree_elem_find 호출
  • delete 옵션이 있으면 do_map_elem_unlink_by_field로 대체

전체 조회 (htree_elem_get_by_cnt)

  • count=0으로 호출하여 트리 전체를 DFS 순회하며 모든 elem 수집
  • delete 옵션이 있으면 unlink=true로 호출하여 수집과 동시에 tree에서 제거

set get 동작

단건 조회 (htree_elem_find)

  • value를 key로 hash chain을 탐색하여 존재 여부 확인 (exist 체크용)

다건 조회 (htree_elem_get_rand / htree_elem_unlink_at_offset)

  • delete 없음: DFS 순회 중 샘플링 확률로 count개를 수집한 뒤 shuffle
  • delete 있음: htree_elem_unlink_at_offset을 count번 반복 — 매 반복마다 랜덤 offset의 elem을 unlink하며 node merge도 함께 진행

전체 조회 (htree_elem_get_by_cnt)

  • count=0, unlink 옵션으로 map과 동일하게 처리

htree_elem_get_by_cnt (unlink=true) vs htree_elem_unlink_by_cnt

htree_elem_get_by_cnt (unlink=true) htree_elem_unlink_by_cnt
반환 방식 elem_array에 포인터 복사 후 반환 elem의 next를 직접 연결해 linked list로 반환
node merge unlink 후 merge 진행 unlink 후 merge 진행
용도 caller가 array 형태로 후처리 필요할 때 array 불필요, caller가 linked list로 순회하며 후처리
  • htree_elem_get_by_cnt(unlink=true): elem_array 인자를 받아 unlink한 elem 포인터를 순서대로 복사해 담음. node merge는 unlink 과정에서 함께 진행
  • htree_elem_unlink_by_cnt: elem_array 인자 없이 unlink한 elem들의 next 포인터를 직접 조작하여 하나의 linked list로 연결한 뒤 head를 반환. caller가 linked list를 순회하며 각 elem을 후처리

do_htree_range — 공통 범위 순회 함수

htree_elem_get_at_offset, htree_elem_unlink_at_offset, htree_elem_unlink_by_cnt, htree_elem_get_by_cnt 모두 내부적으로 do_htree_range를 공통으로 사용:

  • DFS로 hash tree를 순회하며 skip개를 건너뛰고 take개를 수집
  • slot 단위로 tot_elem_cnt와 skip을 비교하여 slot 전체를 한 번에 건너뜀 — 불필요한 탐색 없이 대상 범위로 바로 이동
  • unlink 옵션으로 수집과 동시에 tree에서 제거 여부 결정
  • 수집 결과를 elem_array로 반환해야 하는 경우와 linked list로 반환해야 하는 경우가 달라, collect 콜백(collect_to_array / collect_to_chain)으로 수집 방식을 분리
  • do_htree_range는 public API가 아닌 내부 함수이므로, 다소 복잡하더라도 공통 로직을 최대한 통합하는 데 중점을 둠
함수 skip take unlink collect
htree_elem_get_at_offset offset 1 false collect_to_array
htree_elem_unlink_at_offset offset 1 true collect_to_chain
htree_elem_unlink_by_cnt 0 count or all true collect_to_chain
htree_elem_get_by_cnt 0 count or all 옵션 collect_to_array

@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch 3 times, most recently from 79c2686 to 479a15c Compare May 18, 2026 08:09
@zhy2on zhy2on force-pushed the internal/extract-hash-tree-module branch from 479a15c to df63f64 Compare May 18, 2026 08:13
@zhy2on zhy2on requested review from jhpark816 and namsic May 18, 2026 10:10
Copy link
Copy Markdown
Collaborator

@jhpark816 jhpark816 left a comment

Choose a reason for hiding this comment

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

일부만 리뷰하였고, 모두 리팩토링 관점의 리뷰입니다.
리뷰 의견을 참고하여 다른 부분도 잘 정리 바랍니다.

Comment thread engines/default/hash_tree.c Outdated
break;
node = (htree_node *)node->htab[hidx];
}
assert(node != NULL && hidx != -1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

여기 assert 문은 무조건 만족되는 것이 코드에서 보장되므로 없어도 됩니다.
hidx는 절대 -1이 될 수 없으므로, hidx 변수를 초기에 -1로 설정하지 않아도 됩니다.

오히려 코드를 둔다면, node->hcnt[hidx] == 0 경우에 바로 리턴해도 될 것 같습니다.

    while (node != NULL) {
        hidx = HTREE_GET_HASHIDX(hval, node->hdepth);
        if (node->hcnt[hidx] >= 0)
            break;
        node = (htree_node *)node->htab[hidx];
    }
    if (node->hcnt[hidx] == 0) return NULL;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

반영하였습니다.

Comment thread engines/default/hash_tree.c
Comment thread engines/default/hash_tree.c
Comment thread engines/default/coll_map.c Outdated
}

return ENGINE_SUCCESS;
return do_map_elem_link(info, elem, cookie);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

insert 하는 경우는 replaced 값을 false 설정해야 할 것 같고,
아래의 코드가 읽기 쉬울 것 같습니다.

   map_elem_item *old_elem = (map_elem_item *)htree_elem_find((htree_node *)info->root,
                                                               elem->data, elem->nfield,
                                                               &map_htree_ops, &pos);
    if (old_elem == NULL) {
        if (replaced) *replaced = false;
        return do_map_elem_link(info, elem, cookie);
    }

    if (replace_if_exist) {
        if (replaced) *replaced = true;
        return do_map_elem_replace_at(info, &pos, old_elem, elem);
    } else {
        return ENGINE_ELEM_EEXISTS;
    }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

static ENGINE_ERROR_CODE do_map_elem_insert(hash_item *it, map_elem_item *elem,
                                            const bool replace_if_exist, bool *replaced,
                                            const void *cookie)
{
    map_meta_info *info = (map_meta_info *)item_get_meta(it);

    htree_elem_pos pos;
    map_elem_item *old_elem = (map_elem_item *)htree_elem_find((htree_node *)info->root,
                                                               elem->data, elem->nfield,
                                                               &map_htree_ops, &pos);

    if (replaced) *replaced = false;

    if (old_elem == NULL)
        return do_map_elem_link(info, elem, cookie);

    if (!replace_if_exist)
        return ENGINE_ELEM_EEXISTS;

    ENGINE_ERROR_CODE ret = do_map_elem_replace_at(info, &pos, old_elem, elem);
    if (ret == ENGINE_SUCCESS && replaced) *replaced = true;
    return ret;
}

replaced 초기화를 내부에서 하도록 하고

do_map_elem_replace_at의 성공 여부 확인 후에 replaced = true로 설정하도록 수정하여 반영하였습니다.

Comment thread engines/default/hash_tree.c
Comment thread engines/default/hash_tree.c Outdated
node->tot_elem_cnt += 1;
}

static void do_htree_elem_unlink(htree_node **root_pptr,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

*root 이어도 충분할 것 같은 데, ** 더블 포인터를 사용하였네요.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

do_htree_elem_link, do_htree_elem_unlink 의 인자를 단일 포인터로 변경하였습니다.

Comment thread engines/default/hash_tree.c Outdated
ssize_t *htree_space_delta,
const void *cookie)
{
if (htree_space_delta) *htree_space_delta = 0;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

htree_space_delta가 NULL인 경우가 없을 것 같습니다.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

기본적으로 htree_elem_link는 element를 트리에 링크하는 역할이고, htree_space_delta는 collection의 space accounting을 위해 추가된 인자이니 caller가 필요하지 않을 수도 있다고 가정하여 NULL을 허용하도록 구현했습니다.

하지만 현재 htree_elem_link / htree_elem_unlink / htree_elem_unlink_at_offset은 항상 caller에서 space_delta를 쓰고 있기 때문에 이 함수들은 NULL을 허용하지 않도록 수정하겠습니다.

/* traverse to the leaf node that should contain this element */
htree_node *node = *root_pptr;
int hidx;
while (true) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

while (node != NULL) 조건이 낫지 않는 지 ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

node->hcnt[hidx] < 0이니 자식 노드를 의미하고 NULL이 아님을 보장합니다.

초기에 rootNULL일 때 조기 종료를 하고 있기 때문에 항상 참이 되는 조건이어서 추가적인 검사 없이 while (true)로 두는 것이 맞아보입니다.

해당 부분은 그대로 두고 일관되지 않았던 htree_elem_find 부분을 수정하였습니다.


/* split the hash chain into a new child node if it is full */
if (!do_htree_node_try_split(&node, &hidx, elem->hval,
htree_space_delta, cookie))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

중복 검사 이후에 바로 try split 하는 것 보다
hcnt[hidx] 검사하여 split 수행하는 것이 나을 것 같습니다.

Copy link
Copy Markdown
Collaborator Author

@zhy2on zhy2on May 19, 2026

Choose a reason for hiding this comment

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

hcnt[hidx] 검사가 어떤 검사를 말씀하시는 걸까요?
현재

/* Split the hash chain at *hidx_ptr into a new child node if it is full,
 * updating *node_pptr and *hidx_ptr to point to the insertion slot in the child. */
static bool do_htree_node_try_split(htree_node **node_pptr,
                                    int *hidx_ptr, uint32_t hval,
                                    ssize_t *delta, const void *cookie)
{
    htree_node *par_node = *node_pptr;
    int hidx = *hidx_ptr;
    /* chain not full: no split needed */
    if (par_node->hcnt[hidx] < HTREE_MAX_HASHCHAIN_SIZE)
        return true;

    /* split: allocate child, transfer chain, and link as child of par_node */
    if (!do_htree_node_split(par_node, hidx, cookie))
        return false;

do_htree_node_try_split 함수를 들어가면 조건을 먼저 검사한 다음 do_htree_node_split을 호출하도록 되어 있긴 합니다

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

try_split을 분리하지 않고, 여기에서 조건 검사 후 node_split 직접 호출하는 방식을 말씀하신 것이 아닌가 합니다.

try_split 구현이 작은 편이고, 호출하는 곳이 여기 뿐이라 저도 같은 코멘트를 달려고 했는데
try_merge와 같은 패턴으로 구현하는 것이 더 유용한가 싶어 언급하지 않았습니다.

Comment thread engines/default/hash_tree.c Outdated
return elem;
}

htree_elem_item *htree_elem_get_at_offset(htree_node *node, uint32_t offset)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

hash_tree 파일에서만 호출되는 것 같습니다.
외부 API로 제공하지 않아도 됩니다.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

반영하였습니다.

@zhy2on zhy2on requested a review from jhpark816 May 19, 2026 08:49
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.

3 participants