cache_ut.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. #include <library/cpp/cache/cache.h>
  2. #include <library/cpp/cache/thread_safe_cache.h>
  3. #include <library/cpp/testing/unittest/registar.h>
  4. struct TStrokaWeighter {
  5. static size_t Weight(const TString& s) {
  6. return s.size();
  7. }
  8. };
  9. Y_UNIT_TEST_SUITE(TCacheTest) {
  10. Y_UNIT_TEST(LRUListTest) {
  11. typedef TLRUList<int, TString> TListType;
  12. TListType list(2);
  13. TListType::TItem x1(1, "ttt");
  14. list.Insert(&x1);
  15. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 1);
  16. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  17. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  18. TListType::TItem x2(2, "yyy");
  19. list.Insert(&x2);
  20. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  21. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  22. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  23. list.Promote(list.GetOldest());
  24. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 2);
  25. TListType::TItem x3(3, "zzz");
  26. list.Insert(&x3);
  27. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  28. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  29. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  30. }
  31. Y_UNIT_TEST(LRUListWeightedTest) {
  32. typedef TLRUList<int, TString, size_t (*)(const TString&)> TListType;
  33. TListType list(7, [](auto& string) {
  34. return string.size();
  35. });
  36. TListType::TItem x1(1, "ttt");
  37. list.Insert(&x1);
  38. while (list.RemoveIfOverflown()) {
  39. }
  40. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 3);
  41. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  42. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  43. TListType::TItem x2(2, "yyy");
  44. list.Insert(&x2);
  45. while (list.RemoveIfOverflown()) {
  46. }
  47. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 6);
  48. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  49. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  50. list.Promote(list.GetOldest());
  51. while (list.RemoveIfOverflown()) {
  52. }
  53. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 2);
  54. TListType::TItem x3(3, "zzz");
  55. list.Insert(&x3);
  56. while (list.RemoveIfOverflown()) {
  57. }
  58. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 6);
  59. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  60. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 1);
  61. TListType::TItem x4(4, "longlong");
  62. list.Insert(&x4);
  63. while (list.RemoveIfOverflown()) {
  64. }
  65. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 8);
  66. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  67. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 4);
  68. TListType::TItem x5(5, "xxx");
  69. list.Insert(&x5);
  70. while (list.RemoveIfOverflown()) {
  71. }
  72. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 3);
  73. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  74. UNIT_ASSERT_EQUAL(list.GetOldest()->Key, 5);
  75. }
  76. Y_UNIT_TEST(LFUListTest) {
  77. typedef TLFUList<int, TString> TListType;
  78. TListType list(2);
  79. TListType::TItem x1(1, "ttt");
  80. list.Insert(&x1);
  81. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 1);
  82. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  83. UNIT_ASSERT_EQUAL(list.GetLeastFrequentlyUsed()->Key, 1);
  84. TListType::TItem x2(2, "yyy");
  85. list.Insert(&x2);
  86. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  87. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  88. UNIT_ASSERT_EQUAL(list.GetLeastFrequentlyUsed()->Key, 1);
  89. list.Promote(list.GetLeastFrequentlyUsed());
  90. UNIT_ASSERT_EQUAL(list.GetLeastFrequentlyUsed()->Key, 2);
  91. TListType::TItem x3(3, "zzz");
  92. list.Insert(&x3);
  93. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  94. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  95. UNIT_ASSERT_EQUAL(list.GetLeastFrequentlyUsed()->Key, 1);
  96. }
  97. Y_UNIT_TEST(LWListTest) {
  98. typedef TLWList<int, TString, size_t, TStrokaWeighter> TListType;
  99. TListType list(2);
  100. TListType::TItem x1(1, "tt");
  101. list.Insert(&x1);
  102. UNIT_ASSERT_EQUAL(list.GetLightest()->Key, 1);
  103. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 1);
  104. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  105. TListType::TItem x2(2, "yyyy");
  106. list.Insert(&x2);
  107. UNIT_ASSERT_EQUAL(list.GetLightest()->Key, 1);
  108. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  109. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  110. TListType::TItem x3(3, "z");
  111. list.Insert(&x3);
  112. UNIT_ASSERT_EQUAL(list.GetLightest()->Key, 1);
  113. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  114. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  115. TListType::TItem x4(4, "xxxxxx");
  116. list.Insert(&x4);
  117. UNIT_ASSERT_EQUAL(list.GetLightest()->Key, 2);
  118. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 2);
  119. UNIT_ASSERT_EQUAL(list.GetSize(), 2);
  120. list.Erase(&x2);
  121. UNIT_ASSERT_EQUAL(list.GetLightest()->Key, 4);
  122. UNIT_ASSERT_EQUAL(list.GetTotalSize(), 1);
  123. UNIT_ASSERT_EQUAL(list.GetSize(), 1);
  124. }
  125. Y_UNIT_TEST(SimpleTest) {
  126. typedef TLRUCache<int, TString> TCache;
  127. TCache s(2); // size 2
  128. s.Insert(1, "abcd");
  129. UNIT_ASSERT_EQUAL(s.TotalSize(), 1);
  130. UNIT_ASSERT_EQUAL(s.Size(), 1);
  131. UNIT_ASSERT(s.Find(1) != s.End());
  132. UNIT_ASSERT_EQUAL(*s.Find(1), "abcd");
  133. s.Insert(2, "defg");
  134. UNIT_ASSERT_EQUAL(s.TotalSize(), 2);
  135. UNIT_ASSERT_EQUAL(s.Size(), 2);
  136. UNIT_ASSERT(s.GetOldest() == "abcd");
  137. s.Insert(3, "hjkl");
  138. UNIT_ASSERT_EQUAL(s.TotalSize(), 2);
  139. UNIT_ASSERT_EQUAL(s.Size(), 2);
  140. UNIT_ASSERT(s.GetOldest() == "defg");
  141. // key 1 will be deleted
  142. UNIT_ASSERT(s.Find(1) == s.End());
  143. UNIT_ASSERT(s.Find(2) != s.End());
  144. UNIT_ASSERT(*s.Find(2) == "defg");
  145. UNIT_ASSERT(s.Find(3) != s.End());
  146. UNIT_ASSERT(*s.Find(3) == "hjkl");
  147. UNIT_ASSERT(!s.Insert(3, "abcd"));
  148. UNIT_ASSERT(*s.Find(3) == "hjkl");
  149. s.Update(3, "abcd");
  150. UNIT_ASSERT(*s.Find(3) == "abcd");
  151. UNIT_ASSERT_EQUAL(s.TotalSize(), 2);
  152. UNIT_ASSERT_EQUAL(s.Size(), 2);
  153. TCache::TIterator it = s.Find(3);
  154. s.Erase(it);
  155. UNIT_ASSERT_EQUAL(s.TotalSize(), 1);
  156. UNIT_ASSERT_EQUAL(s.Size(), 1);
  157. UNIT_ASSERT(s.Find(3) == s.End());
  158. }
  159. Y_UNIT_TEST(LRUWithCustomSizeProviderTest) {
  160. typedef TLRUCache<int, TString, TNoopDelete, size_t(*)(const TString&)> TCache;
  161. TCache s(10, false, [](auto& string) { return string.size(); }); // size 10
  162. s.Insert(1, "abcd");
  163. UNIT_ASSERT_EQUAL(s.TotalSize(), 4);
  164. UNIT_ASSERT_EQUAL(s.Size(), 1);
  165. UNIT_ASSERT(s.Find(1) != s.End());
  166. UNIT_ASSERT_EQUAL(*s.Find(1), "abcd");
  167. s.Insert(2, "defg");
  168. UNIT_ASSERT_EQUAL(s.TotalSize(), 8);
  169. UNIT_ASSERT_EQUAL(s.Size(), 2);
  170. UNIT_ASSERT(s.GetOldest() == "abcd");
  171. s.Insert(3, "2c");
  172. UNIT_ASSERT_EQUAL(s.TotalSize(), 10);
  173. UNIT_ASSERT_EQUAL(s.Size(), 3);
  174. UNIT_ASSERT(s.GetOldest() == "abcd");
  175. s.Insert(4, "hjkl");
  176. UNIT_ASSERT_EQUAL(s.TotalSize(), 10);
  177. UNIT_ASSERT_EQUAL(s.Size(), 3);
  178. UNIT_ASSERT(s.GetOldest() == "defg");
  179. // key 1 will be deleted
  180. UNIT_ASSERT(s.Find(1) == s.End());
  181. UNIT_ASSERT(s.Find(2) != s.End());
  182. UNIT_ASSERT(*s.Find(2) == "defg");
  183. UNIT_ASSERT(s.Find(3) != s.End());
  184. UNIT_ASSERT(*s.Find(3) == "2c");
  185. UNIT_ASSERT(s.Find(4) != s.End());
  186. UNIT_ASSERT(*s.Find(4) == "hjkl");
  187. UNIT_ASSERT(!s.Insert(3, "abcd"));
  188. UNIT_ASSERT(*s.Find(3) == "2c");
  189. s.Update(3, "abcd");
  190. UNIT_ASSERT_EQUAL(s.TotalSize(), 8);
  191. UNIT_ASSERT_EQUAL(s.Size(), 2);
  192. UNIT_ASSERT(*s.Find(3) == "abcd");
  193. TCache::TIterator it = s.Find(3);
  194. s.Erase(it);
  195. UNIT_ASSERT_EQUAL(s.TotalSize(), 4);
  196. UNIT_ASSERT_EQUAL(s.Size(), 1);
  197. UNIT_ASSERT(s.Find(3) == s.End());
  198. }
  199. Y_UNIT_TEST(LRUSetMaxSizeTest) {
  200. typedef TLRUCache<int, TString> TCache;
  201. TCache s(2); // size 2
  202. s.Insert(1, "abcd");
  203. s.Insert(2, "efgh");
  204. s.Insert(3, "ijkl");
  205. UNIT_ASSERT(s.GetOldest() == "efgh");
  206. UNIT_ASSERT(s.Find(1) == s.End());
  207. UNIT_ASSERT(s.Find(2) != s.End());
  208. UNIT_ASSERT(s.Find(3) != s.End());
  209. // Increasing size should not change anything
  210. s.SetMaxSize(3);
  211. UNIT_ASSERT(s.GetOldest() == "efgh");
  212. UNIT_ASSERT(s.Find(1) == s.End());
  213. UNIT_ASSERT(s.Find(2) != s.End());
  214. UNIT_ASSERT(s.Find(3) != s.End());
  215. // And we should be able to add fit more entries
  216. s.Insert(4, "mnop");
  217. s.Insert(5, "qrst");
  218. UNIT_ASSERT(s.GetOldest() == "ijkl");
  219. UNIT_ASSERT(s.Find(1) == s.End());
  220. UNIT_ASSERT(s.Find(2) == s.End());
  221. UNIT_ASSERT(s.Find(3) != s.End());
  222. UNIT_ASSERT(s.Find(4) != s.End());
  223. UNIT_ASSERT(s.Find(5) != s.End());
  224. // Decreasing size should remove oldest entries
  225. s.SetMaxSize(2);
  226. UNIT_ASSERT(s.GetOldest() == "mnop");
  227. UNIT_ASSERT(s.Find(1) == s.End());
  228. UNIT_ASSERT(s.Find(2) == s.End());
  229. UNIT_ASSERT(s.Find(3) == s.End());
  230. UNIT_ASSERT(s.Find(4) != s.End());
  231. UNIT_ASSERT(s.Find(5) != s.End());
  232. // Ano no more entries will fit
  233. s.Insert(6, "uvwx");
  234. UNIT_ASSERT(s.GetOldest() == "qrst");
  235. UNIT_ASSERT(s.Find(1) == s.End());
  236. UNIT_ASSERT(s.Find(2) == s.End());
  237. UNIT_ASSERT(s.Find(3) == s.End());
  238. UNIT_ASSERT(s.Find(4) == s.End());
  239. UNIT_ASSERT(s.Find(5) != s.End());
  240. UNIT_ASSERT(s.Find(6) != s.End());
  241. }
  242. Y_UNIT_TEST(LWSetMaxSizeTest) {
  243. typedef TLWCache<int, TString, size_t, TStrokaWeighter> TCache;
  244. TCache s(2); // size 2
  245. s.Insert(1, "a");
  246. s.Insert(2, "aa");
  247. s.Insert(3, "aaa");
  248. UNIT_ASSERT(s.GetLightest() == "aa");
  249. UNIT_ASSERT(s.Find(1) == s.End());
  250. UNIT_ASSERT(s.Find(2) != s.End());
  251. UNIT_ASSERT(s.Find(3) != s.End());
  252. // Increasing size should not change anything
  253. s.SetMaxSize(3);
  254. UNIT_ASSERT(s.GetLightest() == "aa");
  255. UNIT_ASSERT(s.Find(1) == s.End());
  256. UNIT_ASSERT(s.Find(2) != s.End());
  257. UNIT_ASSERT(s.Find(3) != s.End());
  258. // And we should be able to add fit more entries
  259. s.Insert(4, "aaaa");
  260. s.Insert(5, "aaaaa");
  261. UNIT_ASSERT(s.GetLightest() == "aaa");
  262. UNIT_ASSERT(s.Find(1) == s.End());
  263. UNIT_ASSERT(s.Find(2) == s.End());
  264. UNIT_ASSERT(s.Find(3) != s.End());
  265. UNIT_ASSERT(s.Find(4) != s.End());
  266. UNIT_ASSERT(s.Find(5) != s.End());
  267. // Decreasing size should remove oldest entries
  268. s.SetMaxSize(2);
  269. UNIT_ASSERT(s.GetLightest() == "aaaa");
  270. UNIT_ASSERT(s.Find(1) == s.End());
  271. UNIT_ASSERT(s.Find(2) == s.End());
  272. UNIT_ASSERT(s.Find(3) == s.End());
  273. UNIT_ASSERT(s.Find(4) != s.End());
  274. UNIT_ASSERT(s.Find(5) != s.End());
  275. // Ano no more entries will fit
  276. s.Insert(6, "aaaaaa");
  277. UNIT_ASSERT(s.GetLightest() == "aaaaa");
  278. UNIT_ASSERT(s.Find(1) == s.End());
  279. UNIT_ASSERT(s.Find(2) == s.End());
  280. UNIT_ASSERT(s.Find(3) == s.End());
  281. UNIT_ASSERT(s.Find(4) == s.End());
  282. UNIT_ASSERT(s.Find(5) != s.End());
  283. UNIT_ASSERT(s.Find(6) != s.End());
  284. }
  285. Y_UNIT_TEST(LFUSetMaxSizeTest) {
  286. typedef TLFUCache<int, TString> TCache;
  287. TCache s(2); // size 2
  288. s.Insert(1, "abcd");
  289. s.Insert(2, "efgh");
  290. s.Insert(3, "ijkl");
  291. UNIT_ASSERT(s.Find(1) == s.End());
  292. UNIT_ASSERT(s.Find(2) != s.End());
  293. UNIT_ASSERT(s.Find(3) != s.End());
  294. // Increasing size should not change anything
  295. s.SetMaxSize(3);
  296. UNIT_ASSERT(s.Find(1) == s.End());
  297. UNIT_ASSERT(s.Find(2) != s.End());
  298. UNIT_ASSERT(s.Find(3) != s.End());
  299. // And we should be able to add fit more entries
  300. s.Insert(4, "mnop");
  301. s.Insert(5, "qrst");
  302. UNIT_ASSERT(s.Find(1) == s.End());
  303. UNIT_ASSERT(s.Find(2) == s.End());
  304. UNIT_ASSERT(s.Find(3) != s.End());
  305. UNIT_ASSERT(s.Find(4) != s.End());
  306. UNIT_ASSERT(s.Find(5) != s.End());
  307. // Decreasing size should remove oldest entries
  308. s.SetMaxSize(2);
  309. UNIT_ASSERT(s.Find(1) == s.End());
  310. UNIT_ASSERT(s.Find(2) == s.End());
  311. UNIT_ASSERT(s.Find(3) != s.End());
  312. UNIT_ASSERT(s.Find(4) == s.End());
  313. UNIT_ASSERT(s.Find(5) != s.End());
  314. // Ano no more entries will fit
  315. s.Insert(6, "uvwx");
  316. UNIT_ASSERT(s.Find(1) == s.End());
  317. UNIT_ASSERT(s.Find(2) == s.End());
  318. UNIT_ASSERT(s.Find(3) != s.End());
  319. UNIT_ASSERT(s.Find(4) == s.End());
  320. UNIT_ASSERT(s.Find(5) == s.End());
  321. UNIT_ASSERT(s.Find(6) != s.End());
  322. }
  323. Y_UNIT_TEST(MultiCacheTest) {
  324. typedef TLRUCache<int, TString> TCache;
  325. TCache s(3, true);
  326. UNIT_ASSERT(s.Insert(1, "abcd"));
  327. UNIT_ASSERT(s.Insert(1, "bcde"));
  328. UNIT_ASSERT(s.Insert(2, "fghi"));
  329. // (1, "abcd") will be deleted
  330. UNIT_ASSERT(s.Insert(2, "ghij"));
  331. UNIT_ASSERT_EQUAL(s.TotalSize(), 3);
  332. UNIT_ASSERT_EQUAL(s.Size(), 3);
  333. // (1, "bcde") will be promoted
  334. UNIT_ASSERT(*s.Find(1) == "bcde");
  335. UNIT_ASSERT(*s.FindOldest() == "fghi");
  336. }
  337. struct TMyDelete {
  338. static int count;
  339. template <typename T>
  340. static void Destroy(const T&) {
  341. ++count;
  342. }
  343. };
  344. int TMyDelete::count = 0;
  345. Y_UNIT_TEST(DeleterTest) {
  346. typedef TLRUCache<int, TString, TMyDelete> TCache;
  347. TCache s(2);
  348. s.Insert(1, "123");
  349. s.Insert(2, "456");
  350. s.Insert(3, "789");
  351. UNIT_ASSERT(TMyDelete::count == 1);
  352. TCache::TIterator it = s.Find(2);
  353. UNIT_ASSERT(it != s.End());
  354. s.Erase(it);
  355. UNIT_ASSERT(TMyDelete::count == 2);
  356. }
  357. Y_UNIT_TEST(PromoteOnFind) {
  358. typedef TLRUCache<int, TString> TCache;
  359. TCache s(2);
  360. s.Insert(1, "123");
  361. s.Insert(2, "456");
  362. UNIT_ASSERT(s.Find(1) != s.End());
  363. s.Insert(3, "789");
  364. UNIT_ASSERT(s.Find(1) != s.End()); // Key 2 should have been deleted
  365. }
  366. class TMoveOnlyInt {
  367. public:
  368. ui32 Value = 0;
  369. explicit TMoveOnlyInt(ui32 value = 0) : Value(value) {}
  370. TMoveOnlyInt(TMoveOnlyInt&&) = default;
  371. TMoveOnlyInt& operator=(TMoveOnlyInt&&) = default;
  372. TMoveOnlyInt(const TMoveOnlyInt&) = delete;
  373. TMoveOnlyInt& operator=(const TMoveOnlyInt&) = delete;
  374. bool operator==(const TMoveOnlyInt& rhs) const {
  375. return Value == rhs.Value;
  376. }
  377. explicit operator size_t() const {
  378. return Value;
  379. }
  380. };
  381. Y_UNIT_TEST(MoveOnlySimpleTest) {
  382. typedef TLRUCache<TMoveOnlyInt, TMoveOnlyInt> TCache;
  383. TCache s(2); // size 2
  384. s.Insert(TMoveOnlyInt(1), TMoveOnlyInt(0x11111111));
  385. TMoveOnlyInt lookup1(1), lookup2(2), lookup3(3);
  386. UNIT_ASSERT(s.Find(lookup1) != s.End());
  387. UNIT_ASSERT_EQUAL(s.Find(lookup1)->Value, 0x11111111);
  388. s.Insert(TMoveOnlyInt(2), TMoveOnlyInt(0x22222222));
  389. UNIT_ASSERT(s.GetOldest().Value == 0x11111111);
  390. s.Insert(TMoveOnlyInt(3), TMoveOnlyInt(0x33333333));
  391. UNIT_ASSERT(s.GetOldest().Value == 0x22222222);
  392. // key 1 will be deleted
  393. UNIT_ASSERT(s.Find(lookup1) == s.End());
  394. UNIT_ASSERT(s.Find(lookup2) != s.End());
  395. UNIT_ASSERT(s.Find(lookup2)->Value == 0x22222222);
  396. UNIT_ASSERT(s.Find(lookup3) != s.End());
  397. UNIT_ASSERT(s.Find(lookup3)->Value == 0x33333333);
  398. UNIT_ASSERT(!s.Insert(TMoveOnlyInt(3), TMoveOnlyInt(0x11111111)));
  399. UNIT_ASSERT(s.Find(lookup3)->Value == 0x33333333);
  400. s.Update(TMoveOnlyInt(3), TMoveOnlyInt(0x11111111));
  401. UNIT_ASSERT(s.Find(lookup3)->Value == 0x11111111);
  402. TCache::TIterator it = s.Find(lookup3);
  403. s.Erase(it);
  404. UNIT_ASSERT(s.Find(lookup3) == s.End());
  405. }
  406. }
  407. Y_UNIT_TEST_SUITE(TThreadSafeCacheTest) {
  408. typedef TThreadSafeCache<ui32, TString, ui32> TCache;
  409. const char* VALS[] = {"abcd", "defg", "hjkl"};
  410. class TCallbacks: public TCache::ICallbacks {
  411. public:
  412. TKey GetKey(ui32 i) const override {
  413. return i;
  414. }
  415. TValue* CreateObject(ui32 i) const override {
  416. Creations++;
  417. return new TString(VALS[i]);
  418. }
  419. mutable i32 Creations = 0;
  420. };
  421. Y_UNIT_TEST(SimpleTest) {
  422. for (ui32 i = 0; i < Y_ARRAY_SIZE(VALS); ++i) {
  423. const TString data = *TCache::Get<TCallbacks>(i);
  424. UNIT_ASSERT(data == VALS[i]);
  425. }
  426. }
  427. Y_UNIT_TEST(InsertUpdateTest) {
  428. TCallbacks callbacks;
  429. TCache cache(callbacks, 10);
  430. cache.Insert(2, MakeAtomicShared<TString>("hj"));
  431. TAtomicSharedPtr<TString> item = cache.Get(2);
  432. UNIT_ASSERT(callbacks.Creations == 0);
  433. UNIT_ASSERT(*item == "hj");
  434. cache.Insert(2, MakeAtomicShared<TString>("hjk"));
  435. item = cache.Get(2);
  436. UNIT_ASSERT(callbacks.Creations == 0);
  437. UNIT_ASSERT(*item == "hj");
  438. cache.Update(2, MakeAtomicShared<TString>("hjk"));
  439. item = cache.Get(2);
  440. UNIT_ASSERT_EQUAL(cache.TotalSize(), 1);
  441. UNIT_ASSERT_EQUAL(cache.Size(), 1);
  442. UNIT_ASSERT(callbacks.Creations == 0);
  443. UNIT_ASSERT(*item == "hjk");
  444. }
  445. Y_UNIT_TEST(GetOrNullTest) {
  446. TCallbacks callbacks;
  447. TCache cache(callbacks, 10);
  448. i32 expectedCreations = 0;
  449. auto item = cache.GetOrNull(0);
  450. UNIT_ASSERT(item == nullptr);
  451. UNIT_ASSERT(callbacks.Creations == expectedCreations);
  452. UNIT_ASSERT(cache.TotalSize() == 0);
  453. item = cache.Get(0);
  454. UNIT_ASSERT(*item == "abcd");
  455. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  456. UNIT_ASSERT(cache.TotalSize() == 1);
  457. item = cache.GetOrNull(0);
  458. UNIT_ASSERT(*item == "abcd");
  459. UNIT_ASSERT(callbacks.Creations == expectedCreations);
  460. UNIT_ASSERT(cache.TotalSize() == 1);
  461. }
  462. }
  463. Y_UNIT_TEST_SUITE(TThreadSafeCacheUnsafeTest) {
  464. typedef TThreadSafeCache<ui32, TString, ui32> TCache;
  465. const char* VALS[] = {"abcd", "defg", "hjkl"};
  466. const ui32 FAILED_IDX = 1;
  467. class TCallbacks: public TCache::ICallbacks {
  468. public:
  469. TKey GetKey(ui32 i) const override {
  470. return i;
  471. }
  472. TValue* CreateObject(ui32 i) const override {
  473. if (i == FAILED_IDX) {
  474. return nullptr;
  475. }
  476. return new TString(VALS[i]);
  477. }
  478. };
  479. Y_UNIT_TEST(SimpleTest) {
  480. TCallbacks callbacks;
  481. TCache cache(callbacks, Y_ARRAY_SIZE(VALS));
  482. for (ui32 i = 0; i < Y_ARRAY_SIZE(VALS); ++i) {
  483. const TString* data = cache.GetUnsafe(i).Get();
  484. if (i == FAILED_IDX) {
  485. UNIT_ASSERT(data == nullptr);
  486. } else {
  487. UNIT_ASSERT(*data == VALS[i]);
  488. }
  489. }
  490. UNIT_ASSERT_EQUAL(cache.TotalSize(), Y_ARRAY_SIZE(VALS) - 1);
  491. UNIT_ASSERT_EQUAL(cache.Size(), Y_ARRAY_SIZE(VALS) - 1);
  492. }
  493. }
  494. Y_UNIT_TEST_SUITE(TThreadSafeLRUCacheTest) {
  495. typedef TThreadSafeLRUCache<size_t, TString, size_t> TCache;
  496. TVector<TString> Values = {"zero", "one", "two", "three", "four"};
  497. class TCallbacks: public TCache::ICallbacks {
  498. public:
  499. TKey GetKey(size_t i) const override {
  500. return i;
  501. }
  502. TValue* CreateObject(size_t i) const override {
  503. UNIT_ASSERT(i < Values.size());
  504. Creations++;
  505. return new TString(Values[i]);
  506. }
  507. mutable size_t Creations = 0;
  508. };
  509. Y_UNIT_TEST(SimpleTest) {
  510. for (size_t i = 0; i < Values.size(); ++i) {
  511. const TString data = *TCache::Get<TCallbacks>(i);
  512. UNIT_ASSERT(data == Values[i]);
  513. }
  514. }
  515. Y_UNIT_TEST(InsertUpdateTest) {
  516. TCallbacks callbacks;
  517. TCache cache(callbacks, 10);
  518. cache.Insert(2, MakeAtomicShared<TString>("hj"));
  519. TAtomicSharedPtr<TString> item = cache.Get(2);
  520. UNIT_ASSERT(callbacks.Creations == 0);
  521. UNIT_ASSERT(*item == "hj");
  522. cache.Insert(2, MakeAtomicShared<TString>("hjk"));
  523. item = cache.Get(2);
  524. UNIT_ASSERT(callbacks.Creations == 0);
  525. UNIT_ASSERT(*item == "hj");
  526. cache.Update(2, MakeAtomicShared<TString>("hjk"));
  527. item = cache.Get(2);
  528. UNIT_ASSERT_EQUAL(cache.TotalSize(), 1);
  529. UNIT_ASSERT_EQUAL(cache.Size(), 1);
  530. UNIT_ASSERT(callbacks.Creations == 0);
  531. UNIT_ASSERT(*item == "hjk");
  532. }
  533. Y_UNIT_TEST(LRUTest) {
  534. TCallbacks callbacks;
  535. TCache cache(callbacks, 3);
  536. UNIT_ASSERT_EQUAL(cache.GetMaxSize(), 3);
  537. for (size_t i = 0; i < Values.size(); ++i) {
  538. TAtomicSharedPtr<TString> item = cache.Get(i);
  539. UNIT_ASSERT(*item == Values[i]);
  540. }
  541. UNIT_ASSERT(callbacks.Creations == Values.size());
  542. size_t expectedCreations = Values.size();
  543. TAtomicSharedPtr<TString> item;
  544. item = cache.Get(4);
  545. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  546. UNIT_ASSERT(*item == "four");
  547. item = cache.Get(2);
  548. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  549. UNIT_ASSERT(*item == "two");
  550. item = cache.Get(0);
  551. expectedCreations++;
  552. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  553. UNIT_ASSERT(*item == "zero");
  554. UNIT_ASSERT(cache.Contains(1) == false);
  555. UNIT_ASSERT(cache.Contains(3) == false);
  556. UNIT_ASSERT(cache.Contains(4));
  557. UNIT_ASSERT(cache.Contains(2));
  558. UNIT_ASSERT(cache.Contains(0));
  559. item = cache.Get(3);
  560. expectedCreations++;
  561. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  562. UNIT_ASSERT(*item == "three");
  563. item = cache.Get(2);
  564. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  565. UNIT_ASSERT(*item == "two");
  566. item = cache.Get(0);
  567. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  568. UNIT_ASSERT(*item == "zero");
  569. item = cache.Get(1);
  570. expectedCreations++;
  571. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  572. UNIT_ASSERT(*item == "one");
  573. item = cache.Get(2);
  574. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  575. UNIT_ASSERT(*item == "two");
  576. item = cache.Get(4);
  577. expectedCreations++;
  578. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  579. UNIT_ASSERT(*item == "four");
  580. }
  581. Y_UNIT_TEST(ChangeMaxSizeTest) {
  582. TCallbacks callbacks;
  583. TCache cache(callbacks, 3);
  584. UNIT_ASSERT_EQUAL(cache.GetMaxSize(), 3);
  585. for (size_t i = 0; i < Values.size(); ++i) {
  586. TAtomicSharedPtr<TString> item = cache.Get(i);
  587. UNIT_ASSERT(*item == Values[i]);
  588. }
  589. size_t expectedCreations = Values.size();
  590. TAtomicSharedPtr<TString> item;
  591. item = cache.Get(4);
  592. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  593. UNIT_ASSERT(*item == "four");
  594. item = cache.Get(3);
  595. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  596. UNIT_ASSERT(*item == "three");
  597. item = cache.Get(2);
  598. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  599. UNIT_ASSERT(*item == "two");
  600. item = cache.Get(1);
  601. expectedCreations++;
  602. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  603. UNIT_ASSERT(*item == "one");
  604. UNIT_ASSERT_EQUAL(cache.TotalSize(), 3);
  605. UNIT_ASSERT_EQUAL(cache.Size(), 3);
  606. cache.SetMaxSize(4);
  607. item = cache.Get(0);
  608. expectedCreations++;
  609. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  610. UNIT_ASSERT(*item == "zero");
  611. UNIT_ASSERT_EQUAL(cache.TotalSize(), 4);
  612. UNIT_ASSERT_EQUAL(cache.Size(), 4);
  613. item = cache.Get(4);
  614. expectedCreations++;
  615. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  616. UNIT_ASSERT(*item == "four");
  617. UNIT_ASSERT_EQUAL(cache.TotalSize(), 4);
  618. UNIT_ASSERT_EQUAL(cache.Size(), 4);
  619. item = cache.Get(3);
  620. expectedCreations++;
  621. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  622. UNIT_ASSERT(*item == "three");
  623. UNIT_ASSERT(cache.Contains(2) == false);
  624. cache.SetMaxSize(2);
  625. UNIT_ASSERT(cache.Contains(3));
  626. UNIT_ASSERT(cache.Contains(4));
  627. UNIT_ASSERT(cache.Contains(2) == false);
  628. UNIT_ASSERT(cache.Contains(1) == false);
  629. UNIT_ASSERT(cache.Contains(0) == false);
  630. item = cache.Get(0);
  631. expectedCreations++;
  632. UNIT_ASSERT_EQUAL(callbacks.Creations, expectedCreations);
  633. UNIT_ASSERT(*item == "zero");
  634. UNIT_ASSERT(cache.Contains(4) == false);
  635. UNIT_ASSERT(cache.Contains(3));
  636. UNIT_ASSERT(cache.Contains(0));
  637. }
  638. }
  639. Y_UNIT_TEST_SUITE(TThreadSafeLFUCacheTest) {
  640. using TCache = TThreadSafeLFUCache<size_t, TString, size_t>;
  641. TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"};
  642. class TCallbacks: public TCache::ICallbacks {
  643. public:
  644. TKey GetKey(size_t i) const override {
  645. return i;
  646. }
  647. TValue* CreateObject(size_t i) const override {
  648. UNIT_ASSERT(i < Values.size());
  649. ++Creations;
  650. return new TString(Values[i]);
  651. }
  652. mutable size_t Creations = 0;
  653. };
  654. Y_UNIT_TEST(SimpleTest) {
  655. for (size_t i = 0; i < Values.size(); ++i) {
  656. const TString data = *TCache::Get<TCallbacks>(i);
  657. UNIT_ASSERT_EQUAL(data, Values[i]);
  658. }
  659. }
  660. Y_UNIT_TEST(InsertUpdateTest) {
  661. TCallbacks callbacks;
  662. TCache cache(callbacks, 10);
  663. cache.Insert(2, MakeAtomicShared<TString>("hj"));
  664. TAtomicSharedPtr<TString> item = cache.Get(2);
  665. UNIT_ASSERT(callbacks.Creations == 0);
  666. UNIT_ASSERT(*item == "hj");
  667. cache.Insert(2, MakeAtomicShared<TString>("hjk"));
  668. item = cache.Get(2);
  669. UNIT_ASSERT(callbacks.Creations == 0);
  670. UNIT_ASSERT(*item == "hj");
  671. cache.Update(2, MakeAtomicShared<TString>("hjk"));
  672. item = cache.Get(2);
  673. UNIT_ASSERT_EQUAL(cache.TotalSize(), 1);
  674. UNIT_ASSERT_EQUAL(cache.Size(), 1);
  675. UNIT_ASSERT(callbacks.Creations == 0);
  676. UNIT_ASSERT(*item == "hjk");
  677. }
  678. Y_UNIT_TEST(LFUTest) {
  679. TCallbacks callbacks;
  680. TCache cache(callbacks, 3);
  681. size_t expectedCreations = 0;
  682. UNIT_ASSERT_EQUAL(cache.GetMaxSize(), 3);
  683. auto item = cache.Get(0);
  684. UNIT_ASSERT(*item == "a");
  685. UNIT_ASSERT(cache.TotalSize() == 1);
  686. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  687. item = cache.Get(1);
  688. UNIT_ASSERT(*item == "bb");
  689. UNIT_ASSERT(cache.TotalSize() == 2);
  690. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  691. item = cache.Get(2);
  692. UNIT_ASSERT(*item == "ccc");
  693. UNIT_ASSERT(cache.TotalSize() == 3);
  694. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  695. cache.Get(0);
  696. cache.Get(0);
  697. cache.Get(0);
  698. cache.Get(1);
  699. cache.Get(2);
  700. cache.Get(2);
  701. // evict 1
  702. item = cache.Get(3);
  703. UNIT_ASSERT(*item == "dddd");
  704. UNIT_ASSERT(cache.TotalSize() == 3);
  705. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  706. // check that 0 was evicted and left only 1 2 3
  707. item = cache.Get(0);
  708. UNIT_ASSERT(*item == "a");
  709. item = cache.Get(2);
  710. UNIT_ASSERT(*item == "ccc");
  711. item = cache.Get(3);
  712. UNIT_ASSERT(*item == "dddd");
  713. UNIT_ASSERT(callbacks.Creations == expectedCreations);
  714. cache.Get(0);
  715. cache.Get(2);
  716. cache.Get(3);
  717. // evict 3
  718. item = cache.Get(4);
  719. UNIT_ASSERT(*item == "eeeee");
  720. UNIT_ASSERT(cache.TotalSize() == 3);
  721. UNIT_ASSERT(callbacks.Creations == ++expectedCreations);
  722. // check that 1 was evicted and left only 2 3 4
  723. item = cache.Get(0);
  724. UNIT_ASSERT(*item == "a");
  725. item = cache.Get(2);
  726. UNIT_ASSERT(*item == "ccc");
  727. item = cache.Get(4);
  728. UNIT_ASSERT(*item == "eeeee");
  729. UNIT_ASSERT(callbacks.Creations == expectedCreations);
  730. }
  731. }
  732. Y_UNIT_TEST_SUITE(TThreadSafeLRUCacheWithSizeProviderTest) {
  733. struct TStringLengthSizeProvider {
  734. size_t operator()(const TString& s) const {
  735. return s.size();
  736. }
  737. };
  738. using TCache = TThreadSafeLRUCacheWithSizeProvider<size_t, TString, TStringLengthSizeProvider, size_t>;
  739. TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"};
  740. class TCallbacks: public TCache::ICallbacks {
  741. public:
  742. TKey GetKey(size_t i) const override {
  743. return i;
  744. }
  745. TValue* CreateObject(size_t i) const override {
  746. UNIT_ASSERT(i < Values.size());
  747. return new TString(Values[i]);
  748. }
  749. };
  750. Y_UNIT_TEST(Test) {
  751. TCallbacks callbacks;
  752. TCache cache(callbacks, 6);
  753. auto item = cache.Get(0);
  754. UNIT_ASSERT(*item == "a");
  755. UNIT_ASSERT(cache.TotalSize() == 1);
  756. UNIT_ASSERT(cache.Size() == 1);
  757. item = cache.Get(1);
  758. UNIT_ASSERT(*item == "bb");
  759. UNIT_ASSERT(cache.TotalSize() == 3);
  760. UNIT_ASSERT(cache.Size() == 2);
  761. item = cache.Get(2);
  762. UNIT_ASSERT(*item == "ccc");
  763. UNIT_ASSERT(cache.TotalSize() == 6);
  764. UNIT_ASSERT(cache.Size() == 3);
  765. item = cache.Get(3);
  766. UNIT_ASSERT(*item == "dddd");
  767. UNIT_ASSERT(cache.TotalSize() == 4);
  768. UNIT_ASSERT(cache.Size() == 1);
  769. item = cache.Get(0);
  770. UNIT_ASSERT(*item == "a");
  771. UNIT_ASSERT(cache.TotalSize() == 5);
  772. UNIT_ASSERT(cache.Size() == 2);
  773. item = cache.Get(4);
  774. UNIT_ASSERT(*item == "eeeee");
  775. UNIT_ASSERT(cache.TotalSize() == 6);
  776. UNIT_ASSERT(cache.Size() == 2);
  777. cache.Update(0, MakeAtomicShared<TString>("aaa"));
  778. item = cache.Get(0);
  779. UNIT_ASSERT(*item == "aaa");
  780. UNIT_ASSERT(cache.TotalSize() == 3);
  781. UNIT_ASSERT(cache.Size() == 1);
  782. }
  783. }
  784. Y_UNIT_TEST_SUITE(TThreadSafeLFUCacheWithSizeProviderTest) {
  785. struct TStringLengthSizeProvider {
  786. size_t operator()(const TString& s) const {
  787. return s.size();
  788. }
  789. };
  790. using TCache = TThreadSafeLFUCacheWithSizeProvider<size_t, TString, TStringLengthSizeProvider, size_t>;
  791. TVector<TString> Values = {"a", "bb", "ccc", "dddd", "eeeee"};
  792. class TCallbacks: public TCache::ICallbacks {
  793. public:
  794. TKey GetKey(size_t i) const override {
  795. return i;
  796. }
  797. TValue* CreateObject(size_t i) const override {
  798. UNIT_ASSERT(i < Values.size());
  799. return new TString(Values[i]);
  800. }
  801. };
  802. Y_UNIT_TEST(Test) {
  803. TCallbacks callbacks;
  804. TCache cache(callbacks, 6);
  805. auto item = cache.Get(0);
  806. UNIT_ASSERT(*item == "a");
  807. UNIT_ASSERT(cache.TotalSize() == 1);
  808. UNIT_ASSERT(cache.Size() == 1);
  809. item = cache.Get(1);
  810. UNIT_ASSERT(*item == "bb");
  811. UNIT_ASSERT(cache.TotalSize() == 3);
  812. UNIT_ASSERT(cache.Size() == 2);
  813. item = cache.Get(2);
  814. UNIT_ASSERT(*item == "ccc");
  815. UNIT_ASSERT(cache.TotalSize() == 6);
  816. UNIT_ASSERT(cache.Size() == 3);
  817. cache.Get(0);
  818. cache.Get(0);
  819. cache.Get(0);
  820. cache.Get(1);
  821. cache.Get(2);
  822. cache.Get(2);
  823. // evict 1. 0 and 3 left
  824. item = cache.Get(3);
  825. UNIT_ASSERT(*item == "dddd");
  826. UNIT_ASSERT(cache.TotalSize() == 5);
  827. UNIT_ASSERT(cache.Size() == 2);
  828. cache.Get(0);
  829. UNIT_ASSERT(cache.TotalSize() == 5);
  830. UNIT_ASSERT(cache.Size() == 2);
  831. // evict 3. 0 and 4 left
  832. cache.Get(4);
  833. UNIT_ASSERT(cache.TotalSize() == 6);
  834. UNIT_ASSERT(cache.Size() == 2);
  835. cache.Get(4);
  836. cache.Get(4);
  837. cache.Get(4);
  838. cache.Get(4);
  839. cache.Get(4);
  840. // evict both 0 and 4, even if evict only 4 was ok to fit size
  841. // thats because 4 used more times, so it is deleted only after 0
  842. cache.Get(2);
  843. UNIT_ASSERT(cache.TotalSize() == 3);
  844. UNIT_ASSERT(cache.Size() == 1);
  845. }
  846. }