compaction.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package badger
  6. import (
  7. "bytes"
  8. "fmt"
  9. "log"
  10. "math"
  11. "sync"
  12. "github.com/dgraph-io/badger/v4/table"
  13. "github.com/dgraph-io/badger/v4/y"
  14. )
  15. type keyRange struct {
  16. left []byte
  17. right []byte
  18. inf bool
  19. size int64 // size is used for Key splits.
  20. }
  21. func (r keyRange) isEmpty() bool {
  22. return len(r.left) == 0 && len(r.right) == 0 && !r.inf
  23. }
  24. var infRange = keyRange{inf: true}
  25. func (r keyRange) String() string {
  26. return fmt.Sprintf("[left=%x, right=%x, inf=%v]", r.left, r.right, r.inf)
  27. }
  28. func (r keyRange) equals(dst keyRange) bool {
  29. return bytes.Equal(r.left, dst.left) &&
  30. bytes.Equal(r.right, dst.right) &&
  31. r.inf == dst.inf
  32. }
  33. func (r *keyRange) extend(kr keyRange) {
  34. // TODO(ibrahim): Is this needed?
  35. if kr.isEmpty() {
  36. return
  37. }
  38. if r.isEmpty() {
  39. *r = kr
  40. }
  41. if len(r.left) == 0 || y.CompareKeys(kr.left, r.left) < 0 {
  42. r.left = kr.left
  43. }
  44. if len(r.right) == 0 || y.CompareKeys(kr.right, r.right) > 0 {
  45. r.right = kr.right
  46. }
  47. if kr.inf {
  48. r.inf = true
  49. }
  50. }
  51. func (r keyRange) overlapsWith(dst keyRange) bool {
  52. // Empty keyRange always overlaps.
  53. if r.isEmpty() {
  54. return true
  55. }
  56. // TODO(ibrahim): Do you need this?
  57. // Empty dst doesn't overlap with anything.
  58. if dst.isEmpty() {
  59. return false
  60. }
  61. if r.inf || dst.inf {
  62. return true
  63. }
  64. // [dst.left, dst.right] ... [r.left, r.right]
  65. // If my left is greater than dst right, we have no overlap.
  66. if y.CompareKeys(r.left, dst.right) > 0 {
  67. return false
  68. }
  69. // [r.left, r.right] ... [dst.left, dst.right]
  70. // If my right is less than dst left, we have no overlap.
  71. if y.CompareKeys(r.right, dst.left) < 0 {
  72. return false
  73. }
  74. // We have overlap.
  75. return true
  76. }
  77. // getKeyRange returns the smallest and the biggest in the list of tables.
  78. // TODO(naman): Write a test for this. The smallest and the biggest should
  79. // be the smallest of the leftmost table and the biggest of the right most table.
  80. func getKeyRange(tables ...*table.Table) keyRange {
  81. if len(tables) == 0 {
  82. return keyRange{}
  83. }
  84. smallest := tables[0].Smallest()
  85. biggest := tables[0].Biggest()
  86. for i := 1; i < len(tables); i++ {
  87. if y.CompareKeys(tables[i].Smallest(), smallest) < 0 {
  88. smallest = tables[i].Smallest()
  89. }
  90. if y.CompareKeys(tables[i].Biggest(), biggest) > 0 {
  91. biggest = tables[i].Biggest()
  92. }
  93. }
  94. // We pick all the versions of the smallest and the biggest key. Note that version zero would
  95. // be the rightmost key, considering versions are default sorted in descending order.
  96. return keyRange{
  97. left: y.KeyWithTs(y.ParseKey(smallest), math.MaxUint64),
  98. right: y.KeyWithTs(y.ParseKey(biggest), 0),
  99. }
  100. }
  101. type levelCompactStatus struct {
  102. ranges []keyRange
  103. delSize int64
  104. }
  105. func (lcs *levelCompactStatus) debug() string {
  106. var b bytes.Buffer
  107. for _, r := range lcs.ranges {
  108. b.WriteString(r.String())
  109. }
  110. return b.String()
  111. }
  112. func (lcs *levelCompactStatus) overlapsWith(dst keyRange) bool {
  113. for _, r := range lcs.ranges {
  114. if r.overlapsWith(dst) {
  115. return true
  116. }
  117. }
  118. return false
  119. }
  120. func (lcs *levelCompactStatus) remove(dst keyRange) bool {
  121. final := lcs.ranges[:0]
  122. var found bool
  123. for _, r := range lcs.ranges {
  124. if !r.equals(dst) {
  125. final = append(final, r)
  126. } else {
  127. found = true
  128. }
  129. }
  130. lcs.ranges = final
  131. return found
  132. }
  133. type compactStatus struct {
  134. sync.RWMutex
  135. levels []*levelCompactStatus
  136. tables map[uint64]struct{}
  137. }
  138. func (cs *compactStatus) overlapsWith(level int, this keyRange) bool {
  139. cs.RLock()
  140. defer cs.RUnlock()
  141. thisLevel := cs.levels[level]
  142. return thisLevel.overlapsWith(this)
  143. }
  144. func (cs *compactStatus) delSize(l int) int64 {
  145. cs.RLock()
  146. defer cs.RUnlock()
  147. return cs.levels[l].delSize
  148. }
  149. type thisAndNextLevelRLocked struct{}
  150. // compareAndAdd will check whether we can run this compactDef. That it doesn't overlap with any
  151. // other running compaction. If it can be run, it would store this run in the compactStatus state.
  152. func (cs *compactStatus) compareAndAdd(_ thisAndNextLevelRLocked, cd compactDef) bool {
  153. cs.Lock()
  154. defer cs.Unlock()
  155. tl := cd.thisLevel.level
  156. y.AssertTruef(tl < len(cs.levels), "Got level %d. Max levels: %d", tl, len(cs.levels))
  157. thisLevel := cs.levels[cd.thisLevel.level]
  158. nextLevel := cs.levels[cd.nextLevel.level]
  159. if thisLevel.overlapsWith(cd.thisRange) {
  160. return false
  161. }
  162. if nextLevel.overlapsWith(cd.nextRange) {
  163. return false
  164. }
  165. // Check whether this level really needs compaction or not. Otherwise, we'll end up
  166. // running parallel compactions for the same level.
  167. // Update: We should not be checking size here. Compaction priority already did the size checks.
  168. // Here we should just be executing the wish of others.
  169. thisLevel.ranges = append(thisLevel.ranges, cd.thisRange)
  170. nextLevel.ranges = append(nextLevel.ranges, cd.nextRange)
  171. thisLevel.delSize += cd.thisSize
  172. for _, t := range append(cd.top, cd.bot...) {
  173. cs.tables[t.ID()] = struct{}{}
  174. }
  175. return true
  176. }
  177. func (cs *compactStatus) delete(cd compactDef) {
  178. cs.Lock()
  179. defer cs.Unlock()
  180. tl := cd.thisLevel.level
  181. y.AssertTruef(tl < len(cs.levels), "Got level %d. Max levels: %d", tl, len(cs.levels))
  182. thisLevel := cs.levels[cd.thisLevel.level]
  183. nextLevel := cs.levels[cd.nextLevel.level]
  184. thisLevel.delSize -= cd.thisSize
  185. found := thisLevel.remove(cd.thisRange)
  186. // The following check makes sense only if we're compacting more than one
  187. // table. In case of the max level, we might rewrite a single table to
  188. // remove stale data.
  189. if cd.thisLevel != cd.nextLevel && !cd.nextRange.isEmpty() {
  190. found = nextLevel.remove(cd.nextRange) && found
  191. }
  192. if !found {
  193. this := cd.thisRange
  194. next := cd.nextRange
  195. fmt.Printf("Looking for: %s in this level %d.\n", this, tl)
  196. fmt.Printf("This Level:\n%s\n", thisLevel.debug())
  197. fmt.Println()
  198. fmt.Printf("Looking for: %s in next level %d.\n", next, cd.nextLevel.level)
  199. fmt.Printf("Next Level:\n%s\n", nextLevel.debug())
  200. log.Fatal("keyRange not found")
  201. }
  202. for _, t := range append(cd.top, cd.bot...) {
  203. _, ok := cs.tables[t.ID()]
  204. y.AssertTrue(ok)
  205. delete(cs.tables, t.ID())
  206. }
  207. }