목적 및 범위
이 문서는 StableNet에서 상태 액세스를 최적화하는 다층 캐싱 메커니즘과 스냅샷 시스템을 설명합니다.캐싱 아키텍처는 자주 액세스되는 데이터에 대한 메모리 내 LRU 캐시부터, trie 순회 없이 계정 및 저장소 데이터에 대한 빠른 액세스를 제공하는 상태 스냅샷 시스템까지 확장됩니다. 기본 Merkle Patricia Trie 데이터 구조 및 데이터베이스 인터페이스에 대한 정보는 데이터베이스 레이어 및 Merkle Patricia Trie을 참조하세요.
과거 데이터 관리 및 아카이브 전략은 고대 저장소 및 데이터 라이프사이클을 참조하세요.
StateDB 캐싱 아키텍처
StateDB는 모든 블록체인 상태에 대한 최상위 캐싱 레이어로 동작하며, 트랜잭션 실행 중 반복적이고 비용이 큰 trie 및 데이터베이스 접근을 최소화하기 위해 여러 메모리 내 데이터 구조를 유지합니다.계정 및 저장소 캐시 맵
StateDB는 블록 실행 중 상태 변화를 추적하기 위해 다음과 같은 캐시 맵을 유지합니다:| Cache Map | Type | Purpose |
|---|---|---|
stateObjects | map[common.Address]*stateObject | 실행 중 수정되는 라이브 계정 객체 |
accounts | map[common.Hash][]byte | 커밋을 위한 slim RLP 인코딩 계정 데이터 |
storages | map[common.Hash]map[common.Hash][]byte | 커밋 대상 저장소 슬롯 변경사항 |
accountsOrigin | map[common.Address][]byte | 변경 전 계정 상태(diff 계산용) |
storagesOrigin | map[common.Address]map[common.Hash][]byte | 변경 전 저장소 상태(diff 계산용) |
stateObjectsPending | map[common.Address]struct{} | finalise 완료, trie 반영 전 객체 |
stateObjectsDirty | map[common.Address]struct{} | 현재 실행에서 수정된 객체 |
stateObjectsDestruct | map[common.Address]*types.StateAccount | 블록 내에서 selfdestruct된 계정 |
stateObjects는 현재 블록 실행 동안 유지되는 라이브 상태 객체 캐시입니다.각 계정은 최초 접근 시 한 번만 로드되며, 이후에는 동일 객체가 재사용됩니다. 계정 로딩 흐름 요약:
stateObjects캐시 확인- 미존재 시 스냅샷에서 로드 시도
- 스냅샷 미사용 또는 미존재 시 trie 조회
stateObject래퍼 생성stateObjects에 캐시 후 반환
상태 객체 저장소 캐싱
각stateObject는 저장소 슬롯 접근을 위해 3단계 저장소 캐시를 유지합니다.
저장소 단계별 역할:
- dirtyStorage
현재 트랜잭션에서 변경된 슬롯. 트랜잭션 종료 시 초기화됩니다. - pendingStorage
트랜잭션이 종료되어 확정되었지만 아직 trie에 커밋되지 않은 변경사항. - originStorage
스냅샷 또는 trie에서 로드된 기준 상태.
- 트랜잭션 단위 롤백 가능
- 동일 슬롯에 대한 중복 trie 쓰기 방지
- 변경사항의 정확한 diff 계산
상태 스냅샷 시스템
스냅샷 시스템은 Merkle Patricia Trie를 직접 순회하지 않고도 계정 및 저장소 데이터를 조회할 수 있는 평면(flat) 상태 표현을 제공합니다.이는 snap 동기화와 일반 블록 실행 성능 모두에 핵심적인 역할을 합니다.
스냅샷 아키텍처
스냅샷 시스템은 다음 세 구성 요소로 이루어집니다:- 디스크 레이어
특정 블록 높이의 영구 스냅샷으로,
계정 해시 → 계정 데이터, 저장소 해시 → 저장소 값의 평면 매핑을 보유합니다. - Diff 레이어
각 블록 단위의 상태 변경사항을 메모리에 체인 형태로 보관합니다.
변경된 계정과 저장소 슬롯만 저장합니다. - 스냅샷 트리
상태 루트별 스냅샷을 관리하며Snapshot(root)인터페이스를 제공합니다.
스냅샷 기반 상태 액세스
스냅샷이 활성화되면 StateDB는 다음 순서로 상태를 조회합니다:- 계정 조회:
snap.Account(hash) - 저장소 조회:
snap.Storage(addrHash, slotHash) - 미스 시에만 trie로 폴백
- trie 노드 디코딩 없음
- 해시 검증 없음
- 평면 구조 직접 접근
- 일반적으로 계정 조회 기준 10~100배 속도 향상
Trie 프리페처
Trie 프리페처는 트랜잭션 실행 중 접근 가능성이 높은 trie 노드를 백그라운드에서 선제적으로 로드하여 커밋 지연을 최소화합니다. 작동 흐름:- 블록 실행 시작 시
StartPrefetcher()호출 finalise()단계에서 수정된 저장소 슬롯 정보 수집- 워커 고루틴이 해당 경로의 trie 노드를 비동기 로딩
IntermediateRoot()또는 커밋 시 캐시된 trie 재사용- 블록 종료 시
StopPrefetcher()로 정리
저널 및 스냅샷 롤백
StateDB는 저널 기반 변경 추적을 통해 트랜잭션 실행 중 언제든 이전 상태로 롤백할 수 있습니다. 각 상태 변경은journalEntry로 기록되며, 대표적인 항목은 다음과 같습니다:
- 잔액 변경
- nonce 변경
- 코드 변경
- 저장소 변경
- selfdestruct
- 계정 생성
Snapshot() 호출 시 현재 저널 길이가 저장되고, RevertToSnapshot() 호출 시 저널을 역순으로 재생하여 상태를 복원합니다.
이 메커니즘은 다음에 필수적입니다:
- EVM REVERT
- Out-of-gas 처리
- 예외 발생 시 상태 복구
- 추측 실행
상태 커밋 및 캐시 관리
상태 커밋은 다음 3단계로 진행됩니다.단계 1: Finalise
- dirty 객체 정리
- selfdestruct 처리
- dirtyStorage → pendingStorage 이동
- 저널 초기화
단계 2: IntermediateRoot
- 모든 pending 객체의 저장소 trie 해싱
- 계정 trie 업데이트
- 중간 상태 루트 계산
단계 3: Commit
- 모든 저장소 trie 커밋
- 계정 trie 커밋
- 변경된 노드에 대한
NodeSet생성 triedb.Update()로 일괄 기록
빠른 저장소 삭제
대형 저장소 trie 삭제 시 전체 trie 순회는 비효율적입니다.StateDB는 스냅샷을 활용한 빠른 삭제 경로를 제공합니다. 작동 방식:
- 스냅샷 기반 저장소 슬롯 평면 반복
- 메모리 내 StackTrie 구성
- 각 노드에 대한 삭제 마커 생성
- 크기 제한 초과 시 기존 느린 경로로 폴백
- 삭제 마커를 포함한
NodeSet반환
StateDB 복사 의미론
StateDB.Copy()는 병렬 실행 및 추측 상태 생성을 위해 사용됩니다.
공유(읽기 전용):
- 데이터베이스 인터페이스
- 스냅샷 트리
- 기본 trie
- 상태 객체 맵
- 변경 추적 맵
- journal
- logs, preimages
- accessList, transientStorage
- 병렬 트랜잭션 실행 가능
- 메모리 중복 최소화
- 상태 간 완전한 격리 보장
성능 메트릭 및 계측
StateDB는 상태 처리 전반에 대해 상세한 메트릭을 수집합니다. 측정 항목 예:- 계정/저장소 읽기 시간
- trie 해싱 시간
- 커밋 시간
- 스냅샷 접근 시간
- 업데이트/삭제 개수
- 병목 지점 식별
- 캐시 적중률 분석
- 스냅샷 효과 검증
- 성능 회귀 탐지
정리 및 캐시 제거
스냅샷 기반 정리
- 오래된 diff 레이어 병합
- 디스크 레이어 최신화
- 불필요한 trie 노드 제거 가능
캐시 제거 전략
- trie 노드 LRU 캐시
- clean / dirty 캐시 분리
- dirty 노드는 커밋 후 제거
메모리 관리
- 트랜잭션 종료 시 저널 정리
- pending 객체 초기화
- 프리페처 종료
- StateDB 복사 시 구조 공유

