compact_map_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. package needle_map
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "reflect"
  6. "testing"
  7. "github.com/seaweedfs/seaweedfs/weed/storage/types"
  8. )
  9. func TestSegmentBsearchKey(t *testing.T) {
  10. testSegment := &CompactMapSegment{
  11. list: []CompactNeedleValue{
  12. CompactNeedleValue{key: 10},
  13. CompactNeedleValue{key: 20},
  14. CompactNeedleValue{key: 21},
  15. CompactNeedleValue{key: 26},
  16. CompactNeedleValue{key: 30},
  17. },
  18. firstKey: 10,
  19. lastKey: 30,
  20. }
  21. testCases := []struct {
  22. name string
  23. cs *CompactMapSegment
  24. key types.NeedleId
  25. wantIndex int
  26. wantFound bool
  27. }{
  28. {
  29. name: "empty segment",
  30. cs: newCompactMapSegment(0),
  31. key: 123,
  32. wantIndex: 0,
  33. wantFound: false,
  34. },
  35. {
  36. name: "new key, insert at beggining",
  37. cs: testSegment,
  38. key: 5,
  39. wantIndex: 0,
  40. wantFound: false,
  41. },
  42. {
  43. name: "new key, insert at end",
  44. cs: testSegment,
  45. key: 100,
  46. wantIndex: 5,
  47. wantFound: false,
  48. },
  49. {
  50. name: "new key, insert second",
  51. cs: testSegment,
  52. key: 12,
  53. wantIndex: 1,
  54. wantFound: false,
  55. },
  56. {
  57. name: "new key, insert in middle",
  58. cs: testSegment,
  59. key: 23,
  60. wantIndex: 3,
  61. wantFound: false,
  62. },
  63. {
  64. name: "key #1",
  65. cs: testSegment,
  66. key: 10,
  67. wantIndex: 0,
  68. wantFound: true,
  69. },
  70. {
  71. name: "key #2",
  72. cs: testSegment,
  73. key: 20,
  74. wantIndex: 1,
  75. wantFound: true,
  76. },
  77. {
  78. name: "key #3",
  79. cs: testSegment,
  80. key: 21,
  81. wantIndex: 2,
  82. wantFound: true,
  83. },
  84. {
  85. name: "key #4",
  86. cs: testSegment,
  87. key: 26,
  88. wantIndex: 3,
  89. wantFound: true,
  90. },
  91. {
  92. name: "key #5",
  93. cs: testSegment,
  94. key: 30,
  95. wantIndex: 4,
  96. wantFound: true,
  97. },
  98. }
  99. for _, tc := range testCases {
  100. t.Run(tc.name, func(t *testing.T) {
  101. index, found := tc.cs.bsearchKey(tc.key)
  102. if got, want := index, tc.wantIndex; got != want {
  103. t.Errorf("expected %v, got %v", want, got)
  104. }
  105. if got, want := found, tc.wantFound; got != want {
  106. t.Errorf("expected %v, got %v", want, got)
  107. }
  108. })
  109. }
  110. }
  111. func TestSegmentSet(t *testing.T) {
  112. testSegment := &CompactMapSegment{
  113. list: []CompactNeedleValue{
  114. CompactNeedleValue{key: 10, offset: OffsetToCompact(types.Uint32ToOffset(0)), size: 100},
  115. CompactNeedleValue{key: 20, offset: OffsetToCompact(types.Uint32ToOffset(100)), size: 200},
  116. CompactNeedleValue{key: 30, offset: OffsetToCompact(types.Uint32ToOffset(300)), size: 300},
  117. },
  118. firstKey: 10,
  119. lastKey: 30,
  120. }
  121. if got, want := testSegment.len(), 3; got != want {
  122. t.Errorf("got starting size %d, want %d", got, want)
  123. }
  124. if got, want := testSegment.cap(), 3; got != want {
  125. t.Errorf("got starting capacity %d, want %d", got, want)
  126. }
  127. testSets := []struct {
  128. name string
  129. key types.NeedleId
  130. offset types.Offset
  131. size types.Size
  132. wantOffset types.Offset
  133. wantSize types.Size
  134. }{
  135. {
  136. name: "insert at beggining",
  137. key: 5, offset: types.Uint32ToOffset(1000), size: 123,
  138. wantOffset: types.Uint32ToOffset(0), wantSize: 0,
  139. },
  140. {
  141. name: "insert at end",
  142. key: 51, offset: types.Uint32ToOffset(7000), size: 456,
  143. wantOffset: types.Uint32ToOffset(0), wantSize: 0,
  144. },
  145. {
  146. name: "insert in middle",
  147. key: 25, offset: types.Uint32ToOffset(8000), size: 789,
  148. wantOffset: types.Uint32ToOffset(0), wantSize: 0,
  149. },
  150. {
  151. name: "update existing",
  152. key: 30, offset: types.Uint32ToOffset(9000), size: 999,
  153. wantOffset: types.Uint32ToOffset(300), wantSize: 300,
  154. },
  155. }
  156. for _, ts := range testSets {
  157. offset, size := testSegment.set(ts.key, ts.offset, ts.size)
  158. if offset != ts.wantOffset {
  159. t.Errorf("%s: got offset %v, want %v", ts.name, offset, ts.wantOffset)
  160. }
  161. if size != ts.wantSize {
  162. t.Errorf("%s: got size %v, want %v", ts.name, size, ts.wantSize)
  163. }
  164. }
  165. wantSegment := &CompactMapSegment{
  166. list: []CompactNeedleValue{
  167. CompactNeedleValue{key: 5, offset: OffsetToCompact(types.Uint32ToOffset(1000)), size: 123},
  168. CompactNeedleValue{key: 10, offset: OffsetToCompact(types.Uint32ToOffset(0)), size: 100},
  169. CompactNeedleValue{key: 20, offset: OffsetToCompact(types.Uint32ToOffset(100)), size: 200},
  170. CompactNeedleValue{key: 25, offset: OffsetToCompact(types.Uint32ToOffset(8000)), size: 789},
  171. CompactNeedleValue{key: 30, offset: OffsetToCompact(types.Uint32ToOffset(9000)), size: 999},
  172. CompactNeedleValue{key: 51, offset: OffsetToCompact(types.Uint32ToOffset(7000)), size: 456},
  173. },
  174. firstKey: 5,
  175. lastKey: 51,
  176. }
  177. if !reflect.DeepEqual(testSegment, wantSegment) {
  178. t.Errorf("got result segment %v, want %v", testSegment, wantSegment)
  179. }
  180. if got, want := testSegment.len(), 6; got != want {
  181. t.Errorf("got result size %d, want %d", got, want)
  182. }
  183. if got, want := testSegment.cap(), 6; got != want {
  184. t.Errorf("got result capacity %d, want %d", got, want)
  185. }
  186. }
  187. func TestSegmentSetOrdering(t *testing.T) {
  188. keys := []types.NeedleId{}
  189. for i := 0; i < SegmentChunkSize; i++ {
  190. keys = append(keys, types.NeedleId(i))
  191. }
  192. r := rand.New(rand.NewSource(123456789))
  193. r.Shuffle(len(keys), func(i, j int) { keys[i], keys[j] = keys[j], keys[i] })
  194. cs := newCompactMapSegment(0)
  195. for _, k := range keys {
  196. _, _ = cs.set(k, types.Uint32ToOffset(123), 456)
  197. }
  198. if got, want := cs.len(), SegmentChunkSize; got != want {
  199. t.Errorf("expected size %d, got %d", want, got)
  200. }
  201. for i := 1; i < cs.len(); i++ {
  202. if ka, kb := cs.list[i-1].key, cs.list[i].key; ka >= kb {
  203. t.Errorf("found out of order entries at (%d, %d) = (%d, %d)", i-1, i, ka, kb)
  204. }
  205. }
  206. }
  207. func TestSegmentGet(t *testing.T) {
  208. testSegment := &CompactMapSegment{
  209. list: []CompactNeedleValue{
  210. CompactNeedleValue{key: 10, offset: OffsetToCompact(types.Uint32ToOffset(0)), size: 100},
  211. CompactNeedleValue{key: 20, offset: OffsetToCompact(types.Uint32ToOffset(100)), size: 200},
  212. CompactNeedleValue{key: 30, offset: OffsetToCompact(types.Uint32ToOffset(300)), size: 300},
  213. },
  214. firstKey: 10,
  215. lastKey: 30,
  216. }
  217. testCases := []struct {
  218. name string
  219. key types.NeedleId
  220. wantValue *CompactNeedleValue
  221. wantFound bool
  222. }{
  223. {
  224. name: "invalid key",
  225. key: 99,
  226. wantValue: nil,
  227. wantFound: false,
  228. },
  229. {
  230. name: "key #1",
  231. key: 10,
  232. wantValue: &testSegment.list[0],
  233. wantFound: true,
  234. },
  235. {
  236. name: "key #2",
  237. key: 20,
  238. wantValue: &testSegment.list[1],
  239. wantFound: true,
  240. },
  241. {
  242. name: "key #3",
  243. key: 30,
  244. wantValue: &testSegment.list[2],
  245. wantFound: true,
  246. },
  247. }
  248. for _, tc := range testCases {
  249. t.Run(tc.name, func(t *testing.T) {
  250. value, found := testSegment.get(tc.key)
  251. if got, want := value, tc.wantValue; got != want {
  252. t.Errorf("got %v, want %v", got, want)
  253. }
  254. if got, want := found, tc.wantFound; got != want {
  255. t.Errorf("got %v, want %v", got, want)
  256. }
  257. })
  258. }
  259. }
  260. func TestSegmentDelete(t *testing.T) {
  261. testSegment := &CompactMapSegment{
  262. list: []CompactNeedleValue{
  263. CompactNeedleValue{key: 10, offset: OffsetToCompact(types.Uint32ToOffset(0)), size: 100},
  264. CompactNeedleValue{key: 20, offset: OffsetToCompact(types.Uint32ToOffset(100)), size: 200},
  265. CompactNeedleValue{key: 30, offset: OffsetToCompact(types.Uint32ToOffset(300)), size: 300},
  266. CompactNeedleValue{key: 40, offset: OffsetToCompact(types.Uint32ToOffset(600)), size: 400},
  267. },
  268. firstKey: 10,
  269. lastKey: 40,
  270. }
  271. testDeletes := []struct {
  272. name string
  273. key types.NeedleId
  274. want types.Size
  275. }{
  276. {
  277. name: "invalid key",
  278. key: 99,
  279. want: 0,
  280. },
  281. {
  282. name: "delete key #2",
  283. key: 20,
  284. want: 200,
  285. },
  286. {
  287. name: "delete key #4",
  288. key: 40,
  289. want: 400,
  290. },
  291. }
  292. for _, td := range testDeletes {
  293. size := testSegment.delete(td.key)
  294. if got, want := size, td.want; got != want {
  295. t.Errorf("%s: got %v, want %v", td.name, got, want)
  296. }
  297. }
  298. wantSegment := &CompactMapSegment{
  299. list: []CompactNeedleValue{
  300. CompactNeedleValue{key: 10, offset: OffsetToCompact(types.Uint32ToOffset(0)), size: 100},
  301. CompactNeedleValue{key: 20, offset: OffsetToCompact(types.Uint32ToOffset(100)), size: -200},
  302. CompactNeedleValue{key: 30, offset: OffsetToCompact(types.Uint32ToOffset(300)), size: 300},
  303. CompactNeedleValue{key: 40, offset: OffsetToCompact(types.Uint32ToOffset(600)), size: -400},
  304. },
  305. firstKey: 10,
  306. lastKey: 40,
  307. }
  308. if !reflect.DeepEqual(testSegment, wantSegment) {
  309. t.Errorf("got result segment %v, want %v", testSegment, wantSegment)
  310. }
  311. }
  312. func TestSegmentForKey(t *testing.T) {
  313. testMap := NewCompactMap()
  314. tests := []struct {
  315. name string
  316. key types.NeedleId
  317. want *CompactMapSegment
  318. }{
  319. {
  320. name: "first segment",
  321. key: 12,
  322. want: &CompactMapSegment{
  323. list: []CompactNeedleValue{},
  324. chunk: 0,
  325. firstKey: MaxCompactKey,
  326. lastKey: 0,
  327. },
  328. },
  329. {
  330. name: "second segment, gapless",
  331. key: SegmentChunkSize + 34,
  332. want: &CompactMapSegment{
  333. list: []CompactNeedleValue{},
  334. chunk: 1,
  335. firstKey: MaxCompactKey,
  336. lastKey: 0,
  337. },
  338. },
  339. {
  340. name: "gapped segment",
  341. key: (5 * SegmentChunkSize) + 56,
  342. want: &CompactMapSegment{
  343. list: []CompactNeedleValue{},
  344. chunk: 5,
  345. firstKey: MaxCompactKey,
  346. lastKey: 0,
  347. },
  348. },
  349. }
  350. for _, tc := range tests {
  351. t.Run(tc.name, func(t *testing.T) {
  352. cs := testMap.segmentForKey(tc.key)
  353. if !reflect.DeepEqual(cs, tc.want) {
  354. t.Errorf("got segment %v, want %v", cs, tc.want)
  355. }
  356. })
  357. }
  358. wantMap := &CompactMap{
  359. segments: map[Chunk]*CompactMapSegment{
  360. 0: &CompactMapSegment{
  361. list: []CompactNeedleValue{},
  362. chunk: 0,
  363. firstKey: MaxCompactKey,
  364. lastKey: 0,
  365. },
  366. 1: &CompactMapSegment{
  367. list: []CompactNeedleValue{},
  368. chunk: 1,
  369. firstKey: MaxCompactKey,
  370. lastKey: 0,
  371. },
  372. 5: &CompactMapSegment{
  373. list: []CompactNeedleValue{},
  374. chunk: 5,
  375. firstKey: MaxCompactKey,
  376. lastKey: 0,
  377. },
  378. },
  379. }
  380. if !reflect.DeepEqual(testMap, wantMap) {
  381. t.Errorf("got map %v, want %v", testMap, wantMap)
  382. }
  383. }
  384. func TestAscendingVisit(t *testing.T) {
  385. cm := NewCompactMap()
  386. for _, nid := range []types.NeedleId{20, 7, 40000, 300000, 0, 100, 500, 10000, 200000} {
  387. cm.Set(nid, types.Uint32ToOffset(123), 456)
  388. }
  389. got := []NeedleValue{}
  390. err := cm.AscendingVisit(func(nv NeedleValue) error {
  391. got = append(got, nv)
  392. return nil
  393. })
  394. if err != nil {
  395. t.Errorf("got error %v, expected none", err)
  396. }
  397. want := []NeedleValue{
  398. NeedleValue{Key: 0, Offset: types.Uint32ToOffset(123), Size: 456},
  399. NeedleValue{Key: 7, Offset: types.Uint32ToOffset(123), Size: 456},
  400. NeedleValue{Key: 20, Offset: types.Uint32ToOffset(123), Size: 456},
  401. NeedleValue{Key: 100, Offset: types.Uint32ToOffset(123), Size: 456},
  402. NeedleValue{Key: 500, Offset: types.Uint32ToOffset(123), Size: 456},
  403. NeedleValue{Key: 10000, Offset: types.Uint32ToOffset(123), Size: 456},
  404. NeedleValue{Key: 40000, Offset: types.Uint32ToOffset(123), Size: 456},
  405. NeedleValue{Key: 200000, Offset: types.Uint32ToOffset(123), Size: 456},
  406. NeedleValue{Key: 300000, Offset: types.Uint32ToOffset(123), Size: 456},
  407. }
  408. if !reflect.DeepEqual(got, want) {
  409. t.Errorf("got values %v, want %v", got, want)
  410. }
  411. }
  412. func TestRandomInsert(t *testing.T) {
  413. count := 8 * SegmentChunkSize
  414. keys := []types.NeedleId{}
  415. for i := 0; i < count; i++ {
  416. keys = append(keys, types.NeedleId(i))
  417. }
  418. r := rand.New(rand.NewSource(123456789))
  419. r.Shuffle(len(keys), func(i, j int) { keys[i], keys[j] = keys[j], keys[i] })
  420. cm := NewCompactMap()
  421. for _, k := range keys {
  422. _, _ = cm.Set(k, types.Uint32ToOffset(123), 456)
  423. }
  424. if got, want := cm.Len(), count; got != want {
  425. t.Errorf("expected size %d, got %d", want, got)
  426. }
  427. last := -1
  428. err := cm.AscendingVisit(func(nv NeedleValue) error {
  429. key := int(nv.Key)
  430. if key <= last {
  431. return fmt.Errorf("found out of order entries (%d vs %d)", key, last)
  432. }
  433. last = key
  434. return nil
  435. })
  436. if err != nil {
  437. t.Errorf("got error %v, expected none", err)
  438. }
  439. // Given that we've written a integer multiple of SegmentChunkSize, all
  440. // segments should be fully utilized and capacity-adjusted.
  441. if l, c := cm.Len(), cm.Cap(); l != c {
  442. t.Errorf("map length (%d) doesn't match capacity (%d)", l, c)
  443. }
  444. }