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 }