11//! Maintenance walks: rekey under a new epoch and reachable-page collection.
22
3+ use std:: collections:: BTreeMap ;
4+
35use crate :: Result ;
46use crate :: errors:: PagedbError ;
57use crate :: pager:: format:: page_kind:: PageKind ;
@@ -21,20 +23,46 @@ impl<V: Vfs> BTree<V> {
2123 ///
2224 /// Returns the count of pages touched.
2325 pub async fn rekey_walk ( & self ) -> Result < u64 > {
26+ self . rekey_walk_unique ( & mut BTreeMap :: new ( ) ) . await
27+ }
28+
29+ /// Rekey this tree while sharing traversal state with other live roots.
30+ ///
31+ /// Retained snapshots commonly share most of their pages. A caller walking
32+ /// several roots supplies one page-kind map so each physical page is
33+ /// authenticated and rewritten exactly once without hiding a cross-kind
34+ /// alias.
35+ pub ( crate ) async fn rekey_walk_unique (
36+ & self ,
37+ visited : & mut BTreeMap < u64 , PageKind > ,
38+ ) -> Result < u64 > {
2439 if self . root_page_id == 0 {
2540 return Ok ( 0 ) ;
2641 }
27- let mut stack: Vec < u64 > = vec ! [ self . root_page_id] ;
42+ let mut stack: Vec < ( u64 , u64 ) > = vec ! [ ( 0 , self . root_page_id) ] ;
2843 let mut count: u64 = 0 ;
29- while let Some ( page_id) = stack. pop ( ) {
44+ while let Some ( ( parent_page_id, page_id) ) = stack. pop ( ) {
45+ if is_reserved ( page_id) {
46+ return Err ( PagedbError :: reserved_page_referenced (
47+ parent_page_id,
48+ page_id,
49+ ) ) ;
50+ }
51+ if let Some ( kind) = visited. get ( & page_id) {
52+ match kind {
53+ PageKind :: BTreeLeaf | PageKind :: BTreeInternal => continue ,
54+ _ => return Err ( PagedbError :: IllegalPageKind ) ,
55+ }
56+ }
3057 // Determine kind by reading the node under its own header kind byte.
31- let ( is_leaf, body_bytes) = {
32- let ( g, _page_kind ) = self . pager . read_main_node ( page_id, self . realm_id ) . await ?;
58+ let ( is_leaf, page_kind , body_bytes) = {
59+ let ( g, page_kind ) = self . pager . read_main_node ( page_id, self . realm_id ) . await ?;
3360 let body = g. body ( ) ;
3461 let header = read_header ( & body) ?;
3562 let is_leaf = header. kind == NodeKind :: Leaf ;
36- ( is_leaf, body. to_vec ( ) )
63+ ( is_leaf, page_kind , body. to_vec ( ) )
3764 } ;
65+ visited. insert ( page_id, page_kind) ;
3866
3967 if is_leaf {
4068 // Collect overflow chains referenced by this leaf.
@@ -45,37 +73,9 @@ impl<V: Vfs> BTree<V> {
4573 ..
4674 } = v
4775 {
48- // Rewrite the root page (`PageKind::OverflowRoot`).
49- let root_info =
50- overflow:: read_root_page ( & self . pager , self . realm_id , * ov_root) . await ?;
51- self . pager
52- . rewrite_page_under_current_epoch (
53- * ov_root,
54- self . realm_id ,
55- PageKind :: OverflowRoot ,
56- )
76+ count += self
77+ . rekey_overflow_unique ( page_id, * ov_root, visited)
5778 . await ?;
58- count += 1 ;
59- // Walk and rewrite chain pages (always PageKind::Overflow).
60- let mut next = root_info. next ;
61- while next != 0 {
62- let ov_guard = self
63- . pager
64- . read_main_page ( next, self . realm_id , PageKind :: Overflow )
65- . await ?;
66- let ov_body = ov_guard. body ( ) ;
67- let ( ov_next, _) = overflow:: decode_overflow ( & ov_body) ?;
68- drop ( ov_guard) ;
69- self . pager
70- . rewrite_page_under_current_epoch (
71- next,
72- self . realm_id ,
73- PageKind :: Overflow ,
74- )
75- . await ?;
76- count += 1 ;
77- next = ov_next;
78- }
7979 }
8080 }
8181 // Rewrite the leaf page.
@@ -86,9 +86,13 @@ impl<V: Vfs> BTree<V> {
8686 } else {
8787 // Internal node: push children onto stack.
8888 let internal = internal:: Internal :: decode ( & body_bytes) ?;
89- stack. push ( internal. leftmost_child ) ;
89+ if internal. leftmost_child != 0 {
90+ stack. push ( ( page_id, internal. leftmost_child ) ) ;
91+ }
9092 for entry in & internal. entries {
91- stack. push ( entry. right_child ) ;
93+ if entry. right_child != 0 {
94+ stack. push ( ( page_id, entry. right_child ) ) ;
95+ }
9296 }
9397 // Rewrite the internal page.
9498 self . pager
@@ -104,6 +108,55 @@ impl<V: Vfs> BTree<V> {
104108 Ok ( count)
105109 }
106110
111+ async fn rekey_overflow_unique (
112+ & self ,
113+ leaf_page_id : u64 ,
114+ root : u64 ,
115+ visited : & mut BTreeMap < u64 , PageKind > ,
116+ ) -> Result < u64 > {
117+ if is_reserved ( root) {
118+ return Err ( PagedbError :: reserved_page_referenced ( leaf_page_id, root) ) ;
119+ }
120+ if let Some ( kind) = visited. get ( & root) {
121+ return match kind {
122+ PageKind :: OverflowRoot => Ok ( 0 ) ,
123+ _ => Err ( PagedbError :: IllegalPageKind ) ,
124+ } ;
125+ }
126+ visited. insert ( root, PageKind :: OverflowRoot ) ;
127+
128+ let root_info = overflow:: read_root_page ( & self . pager , self . realm_id , root) . await ?;
129+ self . pager
130+ . rewrite_page_under_current_epoch ( root, self . realm_id , PageKind :: OverflowRoot )
131+ . await ?;
132+ let mut count = 1 ;
133+ let mut next = root_info. next ;
134+ while next != 0 {
135+ if is_reserved ( next) {
136+ return Err ( PagedbError :: reserved_page_referenced ( root, next) ) ;
137+ }
138+ if let Some ( kind) = visited. get ( & next) {
139+ return match kind {
140+ PageKind :: Overflow => Err ( PagedbError :: overflow_chain_cycle ( root, next) ) ,
141+ _ => Err ( PagedbError :: IllegalPageKind ) ,
142+ } ;
143+ }
144+ visited. insert ( next, PageKind :: Overflow ) ;
145+ let guard = self
146+ . pager
147+ . read_main_page ( next, self . realm_id , PageKind :: Overflow )
148+ . await ?;
149+ let ( following, _) = overflow:: decode_overflow ( guard. body_ref ( ) ) ?;
150+ drop ( guard) ;
151+ self . pager
152+ . rewrite_page_under_current_epoch ( next, self . realm_id , PageKind :: Overflow )
153+ . await ?;
154+ count += 1 ;
155+ next = following;
156+ }
157+ Ok ( count)
158+ }
159+
107160 /// Collect all page IDs reachable from this tree's root (internal nodes,
108161 /// leaves, overflow chains) into `out`. Used by the deep-walk integrity
109162 /// checker to identify orphan pages.
0 commit comments