github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/session.go (about)

     1  // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package leveldb
     8  
     9  import (
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"sync"
    14  
    15  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/errors"
    16  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/journal"
    17  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/opt"
    18  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/storage"
    19  )
    20  
    21  // ErrManifestCorrupted records manifest corruption.
    22  type ErrManifestCorrupted struct {
    23  	Field  string
    24  	Reason string
    25  }
    26  
    27  func (e *ErrManifestCorrupted) Error() string {
    28  	return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason)
    29  }
    30  
    31  func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error {
    32  	return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason})
    33  }
    34  
    35  // session represent a persistent database session.
    36  type session struct {
    37  	// Need 64-bit alignment.
    38  	stNextFileNum    int64 // current unused file number
    39  	stJournalNum     int64 // current journal file number; need external synchronization
    40  	stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb
    41  	stTempFileNum    int64
    42  	stSeqNum         uint64 // last mem compacted seq; need external synchronization
    43  
    44  	stor     storage.Storage
    45  	storLock storage.Lock
    46  	o        *cachedOptions
    47  	icmp     *iComparer
    48  	tops     *tOps
    49  
    50  	manifest       *journal.Writer
    51  	manifestWriter storage.Writer
    52  	manifestFd     storage.FileDesc
    53  
    54  	stCompPtrs []internalKey // compaction pointers; need external synchronization
    55  	stVersion  *version      // current version
    56  	vmu        sync.Mutex
    57  }
    58  
    59  // Creates new initialized session instance.
    60  func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
    61  	if stor == nil {
    62  		return nil, os.ErrInvalid
    63  	}
    64  	storLock, err := stor.Lock()
    65  	if err != nil {
    66  		return
    67  	}
    68  	s = &session{
    69  		stor:     stor,
    70  		storLock: storLock,
    71  	}
    72  	s.setOptions(o)
    73  	s.tops = newTableOps(s)
    74  	s.setVersion(newVersion(s))
    75  	s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
    76  	return
    77  }
    78  
    79  // Close session.
    80  func (s *session) close() {
    81  	s.tops.close()
    82  	if s.manifest != nil {
    83  		s.manifest.Close()
    84  	}
    85  	if s.manifestWriter != nil {
    86  		s.manifestWriter.Close()
    87  	}
    88  	s.manifest = nil
    89  	s.manifestWriter = nil
    90  	s.stVersion = nil
    91  }
    92  
    93  // Release session lock.
    94  func (s *session) release() {
    95  	s.storLock.Release()
    96  }
    97  
    98  // Create a new database session; need external synchronization.
    99  func (s *session) create() error {
   100  	// create manifest
   101  	return s.newManifest(nil, nil)
   102  }
   103  
   104  // Recover a database session; need external synchronization.
   105  func (s *session) recover() (err error) {
   106  	defer func() {
   107  		if os.IsNotExist(err) {
   108  			// Don't return os.ErrNotExist if the underlying storage contains
   109  			// other files that belong to LevelDB. So the DB won't get trashed.
   110  			if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 {
   111  				err = &errors.ErrCorrupted{Fd: storage.FileDesc{Type: storage.TypeManifest}, Err: &errors.ErrMissingFiles{}}
   112  			}
   113  		}
   114  	}()
   115  
   116  	fd, err := s.stor.GetMeta()
   117  	if err != nil {
   118  		return
   119  	}
   120  
   121  	reader, err := s.stor.Open(fd)
   122  	if err != nil {
   123  		return
   124  	}
   125  	defer reader.Close()
   126  
   127  	var (
   128  		// Options.
   129  		strict = s.o.GetStrict(opt.StrictManifest)
   130  
   131  		jr      = journal.NewReader(reader, dropper{s, fd}, strict, true)
   132  		rec     = &sessionRecord{}
   133  		staging = s.stVersion.newStaging()
   134  	)
   135  	for {
   136  		var r io.Reader
   137  		r, err = jr.Next()
   138  		if err != nil {
   139  			if err == io.EOF {
   140  				err = nil
   141  				break
   142  			}
   143  			return errors.SetFd(err, fd)
   144  		}
   145  
   146  		err = rec.decode(r)
   147  		if err == nil {
   148  			// save compact pointers
   149  			for _, r := range rec.compPtrs {
   150  				s.setCompPtr(r.level, internalKey(r.ikey))
   151  			}
   152  			// commit record to version staging
   153  			staging.commit(rec)
   154  		} else {
   155  			err = errors.SetFd(err, fd)
   156  			if strict || !errors.IsCorrupted(err) {
   157  				return
   158  			}
   159  			s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd))
   160  		}
   161  		rec.resetCompPtrs()
   162  		rec.resetAddedTables()
   163  		rec.resetDeletedTables()
   164  	}
   165  
   166  	switch {
   167  	case !rec.has(recComparer):
   168  		return newErrManifestCorrupted(fd, "comparer", "missing")
   169  	case rec.comparer != s.icmp.uName():
   170  		return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer))
   171  	case !rec.has(recNextFileNum):
   172  		return newErrManifestCorrupted(fd, "next-file-num", "missing")
   173  	case !rec.has(recJournalNum):
   174  		return newErrManifestCorrupted(fd, "journal-file-num", "missing")
   175  	case !rec.has(recSeqNum):
   176  		return newErrManifestCorrupted(fd, "seq-num", "missing")
   177  	}
   178  
   179  	s.manifestFd = fd
   180  	s.setVersion(staging.finish())
   181  	s.setNextFileNum(rec.nextFileNum)
   182  	s.recordCommited(rec)
   183  	return nil
   184  }
   185  
   186  // Commit session; need external synchronization.
   187  func (s *session) commit(r *sessionRecord) (err error) {
   188  	v := s.version()
   189  	defer v.release()
   190  
   191  	// spawn new version based on current version
   192  	nv := v.spawn(r)
   193  
   194  	if s.manifest == nil {
   195  		// manifest journal writer not yet created, create one
   196  		err = s.newManifest(r, nv)
   197  	} else {
   198  		err = s.flushManifest(r)
   199  	}
   200  
   201  	// finally, apply new version if no error rise
   202  	if err == nil {
   203  		s.setVersion(nv)
   204  	}
   205  
   206  	return
   207  }