diff --git a/conanfile.py b/conanfile.py index 4ec340dfb..ae6ed9383 100644 --- a/conanfile.py +++ b/conanfile.py @@ -9,7 +9,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "7.5.11" + version = "7.5.12" homepage = "https://github.com/eBay/Homestore" description = "HomeStore Storage Engine" diff --git a/src/include/homestore/blkdata_service.hpp b/src/include/homestore/blkdata_service.hpp index 518ad26e7..e3e669175 100644 --- a/src/include/homestore/blkdata_service.hpp +++ b/src/include/homestore/blkdata_service.hpp @@ -166,8 +166,13 @@ class BlkDataService { * @brief Commits the block with the given MultiBlkId. * * @param bid The MultiBlkId of the block to commit. + * @param recommit If true, skip the allocation check and commit the blk unconditionally. + * Use this when re-committing a blk that is known to be live but whose allocator state + * may have been lost (e.g. after a crash where the blk exists in the index but the + * allocator watermarks were not yet persisted). Safe to pass when the allocator already + * tracks the blk, as the underlying operations are monotonically advancing. */ - BlkAllocStatus commit_blk(MultiBlkId const& bid); + BlkAllocStatus commit_blk(MultiBlkId const& bid, bool recommit = false); /** * @brief Allocates a contiguous block of disk space of the given size. This API should be called that when consumer diff --git a/src/lib/blkdata_svc/blkdata_service.cpp b/src/lib/blkdata_svc/blkdata_service.cpp index b0b73a5ef..bfc35d675 100644 --- a/src/lib/blkdata_svc/blkdata_service.cpp +++ b/src/lib/blkdata_svc/blkdata_service.cpp @@ -251,19 +251,19 @@ BlkAllocStatus BlkDataService::alloc_blks(uint32_t size, const blk_alloc_hints& return ret; } -BlkAllocStatus BlkDataService::commit_blk(MultiBlkId const& blkid) { +BlkAllocStatus BlkDataService::commit_blk(MultiBlkId const& blkid, bool recommit) { if (is_stopping()) return BlkAllocStatus::FAILED; incr_pending_request_num(); if (blkid.num_pieces() == 1) { // Shortcut to most common case - auto ret = m_vdev->commit_blk(blkid); + auto ret = m_vdev->commit_blk(blkid, recommit); decr_pending_request_num(); return ret; } auto it = blkid.iterate(); while (auto const bid = it.next()) { - auto alloc_status = m_vdev->commit_blk(*bid); + auto alloc_status = m_vdev->commit_blk(*bid, recommit); if (alloc_status != BlkAllocStatus::SUCCESS) { decr_pending_request_num(); return alloc_status; diff --git a/src/lib/device/virtual_dev.cpp b/src/lib/device/virtual_dev.cpp index b3c4bb3ac..b9b43bb89 100644 --- a/src/lib/device/virtual_dev.cpp +++ b/src/lib/device/virtual_dev.cpp @@ -174,7 +174,7 @@ bool VirtualDev::is_blk_alloced(BlkId const& blkid) const { return m_dmgr.get_chunk(blkid.chunk_num())->blk_allocator()->is_blk_alloced(blkid, true /* lock */); } -BlkAllocStatus VirtualDev::commit_blk(BlkId const& blkid) { +BlkAllocStatus VirtualDev::commit_blk(BlkId const& blkid, bool recommit) { Chunk* chunk = m_dmgr.get_chunk_mutable(blkid.chunk_num()); // if we start with missing drive, we will have no chunk for this blkid; if (!chunk) { @@ -188,12 +188,14 @@ BlkAllocStatus VirtualDev::commit_blk(BlkId const& blkid) { } auto const recovering = homestore::hs()->is_initializing(); - if (!recovering) { + if (recovering || recommit) { + // Also advance the in-memory watermark so the allocator considers this blk allocated; + // required both during normal recovery and on an explicit recommit by the caller. + chunk->blk_allocator_mutable()->reserve_on_cache(blkid); + } else { // in non-recovery mode, if a blk is committed without allocating, it will cause data corruption HS_REL_ASSERT(is_blk_alloced(blkid), "commiting blkid {} is not allocated in non-recovery mode", blkid.to_string()); - } else { - chunk->blk_allocator_mutable()->reserve_on_cache(blkid); } return chunk->blk_allocator_mutable()->reserve_on_disk(blkid); } diff --git a/src/lib/device/virtual_dev.hpp b/src/lib/device/virtual_dev.hpp index 621411389..d9c79645b 100644 --- a/src/lib/device/virtual_dev.hpp +++ b/src/lib/device/virtual_dev.hpp @@ -170,11 +170,13 @@ class VirtualDev { /// @brief Commits the blkid in on-disk version of the blk allocator. The blkid is assumed to be allocated using /// alloc_blk or alloc_contiguous_blk method earlier (either after reboot or prior to reboot). It is not required /// to call this method if alloc_blk is called and system is not restarted. Typical use case of this method is - /// during recovery where alloc_blk is called but before it was checkpointed, it crashed and we are trying to - /// recover Please note that even calling this method is not guaranteed to persisted until checkpoint is taken. + /// during recovery where alloc_blk is called, but before it was checkpointed, it crashed, and we are trying to + /// recover. Please note that even calling this method is not guaranteed to be persisted until checkpoint is taken. /// @param blkid BlkId to commit explicitly. + /// @param recommit If true, skip the allocation check and commit unconditionally. + /// See BlkDataService::commit_blk for details. /// @return Allocation Status - virtual BlkAllocStatus commit_blk(BlkId const& blkid); + virtual BlkAllocStatus commit_blk(BlkId const& blkid, bool recommit = false); virtual void free_blk(BlkId const& b, VDevCPContext* vctx = nullptr, bool free_now = false);