@@ -1094,6 +1094,113 @@ TEST_P(STItemPagerTest, ItemPagerEvictionOrder) {
10941094 ;
10951095}
10961096
1097+ TEST_P (STItemPagerTest, ItemPagerEvictionOrderIsSafe) {
1098+ // MB-42688: Test that the ordering of the paging visitor comparator is
1099+ // fixed even if the amount of pageable memory or the vbucket state changes.
1100+ // This is required to meet the strict weak ordering requirement of
1101+ // std::sort
1102+
1103+ // 17 is the minimum number of vbs found to demonstrate a segfault
1104+ // with an unsuitable comparator on linux. This is likely impl dependent.
1105+ // 18 gives 6 vbs per active/replica/pending
1106+ resetEngineAndWarmup (" max_vbuckets=18" );
1107+ std::vector<Vbid> allVBs (18 );
1108+
1109+ for (int i = 0 ; i < 18 ; i++) {
1110+ allVBs[i] = Vbid (i);
1111+ }
1112+
1113+
1114+ for (int i = 0 ; i < 6 ; i++) {
1115+ setVBucketStateAndRunPersistTask (Vbid (i), vbucket_state_active);
1116+ setVBucketStateAndRunPersistTask (Vbid (i + 6 ), vbucket_state_pending);
1117+ setVBucketStateAndRunPersistTask (Vbid (i + 12 ), vbucket_state_replica);
1118+ }
1119+
1120+ if (engine->getConfiguration ().getBucketType () == " persistent" ) {
1121+ // flush all vbs
1122+ for (const auto & vbid : allVBs) {
1123+ dynamic_cast <EPBucket&>(*store).flushVBucket (vbid);
1124+ }
1125+ }
1126+
1127+ auto & stats = engine->getEpStats ();
1128+ auto available = std::make_shared<std::atomic<bool >>();
1129+ auto & config = engine->getConfiguration ();
1130+
1131+ auto pv = std::make_unique<MockPagingVisitor>(
1132+ *store,
1133+ stats,
1134+ EvictionRatios{1.0 /* active*/ , 1.0 /* replica*/ },
1135+ available,
1136+ ITEM_PAGER,
1137+ false ,
1138+ VBucketFilter (allVBs),
1139+ config.getItemEvictionAgePercentage (),
1140+ config.getItemEvictionFreqCounterAgeThreshold ());
1141+
1142+ // now test that even with state changes, the comparator sorts the vbuckets
1143+ // acceptably
1144+ auto innerComparator = pv->getVBucketComparator ();
1145+
1146+ // wrap the comparator to allow insertion of state changes mid-sort
1147+ auto comparator = [&innerComparator, this ](const Vbid& a, const Vbid& b) {
1148+ // run the actual comparator
1149+ auto res = innerComparator (a, b);
1150+ // now, to _intentionally_ try to break the strict weak ordering, change
1151+ // the state of one of the vbuckets. If the vbucket comparator is
1152+ // checking the state each time, this can cause a crash in std::sort
1153+ for (const auto & vbid : {a, b}) {
1154+ auto state = store->getVBucket (vbid)->getState ();
1155+ EXPECT_EQ (ENGINE_SUCCESS,
1156+ store->setVBucketState (vbid,
1157+ state == vbucket_state_replica
1158+ ? vbucket_state_active
1159+ : vbucket_state_replica));
1160+ }
1161+
1162+ return res;
1163+ };
1164+
1165+ // if the vbucket comparator is "safe" and checks the state once, this
1166+ // sort will work as expected - otherwise it _may_ segfault.
1167+ // Note: this is not a robust test, variations in the impl of sort
1168+ // may mean this does _not_ crash even with an unacceptable comparator
1169+ std::sort (allVBs.begin (), allVBs.end (), comparator);
1170+
1171+ // as a secondary check, directly test the requirements of a strict
1172+ // weak ordering on the comparator while actively changing
1173+ // the state of vbuckets. This will fail if the comparator checks
1174+ // the bucket state on every call.
1175+
1176+ // irreflexivity
1177+ for (const auto & x : allVBs) {
1178+ EXPECT_FALSE (comparator (x, x));
1179+ }
1180+
1181+ // asymmetry
1182+ for (const auto & x : allVBs) {
1183+ for (const auto & y : allVBs) {
1184+ EXPECT_FALSE (comparator (x, y) && comparator (y, x));
1185+ }
1186+ }
1187+
1188+ // transitivity
1189+ for (const auto & x : allVBs) {
1190+ for (const auto & y : allVBs) {
1191+ for (const auto & z : allVBs) {
1192+ // x < y && y < z => x < z
1193+ // equivalent to
1194+ // !(x < y && y < z) || x < z
1195+ // if x < y and y < z, it must be true that x < z
1196+ EXPECT_TRUE (!(comparator (x, y) && comparator (y, z)) ||
1197+ comparator (x, z));
1198+ }
1199+ }
1200+ }
1201+ }
1202+
1203+
10971204/* *
10981205 * Test fixture for Ephemeral-only item pager tests.
10991206 */
0 commit comments