filechunks_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. package filer2
  2. import (
  3. "log"
  4. "testing"
  5. "fmt"
  6. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  7. )
  8. func TestCompactFileChunks(t *testing.T) {
  9. chunks := []*filer_pb.FileChunk{
  10. {Offset: 10, Size: 100, FileId: "abc", Mtime: 50},
  11. {Offset: 100, Size: 100, FileId: "def", Mtime: 100},
  12. {Offset: 200, Size: 100, FileId: "ghi", Mtime: 200},
  13. {Offset: 110, Size: 200, FileId: "jkl", Mtime: 300},
  14. }
  15. compacted, garbage := CompactFileChunks(chunks)
  16. if len(compacted) != 3 {
  17. t.Fatalf("unexpected compacted: %d", len(compacted))
  18. }
  19. if len(garbage) != 1 {
  20. t.Fatalf("unexpected garbage: %d", len(garbage))
  21. }
  22. }
  23. func TestCompactFileChunks2(t *testing.T) {
  24. chunks := []*filer_pb.FileChunk{
  25. {Offset: 0, Size: 100, FileId: "abc", Mtime: 50},
  26. {Offset: 100, Size: 100, FileId: "def", Mtime: 100},
  27. {Offset: 200, Size: 100, FileId: "ghi", Mtime: 200},
  28. {Offset: 0, Size: 100, FileId: "abcf", Mtime: 300},
  29. {Offset: 50, Size: 100, FileId: "fhfh", Mtime: 400},
  30. {Offset: 100, Size: 100, FileId: "yuyu", Mtime: 500},
  31. }
  32. k := 3
  33. for n := 0; n < k; n++ {
  34. chunks = append(chunks, &filer_pb.FileChunk{
  35. Offset: int64(n * 100), Size: 100, FileId: fmt.Sprintf("fileId%d", n), Mtime: int64(n),
  36. })
  37. chunks = append(chunks, &filer_pb.FileChunk{
  38. Offset: int64(n * 50), Size: 100, FileId: fmt.Sprintf("fileId%d", n+k), Mtime: int64(n + k),
  39. })
  40. }
  41. compacted, garbage := CompactFileChunks(chunks)
  42. if len(compacted) != 4 {
  43. t.Fatalf("unexpected compacted: %d", len(compacted))
  44. }
  45. if len(garbage) != 8 {
  46. t.Fatalf("unexpected garbage: %d", len(garbage))
  47. }
  48. }
  49. func TestIntervalMerging(t *testing.T) {
  50. testcases := []struct {
  51. Chunks []*filer_pb.FileChunk
  52. Expected []*VisibleInterval
  53. }{
  54. // case 0: normal
  55. {
  56. Chunks: []*filer_pb.FileChunk{
  57. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  58. {Offset: 100, Size: 100, FileId: "asdf", Mtime: 134},
  59. {Offset: 200, Size: 100, FileId: "fsad", Mtime: 353},
  60. },
  61. Expected: []*VisibleInterval{
  62. {start: 0, stop: 100, fileId: "abc"},
  63. {start: 100, stop: 200, fileId: "asdf"},
  64. {start: 200, stop: 300, fileId: "fsad"},
  65. },
  66. },
  67. // case 1: updates overwrite full chunks
  68. {
  69. Chunks: []*filer_pb.FileChunk{
  70. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  71. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  72. },
  73. Expected: []*VisibleInterval{
  74. {start: 0, stop: 200, fileId: "asdf"},
  75. },
  76. },
  77. // case 2: updates overwrite part of previous chunks
  78. {
  79. Chunks: []*filer_pb.FileChunk{
  80. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  81. {Offset: 0, Size: 50, FileId: "asdf", Mtime: 134},
  82. },
  83. Expected: []*VisibleInterval{
  84. {start: 0, stop: 50, fileId: "asdf"},
  85. {start: 50, stop: 100, fileId: "abc"},
  86. },
  87. },
  88. // case 3: updates overwrite full chunks
  89. {
  90. Chunks: []*filer_pb.FileChunk{
  91. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  92. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  93. {Offset: 50, Size: 250, FileId: "xxxx", Mtime: 154},
  94. },
  95. Expected: []*VisibleInterval{
  96. {start: 0, stop: 50, fileId: "asdf"},
  97. {start: 50, stop: 300, fileId: "xxxx"},
  98. },
  99. },
  100. // case 4: updates far away from prev chunks
  101. {
  102. Chunks: []*filer_pb.FileChunk{
  103. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  104. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  105. {Offset: 250, Size: 250, FileId: "xxxx", Mtime: 154},
  106. },
  107. Expected: []*VisibleInterval{
  108. {start: 0, stop: 200, fileId: "asdf"},
  109. {start: 250, stop: 500, fileId: "xxxx"},
  110. },
  111. },
  112. // case 5: updates overwrite full chunks
  113. {
  114. Chunks: []*filer_pb.FileChunk{
  115. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  116. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 184},
  117. {Offset: 70, Size: 150, FileId: "abc", Mtime: 143},
  118. {Offset: 80, Size: 100, FileId: "xxxx", Mtime: 134},
  119. },
  120. Expected: []*VisibleInterval{
  121. {start: 0, stop: 200, fileId: "asdf"},
  122. {start: 200, stop: 220, fileId: "abc"},
  123. },
  124. },
  125. // case 6: same updates
  126. {
  127. Chunks: []*filer_pb.FileChunk{
  128. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  129. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  130. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  131. },
  132. Expected: []*VisibleInterval{
  133. {start: 0, stop: 100, fileId: "abc"},
  134. },
  135. },
  136. // case 7: real updates
  137. {
  138. Chunks: []*filer_pb.FileChunk{
  139. {Offset: 0, Size: 2097152, FileId: "7,0294cbb9892b", Mtime: 123},
  140. {Offset: 0, Size: 3145728, FileId: "3,029565bf3092", Mtime: 130},
  141. {Offset: 2097152, Size: 3145728, FileId: "6,029632f47ae2", Mtime: 140},
  142. {Offset: 5242880, Size: 3145728, FileId: "2,029734c5aa10", Mtime: 150},
  143. {Offset: 8388608, Size: 3145728, FileId: "5,02982f80de50", Mtime: 160},
  144. {Offset: 11534336, Size: 2842193, FileId: "7,0299ad723803", Mtime: 170},
  145. },
  146. Expected: []*VisibleInterval{
  147. {start: 0, stop: 2097152, fileId: "3,029565bf3092"},
  148. {start: 2097152, stop: 5242880, fileId: "6,029632f47ae2"},
  149. {start: 5242880, stop: 8388608, fileId: "2,029734c5aa10"},
  150. {start: 8388608, stop: 11534336, fileId: "5,02982f80de50"},
  151. {start: 11534336, stop: 14376529, fileId: "7,0299ad723803"},
  152. },
  153. },
  154. // case 8: real bug
  155. {
  156. Chunks: []*filer_pb.FileChunk{
  157. {Offset: 0, Size: 77824, FileId: "4,0b3df938e301", Mtime: 123},
  158. {Offset: 471040, Size: 472225 - 471040, FileId: "6,0b3e0650019c", Mtime: 130},
  159. {Offset: 77824, Size: 208896 - 77824, FileId: "4,0b3f0c7202f0", Mtime: 140},
  160. {Offset: 208896, Size: 339968 - 208896, FileId: "2,0b4031a72689", Mtime: 150},
  161. {Offset: 339968, Size: 471040 - 339968, FileId: "3,0b416a557362", Mtime: 160},
  162. },
  163. Expected: []*VisibleInterval{
  164. {start: 0, stop: 77824, fileId: "4,0b3df938e301"},
  165. {start: 77824, stop: 208896, fileId: "4,0b3f0c7202f0"},
  166. {start: 208896, stop: 339968, fileId: "2,0b4031a72689"},
  167. {start: 339968, stop: 471040, fileId: "3,0b416a557362"},
  168. {start: 471040, stop: 472225, fileId: "6,0b3e0650019c"},
  169. },
  170. },
  171. }
  172. for i, testcase := range testcases {
  173. log.Printf("++++++++++ merged test case %d ++++++++++++++++++++", i)
  174. intervals := NonOverlappingVisibleIntervals(testcase.Chunks)
  175. for x, interval := range intervals {
  176. log.Printf("test case %d, interval %d, start=%d, stop=%d, fileId=%s",
  177. i, x, interval.start, interval.stop, interval.fileId)
  178. }
  179. for x, interval := range intervals {
  180. if interval.start != testcase.Expected[x].start {
  181. t.Fatalf("failed on test case %d, interval %d, start %d, expect %d",
  182. i, x, interval.start, testcase.Expected[x].start)
  183. }
  184. if interval.stop != testcase.Expected[x].stop {
  185. t.Fatalf("failed on test case %d, interval %d, stop %d, expect %d",
  186. i, x, interval.stop, testcase.Expected[x].stop)
  187. }
  188. if interval.fileId != testcase.Expected[x].fileId {
  189. t.Fatalf("failed on test case %d, interval %d, chunkId %s, expect %s",
  190. i, x, interval.fileId, testcase.Expected[x].fileId)
  191. }
  192. }
  193. if len(intervals) != len(testcase.Expected) {
  194. t.Fatalf("failed to compact test case %d, len %d expected %d", i, len(intervals), len(testcase.Expected))
  195. }
  196. }
  197. }
  198. func TestChunksReading(t *testing.T) {
  199. testcases := []struct {
  200. Chunks []*filer_pb.FileChunk
  201. Offset int64
  202. Size int
  203. Expected []*ChunkView
  204. }{
  205. // case 0: normal
  206. {
  207. Chunks: []*filer_pb.FileChunk{
  208. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  209. {Offset: 100, Size: 100, FileId: "asdf", Mtime: 134},
  210. {Offset: 200, Size: 100, FileId: "fsad", Mtime: 353},
  211. },
  212. Offset: 0,
  213. Size: 250,
  214. Expected: []*ChunkView{
  215. {Offset: 0, Size: 100, FileId: "abc", LogicOffset: 0},
  216. {Offset: 0, Size: 100, FileId: "asdf", LogicOffset: 100},
  217. {Offset: 0, Size: 50, FileId: "fsad", LogicOffset: 200},
  218. },
  219. },
  220. // case 1: updates overwrite full chunks
  221. {
  222. Chunks: []*filer_pb.FileChunk{
  223. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  224. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  225. },
  226. Offset: 50,
  227. Size: 100,
  228. Expected: []*ChunkView{
  229. {Offset: 50, Size: 100, FileId: "asdf", LogicOffset: 50},
  230. },
  231. },
  232. // case 2: updates overwrite part of previous chunks
  233. {
  234. Chunks: []*filer_pb.FileChunk{
  235. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  236. {Offset: 0, Size: 50, FileId: "asdf", Mtime: 134},
  237. },
  238. Offset: 25,
  239. Size: 50,
  240. Expected: []*ChunkView{
  241. {Offset: 25, Size: 25, FileId: "asdf", LogicOffset: 25},
  242. {Offset: 0, Size: 25, FileId: "abc", LogicOffset: 50},
  243. },
  244. },
  245. // case 3: updates overwrite full chunks
  246. {
  247. Chunks: []*filer_pb.FileChunk{
  248. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  249. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  250. {Offset: 50, Size: 250, FileId: "xxxx", Mtime: 154},
  251. },
  252. Offset: 0,
  253. Size: 200,
  254. Expected: []*ChunkView{
  255. {Offset: 0, Size: 50, FileId: "asdf", LogicOffset: 0},
  256. {Offset: 0, Size: 150, FileId: "xxxx", LogicOffset: 50},
  257. },
  258. },
  259. // case 4: updates far away from prev chunks
  260. {
  261. Chunks: []*filer_pb.FileChunk{
  262. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  263. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 134},
  264. {Offset: 250, Size: 250, FileId: "xxxx", Mtime: 154},
  265. },
  266. Offset: 0,
  267. Size: 400,
  268. Expected: []*ChunkView{
  269. {Offset: 0, Size: 200, FileId: "asdf", LogicOffset: 0},
  270. // {Offset: 0, Size: 150, FileId: "xxxx"}, // missing intervals should not happen
  271. },
  272. },
  273. // case 5: updates overwrite full chunks
  274. {
  275. Chunks: []*filer_pb.FileChunk{
  276. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  277. {Offset: 0, Size: 200, FileId: "asdf", Mtime: 184},
  278. {Offset: 70, Size: 150, FileId: "abc", Mtime: 143},
  279. {Offset: 80, Size: 100, FileId: "xxxx", Mtime: 134},
  280. },
  281. Offset: 0,
  282. Size: 220,
  283. Expected: []*ChunkView{
  284. {Offset: 0, Size: 200, FileId: "asdf", LogicOffset: 0},
  285. {Offset: 0, Size: 20, FileId: "abc", LogicOffset: 200},
  286. },
  287. },
  288. // case 6: same updates
  289. {
  290. Chunks: []*filer_pb.FileChunk{
  291. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  292. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  293. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  294. },
  295. Offset: 0,
  296. Size: 100,
  297. Expected: []*ChunkView{
  298. {Offset: 0, Size: 100, FileId: "abc", LogicOffset: 0},
  299. },
  300. },
  301. // case 7: edge cases
  302. {
  303. Chunks: []*filer_pb.FileChunk{
  304. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  305. {Offset: 100, Size: 100, FileId: "asdf", Mtime: 134},
  306. {Offset: 200, Size: 100, FileId: "fsad", Mtime: 353},
  307. },
  308. Offset: 0,
  309. Size: 200,
  310. Expected: []*ChunkView{
  311. {Offset: 0, Size: 100, FileId: "abc", LogicOffset: 0},
  312. {Offset: 0, Size: 100, FileId: "asdf", LogicOffset: 100},
  313. },
  314. },
  315. // case 8: edge cases
  316. {
  317. Chunks: []*filer_pb.FileChunk{
  318. {Offset: 0, Size: 100, FileId: "abc", Mtime: 123},
  319. {Offset: 90, Size: 200, FileId: "asdf", Mtime: 134},
  320. {Offset: 190, Size: 300, FileId: "fsad", Mtime: 353},
  321. },
  322. Offset: 0,
  323. Size: 300,
  324. Expected: []*ChunkView{
  325. {Offset: 0, Size: 90, FileId: "abc", LogicOffset: 0},
  326. {Offset: 0, Size: 100, FileId: "asdf", LogicOffset: 90},
  327. {Offset: 0, Size: 110, FileId: "fsad", LogicOffset: 190},
  328. },
  329. },
  330. // case 9: edge cases
  331. {
  332. Chunks: []*filer_pb.FileChunk{
  333. {Offset: 0, Size: 43175947, FileId: "2,111fc2cbfac1", Mtime: 1},
  334. {Offset: 43175936, Size: 52981771 - 43175936, FileId: "2,112a36ea7f85", Mtime: 2},
  335. {Offset: 52981760, Size: 72564747 - 52981760, FileId: "4,112d5f31c5e7", Mtime: 3},
  336. {Offset: 72564736, Size: 133255179 - 72564736, FileId: "1,113245f0cdb6", Mtime: 4},
  337. {Offset: 133255168, Size: 137269259 - 133255168, FileId: "3,1141a70733b5", Mtime: 5},
  338. {Offset: 137269248, Size: 153578836 - 137269248, FileId: "1,114201d5bbdb", Mtime: 6},
  339. },
  340. Offset: 0,
  341. Size: 153578836,
  342. Expected: []*ChunkView{
  343. {Offset: 0, Size: 43175936, FileId: "2,111fc2cbfac1", LogicOffset: 0},
  344. {Offset: 0, Size: 52981760 - 43175936, FileId: "2,112a36ea7f85", LogicOffset: 43175936},
  345. {Offset: 0, Size: 72564736 - 52981760, FileId: "4,112d5f31c5e7", LogicOffset: 52981760},
  346. {Offset: 0, Size: 133255168 - 72564736, FileId: "1,113245f0cdb6", LogicOffset: 72564736},
  347. {Offset: 0, Size: 137269248 - 133255168, FileId: "3,1141a70733b5", LogicOffset: 133255168},
  348. {Offset: 0, Size: 153578836 - 137269248, FileId: "1,114201d5bbdb", LogicOffset: 137269248},
  349. },
  350. },
  351. }
  352. for i, testcase := range testcases {
  353. log.Printf("++++++++++ read test case %d ++++++++++++++++++++", i)
  354. chunks := ViewFromChunks(testcase.Chunks, testcase.Offset, testcase.Size)
  355. for x, chunk := range chunks {
  356. log.Printf("read case %d, chunk %d, offset=%d, size=%d, fileId=%s",
  357. i, x, chunk.Offset, chunk.Size, chunk.FileId)
  358. if chunk.Offset != testcase.Expected[x].Offset {
  359. t.Fatalf("failed on read case %d, chunk %d, Offset %d, expect %d",
  360. i, x, chunk.Offset, testcase.Expected[x].Offset)
  361. }
  362. if chunk.Size != testcase.Expected[x].Size {
  363. t.Fatalf("failed on read case %d, chunk %d, Size %d, expect %d",
  364. i, x, chunk.Size, testcase.Expected[x].Size)
  365. }
  366. if chunk.FileId != testcase.Expected[x].FileId {
  367. t.Fatalf("failed on read case %d, chunk %d, FileId %s, expect %s",
  368. i, x, chunk.FileId, testcase.Expected[x].FileId)
  369. }
  370. if chunk.LogicOffset != testcase.Expected[x].LogicOffset {
  371. t.Fatalf("failed on read case %d, chunk %d, LogicOffset %d, expect %d",
  372. i, x, chunk.LogicOffset, testcase.Expected[x].LogicOffset)
  373. }
  374. }
  375. if len(chunks) != len(testcase.Expected) {
  376. t.Fatalf("failed to read test case %d, len %d expected %d", i, len(chunks), len(testcase.Expected))
  377. }
  378. }
  379. }
  380. func BenchmarkCompactFileChunks(b *testing.B) {
  381. var chunks []*filer_pb.FileChunk
  382. k := 1024
  383. for n := 0; n < k; n++ {
  384. chunks = append(chunks, &filer_pb.FileChunk{
  385. Offset: int64(n * 100), Size: 100, FileId: fmt.Sprintf("fileId%d", n), Mtime: int64(n),
  386. })
  387. chunks = append(chunks, &filer_pb.FileChunk{
  388. Offset: int64(n * 50), Size: 100, FileId: fmt.Sprintf("fileId%d", n+k), Mtime: int64(n + k),
  389. })
  390. }
  391. for n := 0; n < b.N; n++ {
  392. CompactFileChunks(chunks)
  393. }
  394. }