@@ -54,6 +54,9 @@ pub struct HeaderRef(PageRef);
5454impl HeaderRef {
5555 pub fn from_pager ( pager : & Pager ) -> Result < IOResult < Self > > {
5656 loop {
57+ if let Some ( page) = pager. cached_header_page ( ) {
58+ return Ok ( IOResult :: Done ( Self ( page) ) ) ;
59+ }
5760 let state = pager. header_ref_state . read ( ) . clone ( ) ;
5861 tracing:: trace!( "HeaderRef::from_pager - {:?}" , state) ;
5962 match state {
@@ -74,6 +77,8 @@ impl HeaderRef {
7477 page. get( ) . id == DatabaseHeader :: PAGE_ID ,
7578 "incorrect header page id"
7679 ) ;
80+ Pager :: validate_header_page ( & page) ?;
81+ pager. set_cached_header_page ( & page) ;
7782 * pager. header_ref_state . write ( ) = HeaderRefState :: Start ;
7883 break Ok ( IOResult :: Done ( Self ( page) ) ) ;
7984 }
@@ -94,6 +99,10 @@ pub struct HeaderRefMut(PageRef);
9499impl HeaderRefMut {
95100 pub fn from_pager ( pager : & Pager ) -> Result < IOResult < Self > > {
96101 loop {
102+ if let Some ( page) = pager. cached_header_page ( ) {
103+ pager. add_dirty ( page. as_ref ( ) ) ?;
104+ return Ok ( IOResult :: Done ( Self ( page) ) ) ;
105+ }
97106 let state = pager. header_ref_state . read ( ) . clone ( ) ;
98107 tracing:: trace!( ?state) ;
99108 match state {
@@ -114,6 +123,8 @@ impl HeaderRefMut {
114123 page. get( ) . id == DatabaseHeader :: PAGE_ID ,
115124 "incorrect header page id"
116125 ) ;
126+ Pager :: validate_header_page ( & page) ?;
127+ pager. set_cached_header_page ( & page) ;
117128
118129 pager. add_dirty ( & page) ?;
119130 * pager. header_ref_state . write ( ) = HeaderRefState :: Start ;
@@ -546,6 +557,7 @@ pub struct Pager {
546557 /// Maximum number of pages allowed in the database. Default is 1073741823 (SQLite default).
547558 max_page_count : AtomicU32 ,
548559 header_ref_state : RwLock < HeaderRefState > ,
560+ header_page : RwLock < Option < PageRef > > ,
549561 #[ cfg( not( feature = "omit_autovacuum" ) ) ]
550562 vacuum_state : RwLock < VacuumState > ,
551563 pub ( crate ) io_ctx : RwLock < IOContext > ,
@@ -660,6 +672,7 @@ impl Pager {
660672 allocate_page_state : RwLock :: new ( AllocatePageState :: Start ) ,
661673 max_page_count : AtomicU32 :: new ( DEFAULT_MAX_PAGE_COUNT ) ,
662674 header_ref_state : RwLock :: new ( HeaderRefState :: Start ) ,
675+ header_page : RwLock :: new ( None ) ,
663676 #[ cfg( not( feature = "omit_autovacuum" ) ) ]
664677 vacuum_state : RwLock :: new ( VacuumState {
665678 ptrmap_get_state : PtrMapGetState :: Start ,
@@ -677,6 +690,53 @@ impl Pager {
677690 Ok ( ( ) )
678691 }
679692
693+ fn cached_header_page ( & self ) -> Option < PageRef > {
694+ let maybe_page = self . header_page . read ( ) . clone ( ) ;
695+ if let Some ( page) = maybe_page {
696+ if page. get ( ) . contents . is_some ( ) {
697+ Some ( page)
698+ } else {
699+ drop ( page) ;
700+ self . clear_cached_header_page ( ) ;
701+ None
702+ }
703+ } else {
704+ None
705+ }
706+ }
707+
708+ fn set_cached_header_page ( & self , page : & PageRef ) {
709+ let mut guard = self . header_page . write ( ) ;
710+ if guard
711+ . as_ref ( )
712+ . map ( |cached| Arc :: ptr_eq ( cached, page) )
713+ . unwrap_or ( false )
714+ {
715+ return ;
716+ }
717+ if let Some ( old) = guard. take ( ) {
718+ if old. is_pinned ( ) {
719+ old. unpin ( ) ;
720+ }
721+ }
722+ page. pin ( ) ;
723+ * guard = Some ( page. clone ( ) ) ;
724+ }
725+
726+ fn clear_cached_header_page ( & self ) {
727+ let mut guard = self . header_page . write ( ) ;
728+ if let Some ( page) = guard. take ( ) {
729+ if page. is_pinned ( ) {
730+ page. unpin ( ) ;
731+ }
732+ }
733+ }
734+
735+ fn validate_header_page ( page : & PageRef ) -> Result < ( ) > {
736+ let content: & PageContent = page. get_contents ( ) ;
737+ DatabaseHeader :: validate_bytes ( & content. buffer . as_slice ( ) [ 0 ..DatabaseHeader :: SIZE ] )
738+ }
739+
680740 /// Open the subjournal if not yet open.
681741 /// The subjournal is a file that is used to store the "before images" of pages for the
682742 /// current savepoint. If the savepoint is rolled back, the pages can be restored from the subjournal.
@@ -2120,6 +2180,7 @@ impl Pager {
21202180 /// of a rollback or in case we want to invalidate page cache after starting a read transaction
21212181 /// right after new writes happened which would invalidate current page cache.
21222182 pub fn clear_page_cache ( & self , clear_dirty : bool ) {
2183+ self . clear_cached_header_page ( ) ;
21232184 let dirty_pages = self . dirty_pages . read ( ) ;
21242185 let mut cache = self . page_cache . write ( ) ;
21252186 for page_id in dirty_pages. iter ( ) {
@@ -2713,6 +2774,9 @@ impl Pager {
27132774 } ) ?;
27142775 page. set_loaded ( ) ;
27152776 page. clear_wal_tag ( ) ;
2777+ if id == DatabaseHeader :: PAGE_ID {
2778+ self . set_cached_header_page ( & page) ;
2779+ }
27162780 Ok ( ( ) )
27172781 }
27182782
0 commit comments