@@ -55,6 +55,9 @@ pub struct HeaderRef(PageRef);
5555impl HeaderRef {
5656 pub fn from_pager ( pager : & Pager ) -> Result < IOResult < Self > > {
5757 loop {
58+ if let Some ( page) = pager. cached_header_page ( ) {
59+ return Ok ( IOResult :: Done ( Self ( page) ) ) ;
60+ }
5861 let state = pager. header_ref_state . read ( ) . clone ( ) ;
5962 tracing:: trace!( "HeaderRef::from_pager - {:?}" , state) ;
6063 match state {
@@ -75,6 +78,8 @@ impl HeaderRef {
7578 page. get( ) . id == DatabaseHeader :: PAGE_ID ,
7679 "incorrect header page id"
7780 ) ;
81+ Pager :: validate_header_page ( & page) ?;
82+ pager. set_cached_header_page ( & page) ;
7883 * pager. header_ref_state . write ( ) = HeaderRefState :: Start ;
7984 break Ok ( IOResult :: Done ( Self ( page) ) ) ;
8085 }
@@ -95,6 +100,10 @@ pub struct HeaderRefMut(PageRef);
95100impl HeaderRefMut {
96101 pub fn from_pager ( pager : & Pager ) -> Result < IOResult < Self > > {
97102 loop {
103+ if let Some ( page) = pager. cached_header_page ( ) {
104+ pager. add_dirty ( page. as_ref ( ) ) ?;
105+ return Ok ( IOResult :: Done ( Self ( page) ) ) ;
106+ }
98107 let state = pager. header_ref_state . read ( ) . clone ( ) ;
99108 tracing:: trace!( ?state) ;
100109 match state {
@@ -115,6 +124,8 @@ impl HeaderRefMut {
115124 page. get( ) . id == DatabaseHeader :: PAGE_ID ,
116125 "incorrect header page id"
117126 ) ;
127+ Pager :: validate_header_page ( & page) ?;
128+ pager. set_cached_header_page ( & page) ;
118129
119130 pager. add_dirty ( & page) ?;
120131 * pager. header_ref_state . write ( ) = HeaderRefState :: Start ;
@@ -547,6 +558,7 @@ pub struct Pager {
547558 /// Maximum number of pages allowed in the database. Default is 1073741823 (SQLite default).
548559 max_page_count : AtomicU32 ,
549560 header_ref_state : RwLock < HeaderRefState > ,
561+ header_page : RwLock < Option < PageRef > > ,
550562 #[ cfg( not( feature = "omit_autovacuum" ) ) ]
551563 vacuum_state : RwLock < VacuumState > ,
552564 pub ( crate ) io_ctx : RwLock < IOContext > ,
@@ -661,6 +673,7 @@ impl Pager {
661673 allocate_page_state : RwLock :: new ( AllocatePageState :: Start ) ,
662674 max_page_count : AtomicU32 :: new ( DEFAULT_MAX_PAGE_COUNT ) ,
663675 header_ref_state : RwLock :: new ( HeaderRefState :: Start ) ,
676+ header_page : RwLock :: new ( None ) ,
664677 #[ cfg( not( feature = "omit_autovacuum" ) ) ]
665678 vacuum_state : RwLock :: new ( VacuumState {
666679 ptrmap_get_state : PtrMapGetState :: Start ,
@@ -678,6 +691,53 @@ impl Pager {
678691 Ok ( ( ) )
679692 }
680693
694+ fn cached_header_page ( & self ) -> Option < PageRef > {
695+ let maybe_page = self . header_page . read ( ) . clone ( ) ;
696+ if let Some ( page) = maybe_page {
697+ if page. get ( ) . contents . is_some ( ) {
698+ Some ( page)
699+ } else {
700+ drop ( page) ;
701+ self . clear_cached_header_page ( ) ;
702+ None
703+ }
704+ } else {
705+ None
706+ }
707+ }
708+
709+ fn set_cached_header_page ( & self , page : & PageRef ) {
710+ let mut guard = self . header_page . write ( ) ;
711+ if guard
712+ . as_ref ( )
713+ . map ( |cached| Arc :: ptr_eq ( cached, page) )
714+ . unwrap_or ( false )
715+ {
716+ return ;
717+ }
718+ if let Some ( old) = guard. take ( ) {
719+ if old. is_pinned ( ) {
720+ old. unpin ( ) ;
721+ }
722+ }
723+ page. pin ( ) ;
724+ * guard = Some ( page. clone ( ) ) ;
725+ }
726+
727+ fn clear_cached_header_page ( & self ) {
728+ let mut guard = self . header_page . write ( ) ;
729+ if let Some ( page) = guard. take ( ) {
730+ if page. is_pinned ( ) {
731+ page. unpin ( ) ;
732+ }
733+ }
734+ }
735+
736+ fn validate_header_page ( page : & PageRef ) -> Result < ( ) > {
737+ let content: & PageContent = page. get_contents ( ) ;
738+ DatabaseHeader :: validate_bytes ( & content. buffer . as_slice ( ) [ 0 ..DatabaseHeader :: SIZE ] )
739+ }
740+
681741 /// Open the subjournal if not yet open.
682742 /// The subjournal is a file that is used to store the "before images" of pages for the
683743 /// 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