@@ -44,8 +44,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(Config config)
44
44
[this ](Item* it) -> ItemHandle { return acquire (it); })),
45
45
chainedItemLocks_ (config_.chainedItemsLockPower,
46
46
std::make_shared<MurmurHash2>()),
47
- movesMap_(kShards ),
48
- moveLock_(kShards ),
49
47
cacheCreationTime_{util::getCurrentTimeSec ()} {
50
48
51
49
if (numTiers_ > 1 || std::holds_alternative<FileShmSegmentOpts>(
@@ -132,8 +130,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemNewT, Config config)
132
130
[this](Item* it) -> ItemHandle { return acquire (it); })),
133
131
chainedItemLocks_(config_.chainedItemsLockPower,
134
132
std::make_shared<MurmurHash2>()),
135
- movesMap_(kShards ),
136
- moveLock_(kShards ),
137
133
cacheCreationTime_{util::getCurrentTimeSec ()} {
138
134
initCommon (false );
139
135
shmManager_->removeShm (detail::kShmInfoName ,
@@ -170,8 +166,6 @@ CacheAllocator<CacheTrait>::CacheAllocator(SharedMemAttachT, Config config)
170
166
[this](Item* it) -> ItemHandle { return acquire (it); })),
171
167
chainedItemLocks_(config_.chainedItemsLockPower,
172
168
std::make_shared<MurmurHash2>()),
173
- movesMap_(kShards ),
174
- moveLock_(kShards ),
175
169
cacheCreationTime_{*metadata_.cacheCreationTime_ref ()} {
176
170
/* TODO - per tier? */
177
171
for (auto pid : *metadata_.compactCachePools_ref ()) {
@@ -272,6 +266,14 @@ void CacheAllocator<CacheTrait>::initCommon(bool dramCacheAttached) {
272
266
nvmAdmissionPolicy_->initMinTTL (config_.nvmAdmissionMinTTL );
273
267
}
274
268
}
269
+ if (numTiers_ > 1 && !config_.moveCb ) {
270
+ XLOG (WARN, " No moveCb set, using memcpy for moving items between tiers." );
271
+ config_.moveCb = [](Item& oldItem, Item& newItem, Item* parentItem){
272
+ if (parentItem != nullptr )
273
+ throw std::runtime_error (" TODO: chained items not supported" );
274
+ std::memcpy (newItem.getMemory (), oldItem.getMemory (), oldItem.getSize ());
275
+ };
276
+ }
275
277
initStats ();
276
278
initNvmCache (dramCacheAttached);
277
279
initWorkers ();
@@ -1257,159 +1259,11 @@ CacheAllocator<CacheTrait>::insertOrReplace(const ItemHandle& handle) {
1257
1259
return replaced;
1258
1260
}
1259
1261
1260
- /* Next two methods are used to asynchronously move Item between memory tiers.
1261
- *
1262
- * The thread, which moves Item, allocates new Item in the tier we are moving to
1263
- * and calls moveRegularItemOnEviction() method. This method does the following:
1264
- * 1. Create MoveCtx and put it to the movesMap.
1265
- * 2. Update the access container with the new item from the tier we are
1266
- * moving to. This Item has kIncomplete flag set.
1267
- * 3. Copy data from the old Item to the new one.
1268
- * 4. Unset the kIncomplete flag and Notify MoveCtx
1269
- *
1270
- * Concurrent threads which are getting handle to the same key:
1271
- * 1. When a handle is created it checks if the kIncomplete flag is set
1272
- * 2. If so, Handle implementation creates waitContext and adds it to the
1273
- * MoveCtx by calling addWaitContextForMovingItem() method.
1274
- * 3. Wait until the moving thread will complete its job.
1275
- */
1276
- template <typename CacheTrait>
1277
- bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
1278
- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter) {
1279
- auto shard = getShardForKey (key);
1280
- auto & movesMap = getMoveMapForShard (shard);
1281
- auto lock = getMoveLockForShard (shard);
1282
- auto it = movesMap.find (key);
1283
- if (it == movesMap.end ()) {
1284
- return false ;
1285
- }
1286
- auto ctx = it->second .get ();
1287
- ctx->addWaiter (std::move (waiter));
1288
- return true ;
1289
- }
1290
-
1291
- template <typename CacheTrait>
1292
- typename CacheAllocator<CacheTrait>::ItemHandle
1293
- CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1294
- Item& oldItem, ItemHandle& newItemHdl) {
1295
- XDCHECK (oldItem.isMoving ());
1296
- // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
1297
- // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
1298
-
1299
- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1300
- return {};
1301
- }
1302
-
1303
- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1304
- XDCHECK_NE (getTierId (oldItem), getTierId (*newItemHdl));
1305
-
1306
- // take care of the flags before we expose the item to be accessed. this
1307
- // will ensure that when another thread removes the item from RAM, we issue
1308
- // a delete accordingly. See D7859775 for an example
1309
- if (oldItem.isNvmClean ()) {
1310
- newItemHdl->markNvmClean ();
1311
- }
1312
-
1313
- folly::StringPiece key (oldItem.getKey ());
1314
- auto shard = getShardForKey (key);
1315
- auto & movesMap = getMoveMapForShard (shard);
1316
- MoveCtx* ctx (nullptr );
1317
- {
1318
- auto lock = getMoveLockForShard (shard);
1319
- auto res = movesMap.try_emplace (key, std::make_unique<MoveCtx>());
1320
- if (!res.second ) {
1321
- return {};
1322
- }
1323
- ctx = res.first ->second .get ();
1324
- }
1325
-
1326
- auto resHdl = ItemHandle{};
1327
- auto guard = folly::makeGuard ([key, this , ctx, shard, &resHdl]() {
1328
- auto & movesMap = getMoveMapForShard (shard);
1329
- if (resHdl)
1330
- resHdl->unmarkIncomplete ();
1331
- auto lock = getMoveLockForShard (shard);
1332
- ctx->setItemHandle (std::move (resHdl));
1333
- movesMap.erase (key);
1334
- });
1335
-
1336
- // TODO: Possibly we can use markMoving() instead. But today
1337
- // moveOnSlabRelease logic assume that we mark as moving old Item
1338
- // and than do copy and replace old Item with the new one in access
1339
- // container. Furthermore, Item can be marked as Moving only
1340
- // if it is linked to MM container. In our case we mark the new Item
1341
- // and update access container before the new Item is ready (content is
1342
- // copied).
1343
- newItemHdl->markIncomplete ();
1344
-
1345
- // Inside the access container's lock, this checks if the old item is
1346
- // accessible and its refcount is zero. If the item is not accessible,
1347
- // there is no point to replace it since it had already been removed
1348
- // or in the process of being removed. If the item is in cache but the
1349
- // refcount is non-zero, it means user could be attempting to remove
1350
- // this item through an API such as remove(ItemHandle). In this case,
1351
- // it is unsafe to replace the old item with a new one, so we should
1352
- // also abort.
1353
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1354
- itemMovingPredicate)) {
1355
- return {};
1356
- }
1357
-
1358
- if (config_.moveCb ) {
1359
- // Execute the move callback. We cannot make any guarantees about the
1360
- // consistency of the old item beyond this point, because the callback can
1361
- // do more than a simple memcpy() e.g. update external references. If there
1362
- // are any remaining handles to the old item, it is the caller's
1363
- // responsibility to invalidate them. The move can only fail after this
1364
- // statement if the old item has been removed or replaced, in which case it
1365
- // should be fine for it to be left in an inconsistent state.
1366
- config_.moveCb (oldItem, *newItemHdl, nullptr );
1367
- } else {
1368
- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1369
- oldItem.getSize ());
1370
- }
1371
-
1372
- // Inside the MM container's lock, this checks if the old item exists to
1373
- // make sure that no other thread removed it, and only then replaces it.
1374
- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1375
- accessContainer_->remove (*newItemHdl);
1376
- return {};
1377
- }
1378
-
1379
- // Replacing into the MM container was successful, but someone could have
1380
- // called insertOrReplace() or remove() before or after the
1381
- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1382
- if (!newItemHdl->isAccessible ()) {
1383
- removeFromMMContainer (*newItemHdl);
1384
- return {};
1385
- }
1386
-
1387
- // no one can add or remove chained items at this point
1388
- if (oldItem.hasChainedItem ()) {
1389
- // safe to acquire handle for a moving Item
1390
- auto oldHandle = acquire (&oldItem);
1391
- XDCHECK_EQ (1u , oldHandle->getRefCount ()) << oldHandle->toString ();
1392
- XDCHECK (!newItemHdl->hasChainedItem ()) << newItemHdl->toString ();
1393
- try {
1394
- auto l = chainedItemLocks_.lockExclusive (oldItem.getKey ());
1395
- transferChainLocked (oldHandle, newItemHdl);
1396
- } catch (const std::exception& e) {
1397
- // this should never happen because we drained all the handles.
1398
- XLOGF (DFATAL, " {}" , e.what ());
1399
- throw ;
1400
- }
1401
-
1402
- XDCHECK (!oldItem.hasChainedItem ());
1403
- XDCHECK (newItemHdl->hasChainedItem ());
1404
- }
1405
- newItemHdl.unmarkNascent ();
1406
- resHdl = std::move (newItemHdl); // guard will assign it to ctx under lock
1407
- return acquire (&oldItem);
1408
- }
1409
-
1410
1262
template <typename CacheTrait>
1263
+ template <typename Predicate>
1411
1264
bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1412
- ItemHandle& newItemHdl) {
1265
+ ItemHandle& newItemHdl,
1266
+ Predicate &&predicate) {
1413
1267
XDCHECK (config_.moveCb );
1414
1268
util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1415
1269
@@ -1418,8 +1272,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1418
1272
}
1419
1273
1420
1274
XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1421
- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&getMMContainer (oldItem)),
1422
- reinterpret_cast <uintptr_t >(&getMMContainer (*newItemHdl)));
1423
1275
1424
1276
// take care of the flags before we expose the item to be accessed. this
1425
1277
// will ensure that when another thread removes the item from RAM, we issue
@@ -1445,7 +1297,7 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1445
1297
// this item through an API such as remove(ItemHandle). In this case,
1446
1298
// it is unsafe to replace the old item with a new one, so we should
1447
1299
// also abort.
1448
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, itemMovingPredicate )) {
1300
+ if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate )) {
1449
1301
return false ;
1450
1302
}
1451
1303
@@ -1486,62 +1338,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1486
1338
return true ;
1487
1339
}
1488
1340
1489
- template <typename CacheTrait>
1490
- bool CacheAllocator<CacheTrait>::moveRegularItemForPromotion(Item& oldItem,
1491
- ItemHandle& newItemHdl) {
1492
- util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1493
-
1494
- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1495
- return false ;
1496
- }
1497
-
1498
- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1499
-
1500
- if (config_.moveCb ) {
1501
- // Execute the move callback. We cannot make any guarantees about the
1502
- // consistency of the old item beyond this point, because the callback can
1503
- // do more than a simple memcpy() e.g. update external references. If there
1504
- // are any remaining handles to the old item, it is the caller's
1505
- // responsibility to invalidate them. The move can only fail after this
1506
- // statement if the old item has been removed or replaced, in which case it
1507
- // should be fine for it to be left in an inconsistent state.
1508
- config_.moveCb (oldItem, *newItemHdl, nullptr );
1509
- } else {
1510
- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1511
- oldItem.getSize ());
1512
- }
1513
-
1514
- auto predicate = [this ](const Item& item) {
1515
- // if inclusive cache is allowed, replace even if there are active users.
1516
- return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1517
- };
1518
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate)) {
1519
- return false ;
1520
- }
1521
-
1522
- // Inside the MM container's lock, this checks if the old item exists to
1523
- // make sure that no other thread removed it, and only then replaces it.
1524
- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1525
- accessContainer_->remove (*newItemHdl);
1526
- return false ;
1527
- }
1528
-
1529
- // Replacing into the MM container was successful, but someone could have
1530
- // called insertOrReplace() or remove() before or after the
1531
- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1532
- if (!newItemHdl->isAccessible ()) {
1533
- removeFromMMContainer (*newItemHdl);
1534
- return false ;
1535
- }
1536
-
1537
- // no one can add or remove chained items at this point
1538
- if (oldItem.hasChainedItem ()) {
1539
- throw std::runtime_error (" Not supported" );
1540
- }
1541
- newItemHdl.unmarkNascent ();
1542
- return true ;
1543
- }
1544
-
1545
1341
template <typename CacheTrait>
1546
1342
bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
1547
1343
ItemHandle& newItemHdl) {
@@ -1781,8 +1577,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1781
1577
1782
1578
if (newItemHdl) {
1783
1579
XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1784
-
1785
- return moveRegularItemOnEviction (item, newItemHdl);
1580
+ if (tryMovingForSlabRelease (item, newItemHdl, itemMovingPredicate)) {
1581
+ return acquire (&item);
1582
+ }
1786
1583
}
1787
1584
}
1788
1585
@@ -1808,7 +1605,11 @@ CacheAllocator<CacheTrait>::tryPromoteToNextMemoryTier(
1808
1605
1809
1606
if (newItemHdl) {
1810
1607
XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1811
- if (moveRegularItemForPromotion (item, newItemHdl)) {
1608
+ auto predicate = [this ](const Item& item) {
1609
+ // if inclusive cache is allowed, replace even if there are active users.
1610
+ return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1611
+ };
1612
+ if (tryMovingForSlabRelease (item, newItemHdl, std::move (predicate))) {
1812
1613
return true ;
1813
1614
}
1814
1615
}
@@ -2923,7 +2724,7 @@ bool CacheAllocator<CacheTrait>::moveForSlabRelease(
2923
2724
2924
2725
// if we have a valid handle, try to move, if not, we retry.
2925
2726
if (newItemHdl) {
2926
- isMoved = tryMovingForSlabRelease (oldItem, newItemHdl);
2727
+ isMoved = tryMovingForSlabRelease (oldItem, newItemHdl, itemMovingPredicate );
2927
2728
if (isMoved) {
2928
2729
break ;
2929
2730
}
@@ -3046,8 +2847,9 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
3046
2847
}
3047
2848
3048
2849
template <typename CacheTrait>
2850
+ template <typename Predicate>
3049
2851
bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3050
- Item& oldItem, ItemHandle& newItemHdl) {
2852
+ Item& oldItem, ItemHandle& newItemHdl, Predicate&& predicate ) {
3051
2853
// By holding onto a user-level synchronization object, we ensure moving
3052
2854
// a regular item or chained item is synchronized with any potential
3053
2855
// user-side mutation.
@@ -3079,7 +2881,7 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3079
2881
3080
2882
return oldItem.isChainedItem ()
3081
2883
? moveChainedItem (oldItem.asChainedItem (), newItemHdl)
3082
- : moveRegularItem (oldItem, newItemHdl);
2884
+ : moveRegularItem (oldItem, newItemHdl, std::move (predicate) );
3083
2885
}
3084
2886
3085
2887
template <typename CacheTrait>
0 commit comments