github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/version.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  	"sync/atomic"
    12  	"unsafe"
    13  
    14  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/iterator"
    15  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/opt"
    16  	"github.com/insionng/yougam/libraries/syndtr/goleveldb/leveldb/util"
    17  )
    18  
    19  type tSet struct {
    20  	level int
    21  	table *tFile
    22  }
    23  
    24  type version struct {
    25  	s *session
    26  
    27  	levels []tFiles
    28  
    29  	// Level that should be compacted next and its compaction score.
    30  	// Score < 1 means compaction is not strictly needed. These fields
    31  	// are initialized by computeCompaction()
    32  	cLevel int
    33  	cScore float64
    34  
    35  	cSeek unsafe.Pointer
    36  
    37  	ref int
    38  	// Succeeding version.
    39  	next *version
    40  }
    41  
    42  func newVersion(s *session) *version {
    43  	return &version{s: s}
    44  }
    45  
    46  func (v *version) releaseNB() {
    47  	v.ref--
    48  	if v.ref > 0 {
    49  		return
    50  	}
    51  	if v.ref < 0 {
    52  		panic("negative version ref")
    53  	}
    54  
    55  	nextTables := make(map[int64]bool)
    56  	for _, tt := range v.next.levels {
    57  		for _, t := range tt {
    58  			num := t.fd.Num
    59  			nextTables[num] = true
    60  		}
    61  	}
    62  
    63  	for _, tt := range v.levels {
    64  		for _, t := range tt {
    65  			num := t.fd.Num
    66  			if _, ok := nextTables[num]; !ok {
    67  				v.s.tops.remove(t)
    68  			}
    69  		}
    70  	}
    71  
    72  	v.next.releaseNB()
    73  	v.next = nil
    74  }
    75  
    76  func (v *version) release() {
    77  	v.s.vmu.Lock()
    78  	v.releaseNB()
    79  	v.s.vmu.Unlock()
    80  }
    81  
    82  func (v *version) walkOverlapping(aux tFiles, ikey internalKey, f func(level int, t *tFile) bool, lf func(level int) bool) {
    83  	ukey := ikey.ukey()
    84  
    85  	// Aux level.
    86  	if aux != nil {
    87  		for _, t := range aux {
    88  			if t.overlaps(v.s.icmp, ukey, ukey) {
    89  				if !f(-1, t) {
    90  					return
    91  				}
    92  			}
    93  		}
    94  
    95  		if lf != nil && !lf(-1) {
    96  			return
    97  		}
    98  	}
    99  
   100  	// Walk tables level-by-level.
   101  	for level, tables := range v.levels {
   102  		if len(tables) == 0 {
   103  			continue
   104  		}
   105  
   106  		if level == 0 {
   107  			// Level-0 files may overlap each other. Find all files that
   108  			// overlap ukey.
   109  			for _, t := range tables {
   110  				if t.overlaps(v.s.icmp, ukey, ukey) {
   111  					if !f(level, t) {
   112  						return
   113  					}
   114  				}
   115  			}
   116  		} else {
   117  			if i := tables.searchMax(v.s.icmp, ikey); i < len(tables) {
   118  				t := tables[i]
   119  				if v.s.icmp.uCompare(ukey, t.imin.ukey()) >= 0 {
   120  					if !f(level, t) {
   121  						return
   122  					}
   123  				}
   124  			}
   125  		}
   126  
   127  		if lf != nil && !lf(level) {
   128  			return
   129  		}
   130  	}
   131  }
   132  
   133  func (v *version) get(aux tFiles, ikey internalKey, ro *opt.ReadOptions, noValue bool) (value []byte, tcomp bool, err error) {
   134  	ukey := ikey.ukey()
   135  
   136  	var (
   137  		tset  *tSet
   138  		tseek bool
   139  
   140  		// Level-0.
   141  		zfound bool
   142  		zseq   uint64
   143  		zkt    keyType
   144  		zval   []byte
   145  	)
   146  
   147  	err = ErrNotFound
   148  
   149  	// Since entries never hop across level, finding key/value
   150  	// in smaller level make later levels irrelevant.
   151  	v.walkOverlapping(aux, ikey, func(level int, t *tFile) bool {
   152  		if level >= 0 && !tseek {
   153  			if tset == nil {
   154  				tset = &tSet{level, t}
   155  			} else {
   156  				tseek = true
   157  			}
   158  		}
   159  
   160  		var (
   161  			fikey, fval []byte
   162  			ferr        error
   163  		)
   164  		if noValue {
   165  			fikey, ferr = v.s.tops.findKey(t, ikey, ro)
   166  		} else {
   167  			fikey, fval, ferr = v.s.tops.find(t, ikey, ro)
   168  		}
   169  
   170  		switch ferr {
   171  		case nil:
   172  		case ErrNotFound:
   173  			return true
   174  		default:
   175  			err = ferr
   176  			return false
   177  		}
   178  
   179  		if fukey, fseq, fkt, fkerr := parseInternalKey(fikey); fkerr == nil {
   180  			if v.s.icmp.uCompare(ukey, fukey) == 0 {
   181  				// Level <= 0 may overlaps each-other.
   182  				if level <= 0 {
   183  					if fseq >= zseq {
   184  						zfound = true
   185  						zseq = fseq
   186  						zkt = fkt
   187  						zval = fval
   188  					}
   189  				} else {
   190  					switch fkt {
   191  					case keyTypeVal:
   192  						value = fval
   193  						err = nil
   194  					case keyTypeDel:
   195  					default:
   196  						panic("leveldb: invalid internalKey type")
   197  					}
   198  					return false
   199  				}
   200  			}
   201  		} else {
   202  			err = fkerr
   203  			return false
   204  		}
   205  
   206  		return true
   207  	}, func(level int) bool {
   208  		if zfound {
   209  			switch zkt {
   210  			case keyTypeVal:
   211  				value = zval
   212  				err = nil
   213  			case keyTypeDel:
   214  			default:
   215  				panic("leveldb: invalid internalKey type")
   216  			}
   217  			return false
   218  		}
   219  
   220  		return true
   221  	})
   222  
   223  	if tseek && tset.table.consumeSeek() <= 0 {
   224  		tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset))
   225  	}
   226  
   227  	return
   228  }
   229  
   230  func (v *version) sampleSeek(ikey internalKey) (tcomp bool) {
   231  	var tset *tSet
   232  
   233  	v.walkOverlapping(nil, ikey, func(level int, t *tFile) bool {
   234  		if tset == nil {
   235  			tset = &tSet{level, t}
   236  			return true
   237  		}
   238  		if tset.table.consumeSeek() <= 0 {
   239  			tcomp = atomic.CompareAndSwapPointer(&v.cSeek, nil, unsafe.Pointer(tset))
   240  		}
   241  		return false
   242  	}, nil)
   243  
   244  	return
   245  }
   246  
   247  func (v *version) getIterators(slice *util.Range, ro *opt.ReadOptions) (its []iterator.Iterator) {
   248  	strict := opt.GetStrict(v.s.o.Options, ro, opt.StrictReader)
   249  	for level, tables := range v.levels {
   250  		if level == 0 {
   251  			// Merge all level zero files together since they may overlap.
   252  			for _, t := range tables {
   253  				its = append(its, v.s.tops.newIterator(t, slice, ro))
   254  			}
   255  		} else if len(tables) != 0 {
   256  			its = append(its, iterator.NewIndexedIterator(tables.newIndexIterator(v.s.tops, v.s.icmp, slice, ro), strict))
   257  		}
   258  	}
   259  	return
   260  }
   261  
   262  func (v *version) newStaging() *versionStaging {
   263  	return &versionStaging{base: v}
   264  }
   265  
   266  // Spawn a new version based on this version.
   267  func (v *version) spawn(r *sessionRecord) *version {
   268  	staging := v.newStaging()
   269  	staging.commit(r)
   270  	return staging.finish()
   271  }
   272  
   273  func (v *version) fillRecord(r *sessionRecord) {
   274  	for level, tables := range v.levels {
   275  		for _, t := range tables {
   276  			r.addTableFile(level, t)
   277  		}
   278  	}
   279  }
   280  
   281  func (v *version) tLen(level int) int {
   282  	if level < len(v.levels) {
   283  		return len(v.levels[level])
   284  	}
   285  	return 0
   286  }
   287  
   288  func (v *version) offsetOf(ikey internalKey) (n int64, err error) {
   289  	for level, tables := range v.levels {
   290  		for _, t := range tables {
   291  			if v.s.icmp.Compare(t.imax, ikey) <= 0 {
   292  				// Entire file is before "ikey", so just add the file size
   293  				n += t.size
   294  			} else if v.s.icmp.Compare(t.imin, ikey) > 0 {
   295  				// Entire file is after "ikey", so ignore
   296  				if level > 0 {
   297  					// Files other than level 0 are sorted by meta->min, so
   298  					// no further files in this level will contain data for
   299  					// "ikey".
   300  					break
   301  				}
   302  			} else {
   303  				// "ikey" falls in the range for this table. Add the
   304  				// approximate offset of "ikey" within the table.
   305  				if m, err := v.s.tops.offsetOf(t, ikey); err == nil {
   306  					n += m
   307  				} else {
   308  					return 0, err
   309  				}
   310  			}
   311  		}
   312  	}
   313  
   314  	return
   315  }
   316  
   317  func (v *version) pickMemdbLevel(umin, umax []byte, maxLevel int) (level int) {
   318  	if maxLevel > 0 {
   319  		if len(v.levels) == 0 {
   320  			return maxLevel
   321  		}
   322  		if !v.levels[0].overlaps(v.s.icmp, umin, umax, true) {
   323  			var overlaps tFiles
   324  			for ; level < maxLevel; level++ {
   325  				if pLevel := level + 1; pLevel >= len(v.levels) {
   326  					return maxLevel
   327  				} else if v.levels[pLevel].overlaps(v.s.icmp, umin, umax, false) {
   328  					break
   329  				}
   330  				if gpLevel := level + 2; gpLevel < len(v.levels) {
   331  					overlaps = v.levels[gpLevel].getOverlaps(overlaps, v.s.icmp, umin, umax, false)
   332  					if overlaps.size() > int64(v.s.o.GetCompactionGPOverlaps(level)) {
   333  						break
   334  					}
   335  				}
   336  			}
   337  		}
   338  	}
   339  	return
   340  }
   341  
   342  func (v *version) computeCompaction() {
   343  	// Precomputed best level for next compaction
   344  	bestLevel := int(-1)
   345  	bestScore := float64(-1)
   346  
   347  	statFiles := make([]int, len(v.levels))
   348  	statSizes := make([]string, len(v.levels))
   349  	statScore := make([]string, len(v.levels))
   350  	statTotSize := int64(0)
   351  
   352  	for level, tables := range v.levels {
   353  		var score float64
   354  		size := tables.size()
   355  		if level == 0 {
   356  			// We treat level-0 specially by bounding the number of files
   357  			// instead of number of bytes for two reasons:
   358  			//
   359  			// (1) With larger write-buffer sizes, it is nice not to do too
   360  			// many level-0 compaction.
   361  			//
   362  			// (2) The files in level-0 are merged on every read and
   363  			// therefore we wish to avoid too many files when the individual
   364  			// file size is small (perhaps because of a small write-buffer
   365  			// setting, or very high compression ratios, or lots of
   366  			// overwrites/deletions).
   367  			score = float64(len(tables)) / float64(v.s.o.GetCompactionL0Trigger())
   368  		} else {
   369  			score = float64(size) / float64(v.s.o.GetCompactionTotalSize(level))
   370  		}
   371  
   372  		if score > bestScore {
   373  			bestLevel = level
   374  			bestScore = score
   375  		}
   376  
   377  		statFiles[level] = len(tables)
   378  		statSizes[level] = shortenb(int(size))
   379  		statScore[level] = fmt.Sprintf("%.2f", score)
   380  		statTotSize += size
   381  	}
   382  
   383  	v.cLevel = bestLevel
   384  	v.cScore = bestScore
   385  
   386  	v.s.logf("version@stat F·%v S·%s%v Sc·%v", statFiles, shortenb(int(statTotSize)), statSizes, statScore)
   387  }
   388  
   389  func (v *version) needCompaction() bool {
   390  	return v.cScore >= 1 || atomic.LoadPointer(&v.cSeek) != nil
   391  }
   392  
   393  type tablesScratch struct {
   394  	added   map[int64]atRecord
   395  	deleted map[int64]struct{}
   396  }
   397  
   398  type versionStaging struct {
   399  	base   *version
   400  	levels []tablesScratch
   401  }
   402  
   403  func (p *versionStaging) getScratch(level int) *tablesScratch {
   404  	if level >= len(p.levels) {
   405  		newLevels := make([]tablesScratch, level+1)
   406  		copy(newLevels, p.levels)
   407  		p.levels = newLevels
   408  	}
   409  	return &(p.levels[level])
   410  }
   411  
   412  func (p *versionStaging) commit(r *sessionRecord) {
   413  	// Deleted tables.
   414  	for _, r := range r.deletedTables {
   415  		scratch := p.getScratch(r.level)
   416  		if r.level < len(p.base.levels) && len(p.base.levels[r.level]) > 0 {
   417  			if scratch.deleted == nil {
   418  				scratch.deleted = make(map[int64]struct{})
   419  			}
   420  			scratch.deleted[r.num] = struct{}{}
   421  		}
   422  		if scratch.added != nil {
   423  			delete(scratch.added, r.num)
   424  		}
   425  	}
   426  
   427  	// New tables.
   428  	for _, r := range r.addedTables {
   429  		scratch := p.getScratch(r.level)
   430  		if scratch.added == nil {
   431  			scratch.added = make(map[int64]atRecord)
   432  		}
   433  		scratch.added[r.num] = r
   434  		if scratch.deleted != nil {
   435  			delete(scratch.deleted, r.num)
   436  		}
   437  	}
   438  }
   439  
   440  func (p *versionStaging) finish() *version {
   441  	// Build new version.
   442  	nv := newVersion(p.base.s)
   443  	numLevel := len(p.levels)
   444  	if len(p.base.levels) > numLevel {
   445  		numLevel = len(p.base.levels)
   446  	}
   447  	nv.levels = make([]tFiles, numLevel)
   448  	for level := 0; level < numLevel; level++ {
   449  		var baseTabels tFiles
   450  		if level < len(p.base.levels) {
   451  			baseTabels = p.base.levels[level]
   452  		}
   453  
   454  		if level < len(p.levels) {
   455  			scratch := p.levels[level]
   456  
   457  			var nt tFiles
   458  			// Prealloc list if possible.
   459  			if n := len(baseTabels) + len(scratch.added) - len(scratch.deleted); n > 0 {
   460  				nt = make(tFiles, 0, n)
   461  			}
   462  
   463  			// Base tables.
   464  			for _, t := range baseTabels {
   465  				if _, ok := scratch.deleted[t.fd.Num]; ok {
   466  					continue
   467  				}
   468  				if _, ok := scratch.added[t.fd.Num]; ok {
   469  					continue
   470  				}
   471  				nt = append(nt, t)
   472  			}
   473  
   474  			// New tables.
   475  			for _, r := range scratch.added {
   476  				nt = append(nt, tableFileFromRecord(r))
   477  			}
   478  
   479  			if len(nt) != 0 {
   480  				// Sort tables.
   481  				if level == 0 {
   482  					nt.sortByNum()
   483  				} else {
   484  					nt.sortByKey(p.base.s.icmp)
   485  				}
   486  
   487  				nv.levels[level] = nt
   488  			}
   489  		} else {
   490  			nv.levels[level] = baseTabels
   491  		}
   492  	}
   493  
   494  	// Trim levels.
   495  	n := len(nv.levels)
   496  	for ; n > 0 && nv.levels[n-1] == nil; n-- {
   497  	}
   498  	nv.levels = nv.levels[:n]
   499  
   500  	// Compute compaction score for new version.
   501  	nv.computeCompaction()
   502  
   503  	return nv
   504  }
   505  
   506  type versionReleaser struct {
   507  	v    *version
   508  	once bool
   509  }
   510  
   511  func (vr *versionReleaser) Release() {
   512  	v := vr.v
   513  	v.s.vmu.Lock()
   514  	if !vr.once {
   515  		v.releaseNB()
   516  		vr.once = true
   517  	}
   518  	v.s.vmu.Unlock()
   519  }