@@ -41,6 +41,9 @@ const (
4141
4242 // BlockEntryVersion is the version of the block entry.
4343 BlockEntryVersion uint16 = 1
44+
45+ // defaultEntryCacheSize is the default number of entries to cache in memory
46+ defaultEntryCacheSize = 256
4447)
4548
4649// BlockHeight defines the type for block heights.
@@ -176,6 +179,7 @@ type Database struct {
176179 log logging.Logger
177180 closed bool
178181 fileCache * lru.Cache [int , * os.File ]
182+ entryCache * lru.Cache [BlockHeight , BlockData ]
179183 compressor compression.Compressor
180184
181185 // closeMu prevents the database from being closed while in use and prevents
@@ -223,6 +227,7 @@ func New(config DatabaseConfig, log logging.Logger) (*Database, error) {
223227 f .Close ()
224228 }
225229 }),
230+ entryCache : lru.NewCache [BlockHeight , BlockData ](defaultEntryCacheSize ),
226231 compressor : compressor ,
227232 }
228233
@@ -372,6 +377,11 @@ func (s *Database) Put(height BlockHeight, block BlockData) error {
372377 return err
373378 }
374379
380+ // Store a copy in cache to prevent external modifications from affecting the cached data
381+ blockCopy := make ([]byte , len (block ))
382+ copy (blockCopy , block )
383+ s .entryCache .Put (height , blockCopy )
384+
375385 s .log .Debug ("Block written successfully" ,
376386 zap .Uint64 ("height" , height ),
377387 zap .Uint32 ("blockSize" , blockDataLen ),
@@ -385,12 +395,6 @@ func (s *Database) Put(height BlockHeight, block BlockData) error {
385395// It returns database.ErrNotFound if the block does not exist.
386396func (s * Database ) readBlockIndex (height BlockHeight ) (indexEntry , error ) {
387397 var entry indexEntry
388- if s .closed {
389- s .log .Error ("Failed to read block index: database is closed" ,
390- zap .Uint64 ("height" , height ),
391- )
392- return entry , database .ErrClosed
393- }
394398
395399 // Skip the index entry read if we know the block is past the max height.
396400 maxHeight := s .maxBlockHeight .Load ()
@@ -436,6 +440,20 @@ func (s *Database) Get(height BlockHeight) (BlockData, error) {
436440 s .closeMu .RLock ()
437441 defer s .closeMu .RUnlock ()
438442
443+ if s .closed {
444+ s .log .Error ("Failed to read block: database is closed" ,
445+ zap .Uint64 ("height" , height ),
446+ )
447+ return nil , database .ErrClosed
448+ }
449+
450+ if cachedData , ok := s .entryCache .Get (height ); ok {
451+ // Return a copy to prevent external modifications from affecting the cache
452+ dataCopy := make ([]byte , len (cachedData ))
453+ copy (dataCopy , cachedData )
454+ return dataCopy , nil
455+ }
456+
439457 indexEntry , err := s .readBlockIndex (height )
440458 if err != nil {
441459 return nil , err
@@ -486,6 +504,10 @@ func (s *Database) Get(height BlockHeight) (BlockData, error) {
486504 return nil , fmt .Errorf ("checksum mismatch: calculated %d, stored %d" , calculatedChecksum , bh .Checksum )
487505 }
488506
507+ cacheCopy := make ([]byte , len (decompressed ))
508+ copy (cacheCopy , decompressed )
509+ s .entryCache .Put (height , cacheCopy )
510+
489511 return decompressed , nil
490512}
491513
@@ -494,6 +516,14 @@ func (s *Database) Has(height BlockHeight) (bool, error) {
494516 s .closeMu .RLock ()
495517 defer s .closeMu .RUnlock ()
496518
519+ if s .closed {
520+ return false , database .ErrClosed
521+ }
522+
523+ if _ , ok := s .entryCache .Get (height ); ok {
524+ return true , nil
525+ }
526+
497527 _ , err := s .readBlockIndex (height )
498528 if err != nil {
499529 if errors .Is (err , database .ErrNotFound ) || errors .Is (err , ErrInvalidBlockHeight ) {
0 commit comments