@@ -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 ();
@@ -1290,159 +1292,11 @@ CacheAllocator<CacheTrait>::insertOrReplace(const ItemHandle& handle) {
1290
1292
return replaced;
1291
1293
}
1292
1294
1293
- /* Next two methods are used to asynchronously move Item between memory tiers.
1294
- *
1295
- * The thread, which moves Item, allocates new Item in the tier we are moving to
1296
- * and calls moveRegularItemOnEviction() method. This method does the following:
1297
- * 1. Create MoveCtx and put it to the movesMap.
1298
- * 2. Update the access container with the new item from the tier we are
1299
- * moving to. This Item has kIncomplete flag set.
1300
- * 3. Copy data from the old Item to the new one.
1301
- * 4. Unset the kIncomplete flag and Notify MoveCtx
1302
- *
1303
- * Concurrent threads which are getting handle to the same key:
1304
- * 1. When a handle is created it checks if the kIncomplete flag is set
1305
- * 2. If so, Handle implementation creates waitContext and adds it to the
1306
- * MoveCtx by calling addWaitContextForMovingItem() method.
1307
- * 3. Wait until the moving thread will complete its job.
1308
- */
1309
- template <typename CacheTrait>
1310
- bool CacheAllocator<CacheTrait>::addWaitContextForMovingItem(
1311
- folly::StringPiece key, std::shared_ptr<WaitContext<ReadHandle>> waiter) {
1312
- auto shard = getShardForKey (key);
1313
- auto & movesMap = getMoveMapForShard (shard);
1314
- auto lock = getMoveLockForShard (shard);
1315
- auto it = movesMap.find (key);
1316
- if (it == movesMap.end ()) {
1317
- return false ;
1318
- }
1319
- auto ctx = it->second .get ();
1320
- ctx->addWaiter (std::move (waiter));
1321
- return true ;
1322
- }
1323
-
1324
- template <typename CacheTrait>
1325
- typename CacheAllocator<CacheTrait>::ItemHandle
1326
- CacheAllocator<CacheTrait>::moveRegularItemOnEviction(
1327
- Item& oldItem, ItemHandle& newItemHdl) {
1328
- XDCHECK (oldItem.isMoving ());
1329
- // TODO: should we introduce new latency tracker. E.g. evictRegularLatency_
1330
- // ??? util::LatencyTracker tracker{stats_.evictRegularLatency_};
1331
-
1332
- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1333
- return {};
1334
- }
1335
-
1336
- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1337
- XDCHECK_NE (getTierId (oldItem), getTierId (*newItemHdl));
1338
-
1339
- // take care of the flags before we expose the item to be accessed. this
1340
- // will ensure that when another thread removes the item from RAM, we issue
1341
- // a delete accordingly. See D7859775 for an example
1342
- if (oldItem.isNvmClean ()) {
1343
- newItemHdl->markNvmClean ();
1344
- }
1345
-
1346
- folly::StringPiece key (oldItem.getKey ());
1347
- auto shard = getShardForKey (key);
1348
- auto & movesMap = getMoveMapForShard (shard);
1349
- MoveCtx* ctx (nullptr );
1350
- {
1351
- auto lock = getMoveLockForShard (shard);
1352
- auto res = movesMap.try_emplace (key, std::make_unique<MoveCtx>());
1353
- if (!res.second ) {
1354
- return {};
1355
- }
1356
- ctx = res.first ->second .get ();
1357
- }
1358
-
1359
- auto resHdl = ItemHandle{};
1360
- auto guard = folly::makeGuard ([key, this , ctx, shard, &resHdl]() {
1361
- auto & movesMap = getMoveMapForShard (shard);
1362
- if (resHdl)
1363
- resHdl->unmarkIncomplete ();
1364
- auto lock = getMoveLockForShard (shard);
1365
- ctx->setItemHandle (std::move (resHdl));
1366
- movesMap.erase (key);
1367
- });
1368
-
1369
- // TODO: Possibly we can use markMoving() instead. But today
1370
- // moveOnSlabRelease logic assume that we mark as moving old Item
1371
- // and than do copy and replace old Item with the new one in access
1372
- // container. Furthermore, Item can be marked as Moving only
1373
- // if it is linked to MM container. In our case we mark the new Item
1374
- // and update access container before the new Item is ready (content is
1375
- // copied).
1376
- newItemHdl->markIncomplete ();
1377
-
1378
- // Inside the access container's lock, this checks if the old item is
1379
- // accessible and its refcount is zero. If the item is not accessible,
1380
- // there is no point to replace it since it had already been removed
1381
- // or in the process of being removed. If the item is in cache but the
1382
- // refcount is non-zero, it means user could be attempting to remove
1383
- // this item through an API such as remove(ItemHandle). In this case,
1384
- // it is unsafe to replace the old item with a new one, so we should
1385
- // also abort.
1386
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl,
1387
- itemMovingPredicate)) {
1388
- return {};
1389
- }
1390
-
1391
- if (config_.moveCb ) {
1392
- // Execute the move callback. We cannot make any guarantees about the
1393
- // consistency of the old item beyond this point, because the callback can
1394
- // do more than a simple memcpy() e.g. update external references. If there
1395
- // are any remaining handles to the old item, it is the caller's
1396
- // responsibility to invalidate them. The move can only fail after this
1397
- // statement if the old item has been removed or replaced, in which case it
1398
- // should be fine for it to be left in an inconsistent state.
1399
- config_.moveCb (oldItem, *newItemHdl, nullptr );
1400
- } else {
1401
- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1402
- oldItem.getSize ());
1403
- }
1404
-
1405
- // Inside the MM container's lock, this checks if the old item exists to
1406
- // make sure that no other thread removed it, and only then replaces it.
1407
- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1408
- accessContainer_->remove (*newItemHdl);
1409
- return {};
1410
- }
1411
-
1412
- // Replacing into the MM container was successful, but someone could have
1413
- // called insertOrReplace() or remove() before or after the
1414
- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1415
- if (!newItemHdl->isAccessible ()) {
1416
- removeFromMMContainer (*newItemHdl);
1417
- return {};
1418
- }
1419
-
1420
- // no one can add or remove chained items at this point
1421
- if (oldItem.hasChainedItem ()) {
1422
- // safe to acquire handle for a moving Item
1423
- auto oldHandle = acquire (&oldItem);
1424
- XDCHECK_EQ (1u , oldHandle->getRefCount ()) << oldHandle->toString ();
1425
- XDCHECK (!newItemHdl->hasChainedItem ()) << newItemHdl->toString ();
1426
- try {
1427
- auto l = chainedItemLocks_.lockExclusive (oldItem.getKey ());
1428
- transferChainLocked (oldHandle, newItemHdl);
1429
- } catch (const std::exception& e) {
1430
- // this should never happen because we drained all the handles.
1431
- XLOGF (DFATAL, " {}" , e.what ());
1432
- throw ;
1433
- }
1434
-
1435
- XDCHECK (!oldItem.hasChainedItem ());
1436
- XDCHECK (newItemHdl->hasChainedItem ());
1437
- }
1438
- newItemHdl.unmarkNascent ();
1439
- resHdl = std::move (newItemHdl); // guard will assign it to ctx under lock
1440
- return acquire (&oldItem);
1441
- }
1442
-
1443
1295
template <typename CacheTrait>
1296
+ template <typename Predicate>
1444
1297
bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1445
- ItemHandle& newItemHdl) {
1298
+ ItemHandle& newItemHdl,
1299
+ Predicate &&predicate) {
1446
1300
XDCHECK (config_.moveCb );
1447
1301
util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1448
1302
@@ -1451,8 +1305,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1451
1305
}
1452
1306
1453
1307
XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1454
- XDCHECK_EQ (reinterpret_cast <uintptr_t >(&getMMContainer (oldItem)),
1455
- reinterpret_cast <uintptr_t >(&getMMContainer (*newItemHdl)));
1456
1308
1457
1309
// take care of the flags before we expose the item to be accessed. this
1458
1310
// will ensure that when another thread removes the item from RAM, we issue
@@ -1478,7 +1330,7 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1478
1330
// this item through an API such as remove(ItemHandle). In this case,
1479
1331
// it is unsafe to replace the old item with a new one, so we should
1480
1332
// also abort.
1481
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, itemMovingPredicate )) {
1333
+ if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate )) {
1482
1334
return false ;
1483
1335
}
1484
1336
@@ -1519,63 +1371,6 @@ bool CacheAllocator<CacheTrait>::moveRegularItem(Item& oldItem,
1519
1371
return true ;
1520
1372
}
1521
1373
1522
- template <typename CacheTrait>
1523
- bool CacheAllocator<CacheTrait>::moveRegularItemForPromotion(Item& oldItem,
1524
- ItemHandle& newItemHdl) {
1525
- XDCHECK (config_.moveCb );
1526
- util::LatencyTracker tracker{stats_.moveRegularLatency_ };
1527
-
1528
- if (!oldItem.isAccessible () || oldItem.isExpired ()) {
1529
- return false ;
1530
- }
1531
-
1532
- XDCHECK_EQ (newItemHdl->getSize (), oldItem.getSize ());
1533
-
1534
- if (config_.moveCb ) {
1535
- // Execute the move callback. We cannot make any guarantees about the
1536
- // consistency of the old item beyond this point, because the callback can
1537
- // do more than a simple memcpy() e.g. update external references. If there
1538
- // are any remaining handles to the old item, it is the caller's
1539
- // responsibility to invalidate them. The move can only fail after this
1540
- // statement if the old item has been removed or replaced, in which case it
1541
- // should be fine for it to be left in an inconsistent state.
1542
- config_.moveCb (oldItem, *newItemHdl, nullptr );
1543
- } else {
1544
- std::memcpy (newItemHdl->getWritableMemory (), oldItem.getMemory (),
1545
- oldItem.getSize ());
1546
- }
1547
-
1548
- auto predicate = [this ](const Item& item) {
1549
- // if inclusive cache is allowed, replace even if there are active users.
1550
- return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1551
- };
1552
- if (!accessContainer_->replaceIf (oldItem, *newItemHdl, predicate)) {
1553
- return false ;
1554
- }
1555
-
1556
- // Inside the MM container's lock, this checks if the old item exists to
1557
- // make sure that no other thread removed it, and only then replaces it.
1558
- if (!replaceInMMContainer (oldItem, *newItemHdl)) {
1559
- accessContainer_->remove (*newItemHdl);
1560
- return false ;
1561
- }
1562
-
1563
- // Replacing into the MM container was successful, but someone could have
1564
- // called insertOrReplace() or remove() before or after the
1565
- // replaceInMMContainer() operation, which would invalidate newItemHdl.
1566
- if (!newItemHdl->isAccessible ()) {
1567
- removeFromMMContainer (*newItemHdl);
1568
- return false ;
1569
- }
1570
-
1571
- // no one can add or remove chained items at this point
1572
- if (oldItem.hasChainedItem ()) {
1573
- throw std::runtime_error (" Not supported" );
1574
- }
1575
- newItemHdl.unmarkNascent ();
1576
- return true ;
1577
- }
1578
-
1579
1374
template <typename CacheTrait>
1580
1375
bool CacheAllocator<CacheTrait>::moveChainedItem(ChainedItem& oldItem,
1581
1376
ItemHandle& newItemHdl) {
@@ -1815,8 +1610,9 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1815
1610
1816
1611
if (newItemHdl) {
1817
1612
XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1818
-
1819
- return moveRegularItemOnEviction (item, newItemHdl);
1613
+ if (tryMovingForSlabRelease (item, newItemHdl, itemMovingPredicate)) {
1614
+ return acquire (&item);
1615
+ }
1820
1616
}
1821
1617
}
1822
1618
@@ -1842,7 +1638,11 @@ CacheAllocator<CacheTrait>::tryPromoteToNextMemoryTier(
1842
1638
1843
1639
if (newItemHdl) {
1844
1640
XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1845
- if (moveRegularItemForPromotion (item, newItemHdl)) {
1641
+ auto predicate = [this ](const Item& item) {
1642
+ // if inclusive cache is allowed, replace even if there are active users.
1643
+ return config_.numDuplicateElements > 0 || item.getRefCount () == 0 ;
1644
+ };
1645
+ if (tryMovingForSlabRelease (item, newItemHdl, std::move (predicate))) {
1846
1646
return true ;
1847
1647
}
1848
1648
}
@@ -2919,7 +2719,7 @@ bool CacheAllocator<CacheTrait>::moveForSlabRelease(
2919
2719
2920
2720
// if we have a valid handle, try to move, if not, we retry.
2921
2721
if (newItemHdl) {
2922
- isMoved = tryMovingForSlabRelease (oldItem, newItemHdl);
2722
+ isMoved = tryMovingForSlabRelease (oldItem, newItemHdl, itemMovingPredicate );
2923
2723
if (isMoved) {
2924
2724
break ;
2925
2725
}
@@ -3042,8 +2842,9 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
3042
2842
}
3043
2843
3044
2844
template <typename CacheTrait>
2845
+ template <typename Predicate>
3045
2846
bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3046
- Item& oldItem, ItemHandle& newItemHdl) {
2847
+ Item& oldItem, ItemHandle& newItemHdl, Predicate&& predicate ) {
3047
2848
// By holding onto a user-level synchronization object, we ensure moving
3048
2849
// a regular item or chained item is synchronized with any potential
3049
2850
// user-side mutation.
@@ -3075,7 +2876,7 @@ bool CacheAllocator<CacheTrait>::tryMovingForSlabRelease(
3075
2876
3076
2877
return oldItem.isChainedItem ()
3077
2878
? moveChainedItem (oldItem.asChainedItem (), newItemHdl)
3078
- : moveRegularItem (oldItem, newItemHdl);
2879
+ : moveRegularItem (oldItem, newItemHdl, std::move (predicate) );
3079
2880
}
3080
2881
3081
2882
template <typename CacheTrait>
0 commit comments