github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/snapshot.go (about)

     1  // Copyright 2012 The LevelDB-Go and Pebble and Bitalostored Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package bitalostable
     6  
     7  import (
     8  	"io"
     9  	"math"
    10  )
    11  
    12  // Snapshot provides a read-only point-in-time view of the DB state.
    13  type Snapshot struct {
    14  	// The db the snapshot was created from.
    15  	db     *DB
    16  	seqNum uint64
    17  
    18  	// The list the snapshot is linked into.
    19  	list *snapshotList
    20  
    21  	// The next/prev link for the snapshotList doubly-linked list of snapshots.
    22  	prev, next *Snapshot
    23  }
    24  
    25  var _ Reader = (*Snapshot)(nil)
    26  
    27  // Get gets the value for the given key. It returns ErrNotFound if the Snapshot
    28  // does not contain the key.
    29  //
    30  // The caller should not modify the contents of the returned slice, but it is
    31  // safe to modify the contents of the argument after Get returns. The returned
    32  // slice will remain valid until the returned Closer is closed. On success, the
    33  // caller MUST call closer.Close() or a memory leak will occur.
    34  func (s *Snapshot) Get(key []byte) ([]byte, io.Closer, error) {
    35  	if s.db == nil {
    36  		panic(ErrClosed)
    37  	}
    38  	return s.db.getInternal(key, nil /* batch */, s)
    39  }
    40  
    41  // NewIter returns an iterator that is unpositioned (Iterator.Valid() will
    42  // return false). The iterator can be positioned via a call to SeekGE,
    43  // SeekLT, First or Last.
    44  func (s *Snapshot) NewIter(o *IterOptions) *Iterator {
    45  	if s.db == nil {
    46  		panic(ErrClosed)
    47  	}
    48  	return s.db.newIterInternal(nil /* batch */, s, o)
    49  }
    50  
    51  // Close closes the snapshot, releasing its resources. Close must be called.
    52  // Failure to do so will result in a tiny memory leak and a large leak of
    53  // resources on disk due to the entries the snapshot is preventing from being
    54  // deleted.
    55  func (s *Snapshot) Close() error {
    56  	if s.db == nil {
    57  		panic(ErrClosed)
    58  	}
    59  	s.db.mu.Lock()
    60  	s.db.mu.snapshots.remove(s)
    61  
    62  	// If s was the previous earliest snapshot, we might be able to reclaim
    63  	// disk space by dropping obsolete records that were pinned by s.
    64  	if e := s.db.mu.snapshots.earliest(); e > s.seqNum {
    65  		s.db.maybeScheduleCompactionPicker(pickElisionOnly)
    66  	}
    67  	s.db.mu.Unlock()
    68  	s.db = nil
    69  	return nil
    70  }
    71  
    72  type snapshotList struct {
    73  	root Snapshot
    74  }
    75  
    76  func (l *snapshotList) init() {
    77  	l.root.next = &l.root
    78  	l.root.prev = &l.root
    79  }
    80  
    81  func (l *snapshotList) empty() bool {
    82  	return l.root.next == &l.root
    83  }
    84  
    85  func (l *snapshotList) count() int {
    86  	if l.empty() {
    87  		return 0
    88  	}
    89  	var count int
    90  	for i := l.root.next; i != &l.root; i = i.next {
    91  		count++
    92  	}
    93  	return count
    94  }
    95  
    96  func (l *snapshotList) earliest() uint64 {
    97  	v := uint64(math.MaxUint64)
    98  	if !l.empty() {
    99  		v = l.root.next.seqNum
   100  	}
   101  	return v
   102  }
   103  
   104  func (l *snapshotList) toSlice() []uint64 {
   105  	if l.empty() {
   106  		return nil
   107  	}
   108  	var results []uint64
   109  	for i := l.root.next; i != &l.root; i = i.next {
   110  		results = append(results, i.seqNum)
   111  	}
   112  	return results
   113  }
   114  
   115  func (l *snapshotList) pushBack(s *Snapshot) {
   116  	if s.list != nil || s.prev != nil || s.next != nil {
   117  		panic("bitalostable: snapshot list is inconsistent")
   118  	}
   119  	s.prev = l.root.prev
   120  	s.prev.next = s
   121  	s.next = &l.root
   122  	s.next.prev = s
   123  	s.list = l
   124  }
   125  
   126  func (l *snapshotList) remove(s *Snapshot) {
   127  	if s == &l.root {
   128  		panic("bitalostable: cannot remove snapshot list root node")
   129  	}
   130  	if s.list != l {
   131  		panic("bitalostable: snapshot list is inconsistent")
   132  	}
   133  	s.prev.next = s.next
   134  	s.next.prev = s.prev
   135  	s.next = nil // avoid memory leaks
   136  	s.prev = nil // avoid memory leaks
   137  	s.list = nil // avoid memory leaks
   138  }