/src/dbnode/storage/index/read_through_segment_test.go

https://github.com/m3db/m3 · Go · 416 lines · 281 code · 91 blank · 44 comment · 18 complexity · 75bdd7214b0cbdea1f1a264327ac5067 MD5 · raw file

  1. // Copyright (c) 2019 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package index
  21. import (
  22. "regexp/syntax"
  23. "testing"
  24. "github.com/m3db/m3/src/m3ninx/index"
  25. "github.com/m3db/m3/src/m3ninx/index/segment"
  26. "github.com/m3db/m3/src/m3ninx/index/segment/fst"
  27. "github.com/m3db/m3/src/m3ninx/postings/roaring"
  28. "github.com/golang/mock/gomock"
  29. "github.com/stretchr/testify/require"
  30. )
  31. var (
  32. defaultReadThroughSegmentOptions = ReadThroughSegmentOptions{
  33. CacheRegexp: true,
  34. CacheTerms: true,
  35. }
  36. )
  37. func TestReadThroughSegmentMatchRegexp(t *testing.T) {
  38. ctrl := gomock.NewController(t)
  39. defer ctrl.Finish()
  40. seg := fst.NewMockSegment(ctrl)
  41. reader := segment.NewMockReader(ctrl)
  42. seg.EXPECT().Reader().Return(reader, nil)
  43. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  44. require.NoError(t, err)
  45. defer stopReporting()
  46. field := []byte("some-field")
  47. parsedRegex, err := syntax.Parse(".*this-will-be-slow.*", syntax.Simple)
  48. require.NoError(t, err)
  49. compiledRegex := index.CompiledRegex{
  50. FSTSyntax: parsedRegex,
  51. }
  52. readThrough, err := NewReadThroughSegment(
  53. seg, cache, defaultReadThroughSegmentOptions).Reader()
  54. require.NoError(t, err)
  55. originalPL := roaring.NewPostingsList()
  56. require.NoError(t, originalPL.Insert(1))
  57. reader.EXPECT().MatchRegexp(field, gomock.Any()).Return(originalPL, nil)
  58. // Make sure it goes to the segment when the cache misses.
  59. pl, err := readThrough.MatchRegexp(field, compiledRegex)
  60. require.NoError(t, err)
  61. require.True(t, pl.Equal(originalPL))
  62. // Make sure it relies on the cache if its present (mock only expects
  63. // one call.)
  64. pl, err = readThrough.MatchRegexp(field, compiledRegex)
  65. require.NoError(t, err)
  66. require.True(t, pl.Equal(originalPL))
  67. }
  68. func TestReadThroughSegmentMatchRegexpCacheDisabled(t *testing.T) {
  69. ctrl := gomock.NewController(t)
  70. defer ctrl.Finish()
  71. seg := fst.NewMockSegment(ctrl)
  72. reader := segment.NewMockReader(ctrl)
  73. seg.EXPECT().Reader().Return(reader, nil)
  74. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  75. require.NoError(t, err)
  76. defer stopReporting()
  77. field := []byte("some-field")
  78. parsedRegex, err := syntax.Parse(".*this-will-be-slow.*", syntax.Simple)
  79. require.NoError(t, err)
  80. compiledRegex := index.CompiledRegex{
  81. FSTSyntax: parsedRegex,
  82. }
  83. readThrough, err := NewReadThroughSegment(seg, cache, ReadThroughSegmentOptions{
  84. CacheRegexp: false,
  85. }).Reader()
  86. require.NoError(t, err)
  87. originalPL := roaring.NewPostingsList()
  88. require.NoError(t, originalPL.Insert(1))
  89. reader.EXPECT().
  90. MatchRegexp(field, gomock.Any()).
  91. Return(originalPL, nil).
  92. Times(2)
  93. // Make sure it goes to the segment.
  94. pl, err := readThrough.MatchRegexp(field, compiledRegex)
  95. require.NoError(t, err)
  96. require.True(t, pl.Equal(originalPL))
  97. // Make sure it goes to the segment the second time - meaning the cache was
  98. // disabled.
  99. pl, err = readThrough.MatchRegexp(field, compiledRegex)
  100. require.NoError(t, err)
  101. require.True(t, pl.Equal(originalPL))
  102. }
  103. func TestReadThroughSegmentMatchRegexpNoCache(t *testing.T) {
  104. ctrl := gomock.NewController(t)
  105. defer ctrl.Finish()
  106. var (
  107. seg = fst.NewMockSegment(ctrl)
  108. reader = segment.NewMockReader(ctrl)
  109. field = []byte("some-field")
  110. parsedRegex, err = syntax.Parse(".*this-will-be-slow.*", syntax.Simple)
  111. )
  112. require.NoError(t, err)
  113. seg.EXPECT().Reader().Return(reader, nil)
  114. compiledRegex := index.CompiledRegex{
  115. FSTSyntax: parsedRegex,
  116. }
  117. readThrough, err := NewReadThroughSegment(
  118. seg, nil, defaultReadThroughSegmentOptions).Reader()
  119. require.NoError(t, err)
  120. originalPL := roaring.NewPostingsList()
  121. require.NoError(t, originalPL.Insert(1))
  122. reader.EXPECT().MatchRegexp(field, gomock.Any()).Return(originalPL, nil)
  123. // Make sure it it works with no cache.
  124. pl, err := readThrough.MatchRegexp(field, compiledRegex)
  125. require.NoError(t, err)
  126. require.True(t, pl.Equal(originalPL))
  127. }
  128. func TestReadThroughSegmentMatchTerm(t *testing.T) {
  129. ctrl := gomock.NewController(t)
  130. defer ctrl.Finish()
  131. seg := fst.NewMockSegment(ctrl)
  132. reader := segment.NewMockReader(ctrl)
  133. seg.EXPECT().Reader().Return(reader, nil)
  134. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  135. require.NoError(t, err)
  136. defer stopReporting()
  137. var (
  138. field = []byte("some-field")
  139. term = []byte("some-term")
  140. originalPL = roaring.NewPostingsList()
  141. )
  142. require.NoError(t, originalPL.Insert(1))
  143. readThrough, err := NewReadThroughSegment(
  144. seg, cache, defaultReadThroughSegmentOptions).Reader()
  145. require.NoError(t, err)
  146. reader.EXPECT().MatchTerm(field, term).Return(originalPL, nil)
  147. // Make sure it goes to the segment when the cache misses.
  148. pl, err := readThrough.MatchTerm(field, term)
  149. require.NoError(t, err)
  150. require.True(t, pl.Equal(originalPL))
  151. // Make sure it relies on the cache if its present (mock only expects
  152. // one call.)
  153. pl, err = readThrough.MatchTerm(field, term)
  154. require.NoError(t, err)
  155. require.True(t, pl.Equal(originalPL))
  156. }
  157. func TestReadThroughSegmentMatchTermCacheDisabled(t *testing.T) {
  158. ctrl := gomock.NewController(t)
  159. defer ctrl.Finish()
  160. seg := fst.NewMockSegment(ctrl)
  161. reader := segment.NewMockReader(ctrl)
  162. seg.EXPECT().Reader().Return(reader, nil)
  163. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  164. require.NoError(t, err)
  165. defer stopReporting()
  166. var (
  167. field = []byte("some-field")
  168. term = []byte("some-term")
  169. originalPL = roaring.NewPostingsList()
  170. )
  171. require.NoError(t, originalPL.Insert(1))
  172. readThrough, err := NewReadThroughSegment(seg, cache, ReadThroughSegmentOptions{
  173. CacheTerms: false,
  174. }).Reader()
  175. require.NoError(t, err)
  176. reader.EXPECT().
  177. MatchTerm(field, term).
  178. Return(originalPL, nil).
  179. Times(2)
  180. // Make sure it goes to the segment when the cache misses.
  181. pl, err := readThrough.MatchTerm(field, term)
  182. require.NoError(t, err)
  183. require.True(t, pl.Equal(originalPL))
  184. // Make sure it goes to the segment the second time - meaning the cache was
  185. // disabled.
  186. pl, err = readThrough.MatchTerm(field, term)
  187. require.NoError(t, err)
  188. require.True(t, pl.Equal(originalPL))
  189. }
  190. func TestReadThroughSegmentMatchTermNoCache(t *testing.T) {
  191. ctrl := gomock.NewController(t)
  192. defer ctrl.Finish()
  193. var (
  194. seg = fst.NewMockSegment(ctrl)
  195. reader = segment.NewMockReader(ctrl)
  196. field = []byte("some-field")
  197. term = []byte("some-term")
  198. originalPL = roaring.NewPostingsList()
  199. )
  200. require.NoError(t, originalPL.Insert(1))
  201. seg.EXPECT().Reader().Return(reader, nil)
  202. readThrough, err := NewReadThroughSegment(
  203. seg, nil, defaultReadThroughSegmentOptions).Reader()
  204. require.NoError(t, err)
  205. reader.EXPECT().MatchTerm(field, term).Return(originalPL, nil)
  206. // Make sure it it works with no cache.
  207. pl, err := readThrough.MatchTerm(field, term)
  208. require.NoError(t, err)
  209. require.True(t, pl.Equal(originalPL))
  210. }
  211. func TestClose(t *testing.T) {
  212. ctrl := gomock.NewController(t)
  213. defer ctrl.Finish()
  214. segment := fst.NewMockSegment(ctrl)
  215. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  216. require.NoError(t, err)
  217. defer stopReporting()
  218. readThroughSeg := NewReadThroughSegment(
  219. segment, cache, defaultReadThroughSegmentOptions)
  220. segmentUUID := readThroughSeg.(*ReadThroughSegment).uuid
  221. // Store an entry for the segment in the cache so we can check if it
  222. // gets purged after.
  223. cache.PutRegexp(segmentUUID, "some-field", "some-pattern", roaring.NewPostingsList())
  224. segment.EXPECT().Close().Return(nil)
  225. err = readThroughSeg.Close()
  226. require.NoError(t, err)
  227. require.True(t, readThroughSeg.(*ReadThroughSegment).closed)
  228. // Make sure it does not allow double closes.
  229. err = readThroughSeg.Close()
  230. require.Equal(t, errCantCloseClosedSegment, err)
  231. // Make sure it does not allow readers to be created after closing.
  232. _, err = readThroughSeg.Reader()
  233. require.Equal(t, errCantGetReaderFromClosedSegment, err)
  234. }
  235. func TestReadThroughSegmentMatchField(t *testing.T) {
  236. ctrl := gomock.NewController(t)
  237. defer ctrl.Finish()
  238. seg := fst.NewMockSegment(ctrl)
  239. reader := segment.NewMockReader(ctrl)
  240. seg.EXPECT().Reader().Return(reader, nil)
  241. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  242. require.NoError(t, err)
  243. defer stopReporting()
  244. var (
  245. field = []byte("some-field")
  246. originalPL = roaring.NewPostingsList()
  247. )
  248. require.NoError(t, originalPL.Insert(1))
  249. readThrough, err := NewReadThroughSegment(
  250. seg, cache, defaultReadThroughSegmentOptions).Reader()
  251. require.NoError(t, err)
  252. reader.EXPECT().MatchField(field).Return(originalPL, nil)
  253. // Make sure it goes to the segment when the cache misses.
  254. pl, err := readThrough.MatchField(field)
  255. require.NoError(t, err)
  256. require.True(t, pl.Equal(originalPL))
  257. // Make sure it relies on the cache if its present (mock only expects
  258. // one call.)
  259. pl, err = readThrough.MatchField(field)
  260. require.NoError(t, err)
  261. require.True(t, pl.Equal(originalPL))
  262. }
  263. func TestReadThroughSegmentMatchFieldCacheDisabled(t *testing.T) {
  264. ctrl := gomock.NewController(t)
  265. defer ctrl.Finish()
  266. seg := fst.NewMockSegment(ctrl)
  267. reader := segment.NewMockReader(ctrl)
  268. seg.EXPECT().Reader().Return(reader, nil)
  269. cache, stopReporting, err := NewPostingsListCache(1, testPostingListCacheOptions)
  270. require.NoError(t, err)
  271. defer stopReporting()
  272. var (
  273. field = []byte("some-field")
  274. originalPL = roaring.NewPostingsList()
  275. )
  276. require.NoError(t, originalPL.Insert(1))
  277. readThrough, err := NewReadThroughSegment(seg, cache, ReadThroughSegmentOptions{
  278. CacheTerms: false,
  279. }).Reader()
  280. require.NoError(t, err)
  281. reader.EXPECT().
  282. MatchField(field).
  283. Return(originalPL, nil).
  284. Times(2)
  285. // Make sure it goes to the segment when the cache misses.
  286. pl, err := readThrough.MatchField(field)
  287. require.NoError(t, err)
  288. require.True(t, pl.Equal(originalPL))
  289. // Make sure it goes to the segment the second time - meaning the cache was
  290. // disabled.
  291. pl, err = readThrough.MatchField(field)
  292. require.NoError(t, err)
  293. require.True(t, pl.Equal(originalPL))
  294. }
  295. func TestReadThroughSegmentMatchFieldNoCache(t *testing.T) {
  296. ctrl := gomock.NewController(t)
  297. defer ctrl.Finish()
  298. var (
  299. seg = fst.NewMockSegment(ctrl)
  300. reader = segment.NewMockReader(ctrl)
  301. field = []byte("some-field")
  302. originalPL = roaring.NewPostingsList()
  303. )
  304. require.NoError(t, originalPL.Insert(1))
  305. seg.EXPECT().Reader().Return(reader, nil)
  306. readThrough, err := NewReadThroughSegment(
  307. seg, nil, defaultReadThroughSegmentOptions).Reader()
  308. require.NoError(t, err)
  309. reader.EXPECT().MatchField(field).Return(originalPL, nil)
  310. // Make sure it it works with no cache.
  311. pl, err := readThrough.MatchField(field)
  312. require.NoError(t, err)
  313. require.True(t, pl.Equal(originalPL))
  314. }
  315. func TestCloseNoCache(t *testing.T) {
  316. ctrl := gomock.NewController(t)
  317. defer ctrl.Finish()
  318. seg := fst.NewMockSegment(ctrl)
  319. readThrough := NewReadThroughSegment(
  320. seg, nil, defaultReadThroughSegmentOptions)
  321. seg.EXPECT().Close().Return(nil)
  322. err := readThrough.Close()
  323. require.NoError(t, err)
  324. require.True(t, readThrough.(*ReadThroughSegment).closed)
  325. }