discard.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * Copyright 2020 Dgraph Labs, Inc. and Contributors
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package badger
  17. import (
  18. "encoding/binary"
  19. "os"
  20. "path/filepath"
  21. "sort"
  22. "sync"
  23. "github.com/dgraph-io/badger/v4/y"
  24. "github.com/dgraph-io/ristretto/v2/z"
  25. )
  26. // discardStats keeps track of the amount of data that could be discarded for
  27. // a given logfile.
  28. type discardStats struct {
  29. sync.Mutex
  30. *z.MmapFile
  31. opt Options
  32. nextEmptySlot int
  33. }
  34. const discardFname string = "DISCARD"
  35. func InitDiscardStats(opt Options) (*discardStats, error) {
  36. fname := filepath.Join(opt.ValueDir, discardFname)
  37. // 1MB file can store 65.536 discard entries. Each entry is 16 bytes.
  38. mf, err := z.OpenMmapFile(fname, os.O_CREATE|os.O_RDWR, 1<<20)
  39. lf := &discardStats{
  40. MmapFile: mf,
  41. opt: opt,
  42. }
  43. if err == z.NewFile {
  44. // We don't need to zero out the entire 1MB.
  45. lf.zeroOut()
  46. } else if err != nil {
  47. return nil, y.Wrapf(err, "while opening file: %s\n", discardFname)
  48. }
  49. for slot := 0; slot < lf.maxSlot(); slot++ {
  50. if lf.get(16*slot) == 0 {
  51. lf.nextEmptySlot = slot
  52. break
  53. }
  54. }
  55. sort.Sort(lf)
  56. opt.Infof("Discard stats nextEmptySlot: %d\n", lf.nextEmptySlot)
  57. return lf, nil
  58. }
  59. func (lf *discardStats) Len() int {
  60. return lf.nextEmptySlot
  61. }
  62. func (lf *discardStats) Less(i, j int) bool {
  63. return lf.get(16*i) < lf.get(16*j)
  64. }
  65. func (lf *discardStats) Swap(i, j int) {
  66. left := lf.Data[16*i : 16*i+16]
  67. right := lf.Data[16*j : 16*j+16]
  68. var tmp [16]byte
  69. copy(tmp[:], left)
  70. copy(left, right)
  71. copy(right, tmp[:])
  72. }
  73. // offset is not slot.
  74. func (lf *discardStats) get(offset int) uint64 {
  75. return binary.BigEndian.Uint64(lf.Data[offset : offset+8])
  76. }
  77. func (lf *discardStats) set(offset int, val uint64) {
  78. binary.BigEndian.PutUint64(lf.Data[offset:offset+8], val)
  79. }
  80. // zeroOut would zero out the next slot.
  81. func (lf *discardStats) zeroOut() {
  82. lf.set(lf.nextEmptySlot*16, 0)
  83. lf.set(lf.nextEmptySlot*16+8, 0)
  84. }
  85. func (lf *discardStats) maxSlot() int {
  86. return len(lf.Data) / 16
  87. }
  88. // Update would update the discard stats for the given file id. If discard is
  89. // 0, it would return the current value of discard for the file. If discard is
  90. // < 0, it would set the current value of discard to zero for the file.
  91. func (lf *discardStats) Update(fidu uint32, discard int64) int64 {
  92. fid := uint64(fidu)
  93. lf.Lock()
  94. defer lf.Unlock()
  95. idx := sort.Search(lf.nextEmptySlot, func(slot int) bool {
  96. return lf.get(slot*16) >= fid
  97. })
  98. if idx < lf.nextEmptySlot && lf.get(idx*16) == fid {
  99. off := idx*16 + 8
  100. curDisc := lf.get(off)
  101. if discard == 0 {
  102. return int64(curDisc)
  103. }
  104. if discard < 0 {
  105. lf.set(off, 0)
  106. return 0
  107. }
  108. lf.set(off, curDisc+uint64(discard))
  109. return int64(curDisc + uint64(discard))
  110. }
  111. if discard <= 0 {
  112. // No need to add a new entry.
  113. return 0
  114. }
  115. // Could not find the fid. Add the entry.
  116. idx = lf.nextEmptySlot
  117. lf.set(idx*16, fid)
  118. lf.set(idx*16+8, uint64(discard))
  119. // Move to next slot.
  120. lf.nextEmptySlot++
  121. for lf.nextEmptySlot >= lf.maxSlot() {
  122. y.Check(lf.Truncate(2 * int64(len(lf.Data))))
  123. }
  124. lf.zeroOut()
  125. sort.Sort(lf)
  126. return discard
  127. }
  128. func (lf *discardStats) Iterate(f func(fid, stats uint64)) {
  129. for slot := 0; slot < lf.nextEmptySlot; slot++ {
  130. idx := 16 * slot
  131. f(lf.get(idx), lf.get(idx+8))
  132. }
  133. }
  134. // MaxDiscard returns the file id with maximum discard bytes.
  135. func (lf *discardStats) MaxDiscard() (uint32, int64) {
  136. lf.Lock()
  137. defer lf.Unlock()
  138. var maxFid, maxVal uint64
  139. lf.Iterate(func(fid, val uint64) {
  140. if maxVal < val {
  141. maxVal = val
  142. maxFid = fid
  143. }
  144. })
  145. return uint32(maxFid), int64(maxVal)
  146. }