FIX bad merge: pw_kvs: Add public garbage collection methods and prep for redundancy
- Add public methods to do full garbage collect and partial garbage
collect.
- Do clean up related to finding sector to write entry to that is in
preparation for adding redundancy support.
Change-Id: Ib7e43aa8955a292b025cdff712e4af1745229a69
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 45bf2e4..faf7944 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -629,11 +629,11 @@
// an immediate extra relocation).
SectorDescriptor* new_sector;
- // TODO: For redundancy work, replace old_sector_const with a span of sectors
- // to avoid.
+ // TODO: For redundancy work, replace old_sector_const with a vector of
+ // sectors to avoid.
const SectorDescriptor* old_sector_const = old_sector;
TRY(FindSectorWithSpace(
- &new_sector, entry.size(), span(&old_sector_const, 1), true, false));
+ &new_sector, entry.size(), kGarbageCollect, span(&old_sector_const, 1)));
TRY(AppendEntry(
new_sector, &key_descriptor, key, value, key_descriptor.state()));
@@ -645,16 +645,15 @@
// Find either an existing sector with enough space that is not the sector to
// skip, or an empty sector. Maintains the invariant that there is always at
-// least 1 empty sector unless set to bypass the rule. Optionally skip sectors
-// that have reclaimable bytes.
+// least 1 empty sector except during GC. On GC, skip sectors that have
+// reclaimable bytes.
Status KeyValueStore::FindSectorWithSpace(
SectorDescriptor** found_sector,
size_t size,
- span<const SectorDescriptor*> sectors_to_skip,
- bool bypass_empty_sector_rule,
- bool allow_reclaimable) {
+ FindSectorMode find_mode,
+ span<const SectorDescriptor*> sectors_to_skip) {
SectorDescriptor* first_empty_sector = nullptr;
- bool at_least_two_empty_sectors = bypass_empty_sector_rule;
+ bool at_least_two_empty_sectors = (find_mode == kGarbageCollect);
DBG("Find sector with %zu bytes available, starting with sector %u",
size,
@@ -662,9 +661,6 @@
for (auto& skip_sector : sectors_to_skip) {
DBG(" Skip sector %u", SectorIndex(skip_sector));
}
- if (bypass_empty_sector_rule) {
- DBG(" Bypassing empty sector rule");
- }
// The last_new_sector_ is the sector that was last selected as the "new empty
// sector" to write to. This last new sector is used as the starting point for
@@ -678,14 +674,13 @@
// Look for a sector to use with enough space. The search uses a 2 priority
// tier process.
//
- // Tier 1 is sector that already has valid data. Optionally also only select
- // sector that has no reclaimable bytes. Immediately use the first one of
- // those that is found.
+ // Tier 1 is sector that already has valid data. During GC only select a
+ // sector that has no reclaimable bytes. Immediately use the first matching
+ // sector that is found.
//
- // Tier 2 is sectors that are empty. While scanning for a partial sector, keep
- // track of the first empty sector and if a second empty sector was seen. If
- // bypass_empty_sector_rule is true then count the second empty sector as
- // always seen.
+ // Tier 2 is find sectors that are empty/erased. While scanning for a partial
+ // sector, keep track of the first empty sector and if a second empty sector
+ // was seen. If during GC then count the second empty sector as always seen.
for (size_t j = 0; j < sectors_.size(); j++) {
sector += 1;
if (sector == sectors_.end()) {
@@ -699,7 +694,7 @@
const size_t sector_size_bytes = partition_.sector_size_bytes();
if (!sector->Empty(sector_size_bytes) && sector->HasSpace(size) &&
- (allow_reclaimable ||
+ ((find_mode == kAppendEntry) ||
(sector->RecoverableBytes(sector_size_bytes) == 0))) {
*found_sector = sector;
return Status::OK;
@@ -716,8 +711,7 @@
// If the scan for a partial sector does not find a suitable sector, use the
// first empty sector that was found. Normally it is required to keep 1 empty
- // sector after the sector found here, but that rule can be bypassed in
- // special circumstances (such as during garbage collection).
+ // sector after the sector found here, but that rule does not apply during GC.
if (at_least_two_empty_sectors) {
DBG(" Found a usable empty sector; returning the first found (%u)",
SectorIndex(first_empty_sector));
@@ -734,11 +728,11 @@
Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorDescriptor** sector,
size_t size) {
- Status result = FindSectorWithSpace(sector, size);
+ Status result = FindSectorWithSpace(sector, size, kAppendEntry);
if (result == Status::RESOURCE_EXHAUSTED && options_.partial_gc_on_write) {
// Garbage collect and then try again to find the best sector.
TRY(GarbageCollectPartial());
- return FindSectorWithSpace(sector, size);
+ return FindSectorWithSpace(sector, size, kAppendEntry);
}
return result;
}
@@ -782,7 +776,6 @@
Status KeyValueStore::GarbageCollectFull() {
DBG("Garbage Collect all sectors");
- LogSectors();
SectorDescriptor* sector = last_new_sector_;
// TODO: look in to making an iterator method for cycling through sectors
@@ -799,7 +792,6 @@
}
DBG("Garbage Collect all complete");
- LogSectors();
return Status::OK;
}
@@ -808,7 +800,6 @@
// Step 1: Find the sector to garbage collect
SectorDescriptor* sector_to_gc = FindSectorToGarbageCollect();
- LogSectors();
if (sector_to_gc == nullptr) {
// Nothing to GC, all done.
@@ -816,7 +807,6 @@
}
TRY(GarbageCollectSector(sector_to_gc));
- LogSectors();
return Status::OK;
}
diff --git a/pw_kvs/key_value_store_map_test.cc b/pw_kvs/key_value_store_map_test.cc
index f599cbb..99b76d2 100644
--- a/pw_kvs/key_value_store_map_test.cc
+++ b/pw_kvs/key_value_store_map_test.cc
@@ -56,6 +56,13 @@
size_t partition_alignment;
};
+enum Options {
+ kNone,
+ kReinit,
+ kReinitWithFullGC,
+ kReinitWithPartialGC,
+};
+
template <typename T>
std::set<T> difference(const std::set<T> lhs, const std::set<T> rhs) {
std::set<T> diff;
@@ -93,9 +100,7 @@
void Test_RandomValidInputs(int iterations,
uint_fast32_t seed,
- bool reinit = false,
- bool full_gc = false,
- bool partial_gc = false) {
+ Options options) {
std::mt19937 random(seed);
std::uniform_int_distribution<unsigned> distro;
auto random_int = [&] { return distro(random); };
@@ -109,7 +114,7 @@
};
for (int i = 0; i < iterations; ++i) {
- if (reinit && random_int() % 10 == 0) {
+ if (options != kNone && random_int() % 10 == 0) {
Init();
}
@@ -135,9 +140,9 @@
Put(key, random_string(random_int() % kMaxValueLength));
}
- if (full_gc && random_int() % 25 == 0) {
+ if (options == kReinitWithFullGC && random_int() % 250 == 0) {
GCFull();
- } else if (partial_gc && random_int() % 25 == 0) {
+ } else if (options == kReinitWithPartialGC && random_int() % 40 == 0) {
GCPartial();
}
}
@@ -366,47 +371,40 @@
// Defines a test fixture that runs all tests against a flash with the specified
// parameters.
-#define RUN_TESTS_WITH_PARAMETERS(name, ...) \
- class name : public ::testing::Test { \
- protected: \
- static constexpr TestParameters kParams = {__VA_ARGS__}; \
- \
- KvsTester<kParams> tester_; \
- }; \
- \
- /* Run each test defined in the KvsTester class with these parameters. */ \
- _TEST(name, Put); \
- _TEST(name, PutAndDelete_RelocateDeletedEntriesShouldStayDeleted); \
- _TEST_VARIANT(name, RandomValidInputs, 1, 100, 6006411, false); \
- _TEST_VARIANT(name, RandomValidInputs, 1WithReinit, 100, 6006411, true); \
- _TEST_VARIANT(name, RandomValidInputs, 2, 100, 123, false); \
- _TEST_VARIANT(name, RandomValidInputs, 2WithReinit, 100, 123, true); \
- _TEST_VARIANT(name, RandomValidInputs, 1FullGC, 100, 6006411, false, true); \
- _TEST_VARIANT( \
- name, RandomValidInputs, 1WithReinitFullGC, 100, 6006411, true, true); \
- _TEST_VARIANT(name, RandomValidInputs, 2FullGC, 100, 123, false); \
- _TEST_VARIANT( \
- name, RandomValidInputs, 2WithReinitFullGC, 100, 123, true, true); \
- _TEST_VARIANT( \
- name, RandomValidInputs, 1PartialGC, 100, 6006411, false, false, true); \
- _TEST_VARIANT(name, \
- RandomValidInputs, \
- 1WithReinitPartialGC, \
- 1000, \
- 6006411, \
- true, \
- false, \
- true); \
- _TEST_VARIANT( \
- name, RandomValidInputs, 2PartialGC, 100, 123, false, false, true); \
- _TEST_VARIANT(name, \
- RandomValidInputs, \
- 2WithReinitFullPartialGC, \
- 1000, \
- 123, \
- true, \
- true, \
- true); \
+#define RUN_TESTS_WITH_PARAMETERS(name, ...) \
+ class name : public ::testing::Test { \
+ protected: \
+ static constexpr TestParameters kParams = {__VA_ARGS__}; \
+ \
+ KvsTester<kParams> tester_; \
+ }; \
+ /* Run each test defined in the KvsTester class with these parameters. */ \
+ _TEST(name, Put); \
+ _TEST(name, PutAndDelete_RelocateDeletedEntriesShouldStayDeleted); \
+ _TEST_VARIANT(name, RandomValidInputs, 1, 1000, 6006411, kNone); \
+ _TEST_VARIANT(name, RandomValidInputs, 1WithReinit, 1000, 6006411, kReinit); \
+ _TEST_VARIANT(name, RandomValidInputs, 2, 100, 123, kNone); \
+ _TEST_VARIANT(name, RandomValidInputs, 2WithReinit, 100, 123, kReinit); \
+ _TEST_VARIANT(name, \
+ RandomValidInputs, \
+ 1ReinitFullGC, \
+ 1000, \
+ 6006411, \
+ kReinitWithFullGC); \
+ _TEST_VARIANT( \
+ name, RandomValidInputs, 2ReinitFullGC, 1000, 123, kReinitWithFullGC); \
+ _TEST_VARIANT(name, \
+ RandomValidInputs, \
+ 1ReinitPartialGC, \
+ 100, \
+ 6006411, \
+ kReinitWithPartialGC); \
+ _TEST_VARIANT(name, \
+ RandomValidInputs, \
+ 2ReinitPartialGC, \
+ 200, \
+ 123, \
+ kReinitWithPartialGC); \
static_assert(true, "Don't forget a semicolon!")
RUN_TESTS_WITH_PARAMETERS(Basic,
diff --git a/pw_kvs/public/pw_kvs/key_value_store.h b/pw_kvs/public/pw_kvs/key_value_store.h
index 5c7d384..e605754 100644
--- a/pw_kvs/public/pw_kvs/key_value_store.h
+++ b/pw_kvs/public/pw_kvs/key_value_store.h
@@ -313,12 +313,13 @@
Status RelocateEntry(KeyDescriptor& key_descriptor);
+ enum FindSectorMode { kAppendEntry, kGarbageCollect };
+
Status FindSectorWithSpace(SectorDescriptor** found_sector,
size_t size,
+ FindSectorMode find_mode,
span<const SectorDescriptor*> sector_to_skip =
- span<const SectorDescriptor*>(),
- bool bypass_empty_sector_rule = false,
- bool allow_reclaimable = true);
+ span<const SectorDescriptor*>());
Status FindOrRecoverSectorWithSpace(SectorDescriptor** sector, size_t size);