C/C++ API Reference
Loading...
Searching...
No Matches
key_value_store.h
1// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <array>
17#include <cstddef>
18#include <cstdint>
19#include <string_view>
20#include <type_traits>
21
22#include "pw_containers/vector.h"
23#include "pw_kvs/checksum.h"
24#include "pw_kvs/flash_memory.h"
25#include "pw_kvs/format.h"
26#include "pw_kvs/internal/entry.h"
27#include "pw_kvs/internal/entry_cache.h"
28#include "pw_kvs/internal/key_descriptor.h"
29#include "pw_kvs/internal/sectors.h"
30#include "pw_kvs/internal/span_traits.h"
31#include "pw_span/span.h"
32#include "pw_status/status.h"
33#include "pw_status/status_with_size.h"
34
35namespace pw {
36
38
40namespace kvs {
41
42enum class GargbageCollectOnWrite {
43 // Disable all automatic garbage collection on write.
44 kDisabled,
45
46 // Allow up to a single sector, if needed, to be garbage collected on write.
47 kOneSector,
48
49 // Allow as many sectors as needed be garbage collected on write.
50 kAsManySectorsNeeded,
51};
52
53enum class ErrorRecovery {
54 // Immediately do full recovery of any errors that are detected.
55 kImmediate,
56
57 // Recover from errors, but delay time consuming recovery steps until later
58 // as part of other time consuming operations. Such as waiting to garbage
59 // collect sectors with corrupt entries until the next garbage collection.
60 kLazy,
61
62 // Only recover from errors when manually triggered as part of maintenance
63 // operations. This is not recommended for normal use, only for test or
64 // read-only use.
65 kManual,
66};
67
68struct Options {
69 // Perform garbage collection if necessary when writing. If not kDisabled,
70 // garbage collection is attempted if space for an entry cannot be found. This
71 // is a relatively lengthy operation. If kDisabled, Put calls that would
72 // require garbage collection fail with RESOURCE_EXHAUSTED.
73 GargbageCollectOnWrite gc_on_write =
74 GargbageCollectOnWrite::kAsManySectorsNeeded;
75
76 // When the KVS handles errors that are discovered, such as corrupt entries,
77 // not enough redundant copys of an entry, etc.
78 ErrorRecovery recovery = ErrorRecovery::kLazy;
79
80 // Verify an entry's checksum after reading it from flash.
81 bool verify_on_read = true;
82
83 // Verify an in-flash entry's checksum after writing it.
84 bool verify_on_write = true;
85};
86
112 public:
121
122 bool initialized() const {
123 return initialized_ == InitializationState::kReady;
124 }
125
147 StatusWithSize Get(std::string_view key,
148 span<std::byte> value,
149 size_t offset_bytes = 0) const;
150
157 template <typename Pointer,
158 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
159 Status Get(const std::string_view& key, const Pointer& pointer) const {
160 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
161 CheckThatObjectCanBePutOrGet<T>();
162 return FixedSizeGet(key, pointer, sizeof(T));
163 }
164
184 template <typename T,
185 typename std::enable_if_t<ConvertsToSpan<T>::value>* = nullptr>
186 Status Put(const std::string_view& key, const T& value) {
187 return PutBytes(key, as_bytes(internal::make_span(value)));
188 }
189
190 template <typename T,
191 typename std::enable_if_t<!ConvertsToSpan<T>::value>* = nullptr>
192 Status Put(const std::string_view& key, const T& value) {
193 CheckThatObjectCanBePutOrGet<T>();
194 return PutBytes(key, as_bytes(span<const T>(&value, 1)));
195 }
196
209 Status Delete(std::string_view key);
210
222 StatusWithSize ValueSize(std::string_view key) const;
223
233 return FullMaintenanceHelper(MaintenanceType::kHeavy);
234 }
235
244 return FullMaintenanceHelper(MaintenanceType::kRegular);
245 }
246
252
253 void LogDebugInfo() const;
254
255 // Classes and functions to support STL-style iteration.
256 class iterator;
257
259 class Item {
260 public:
262 const char* key() const { return key_buffer_.data(); }
263
267 size_t offset_bytes = 0) const {
268 return kvs_.Get(key(), *iterator_, value_buffer, offset_bytes);
269 }
270
271 template <typename Pointer,
272 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
273 Status Get(const Pointer& pointer) const {
274 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
275 CheckThatObjectCanBePutOrGet<T>();
276 return kvs_.FixedSizeGet(key(), *iterator_, pointer, sizeof(T));
277 }
278
279 // Reads the size of the value referred to by this iterator. Equivalent to
280 // KeyValueStore::ValueSize.
281 StatusWithSize ValueSize() const { return kvs_.ValueSize(*iterator_); }
282
283 private:
284 friend class iterator;
285
286 constexpr Item(const KeyValueStore& kvs,
287 const internal::EntryCache::const_iterator& item_iterator)
288 : kvs_(kvs), iterator_(item_iterator), key_buffer_{} {}
289
290 void ReadKey();
291
292 const KeyValueStore& kvs_;
293 internal::EntryCache::const_iterator iterator_;
294
295 // Buffer large enough for a null-terminated version of any valid key.
296 std::array<char, internal::Entry::kMaxKeyLength + 1> key_buffer_;
297 };
298
300 class iterator {
301 public:
304
307 const iterator original(item_.kvs_, item_.iterator_);
308 operator++();
309 return original;
310 }
311
313 const Item& operator*() {
314 item_.ReadKey();
315 return item_;
316 }
317
319 const Item* operator->() { return &operator*(); }
320
322 constexpr bool operator==(const iterator& rhs) const {
323 return item_.iterator_ == rhs.item_.iterator_;
324 }
325
327 constexpr bool operator!=(const iterator& rhs) const {
328 return item_.iterator_ != rhs.item_.iterator_;
329 }
330
331 private:
332 friend class KeyValueStore;
333
334 constexpr iterator(
335 const KeyValueStore& kvs,
336 const internal::EntryCache::const_iterator& item_iterator)
337 : item_(kvs, item_iterator) {}
338
339 Item item_;
340 };
341
342 using const_iterator = iterator; // Standard alias for iterable types.
343
347 iterator end() const { return iterator(*this, entry_cache_.end()); }
348
350 size_t size() const { return entry_cache_.present_entries(); }
351
355 return entry_cache_.total_entries();
356 }
357
359 size_t max_size() const { return entry_cache_.max_entries(); }
360
362 size_t empty() const { return size() == 0u; }
363
367 uint32_t transaction_count() const { return last_transaction_id_; }
368
389 };
390
394
397 size_t redundancy() const { return entry_cache_.redundancy(); }
398
400 bool error_detected() const { return error_detected_; }
401
404 return max_key_value_size_bytes(partition_.sector_size_bytes());
405 }
406
409 static constexpr size_t max_key_value_size_bytes(
410 size_t partition_sector_size_bytes) {
411 return partition_sector_size_bytes - Entry::entry_overhead();
412 }
413
417
418 protected:
419 using Address = FlashPartition::Address;
420 using Entry = internal::Entry;
421 using KeyDescriptor = internal::KeyDescriptor;
422 using SectorDescriptor = internal::SectorDescriptor;
423
424 // In the future, will be able to provide additional EntryFormats for
425 // backwards compatibility.
426 KeyValueStore(FlashPartition* partition,
428 const Options& options,
429 size_t redundancy,
430 Vector<SectorDescriptor>& sector_descriptor_list,
431 const SectorDescriptor** temp_sectors_to_skip,
432 Vector<KeyDescriptor>& key_descriptor_list,
433 Address* addresses);
434
435 private:
436 using EntryMetadata = internal::EntryMetadata;
437 using EntryState = internal::EntryState;
438
439 template <typename T>
440 static constexpr void CheckThatObjectCanBePutOrGet() {
441 static_assert(
442 std::is_trivially_copyable<T>::value && !std::is_pointer<T>::value,
443 "Only trivially copyable, non-pointer objects may be Put and Get by "
444 "value. Any value may be stored by converting it to a byte span "
445 "with as_bytes(span(&value, 1)) or "
446 "as_writable_bytes(span(&value, 1)).");
447 }
448
449 Status InitializeMetadata();
450 Status LoadEntry(Address entry_address, Address* next_entry_address);
451 Status ScanForEntry(const SectorDescriptor& sector,
452 Address start_address,
453 Address* next_entry_address);
454
455 // Remove deleted keys from the entry cache, including freeing sector bytes
456 // used by those keys. This must only be done directly after a full garbage
457 // collection, otherwise the current deleted entry could be garbage
458 // collected before the older stale entry producing a window for an
459 // invalid/corrupted KVS state if there was a power-fault, crash or other
460 // interruption.
461 Status RemoveDeletedKeyEntries();
462
463 Status PutBytes(std::string_view key, span<const std::byte> value);
464
465 StatusWithSize ValueSize(const EntryMetadata& metadata) const;
466
467 Status ReadEntry(const EntryMetadata& metadata, Entry& entry) const;
468
469 // Finds the metadata for an entry matching a particular key. Searches for a
470 // KeyDescriptor that matches this key and sets *metadata_out to point to it
471 // if one is found.
472 //
473 // OK: there is a matching descriptor and *metadata is set
474 // NOT_FOUND: there is no descriptor that matches this key, but this key
475 // has a unique hash (and could potentially be added to the
476 // KVS)
477 // ALREADY_EXISTS: there is no descriptor that matches this key, but the
478 // key's hash collides with the hash for an existing
479 // descriptor
480 //
481 Status FindEntry(std::string_view key, EntryMetadata* metadata_out) const;
482
483 // Searches for a KeyDescriptor that matches this key and sets *metadata_out
484 // to point to it if one is found.
485 //
486 // OK: there is a matching descriptor and *metadata_out is set
487 // NOT_FOUND: there is no descriptor that matches this key
488 //
489 Status FindExisting(std::string_view key, EntryMetadata* metadata_out) const;
490
491 StatusWithSize Get(std::string_view key,
492 const EntryMetadata& metadata,
493 span<std::byte> value_buffer,
494 size_t offset_bytes) const;
495
496 Status FixedSizeGet(std::string_view key,
497 void* value,
498 size_t size_bytes) const;
499
500 Status FixedSizeGet(std::string_view key,
501 const EntryMetadata& metadata,
502 void* value,
503 size_t size_bytes) const;
504
505 Status CheckWriteOperation(std::string_view key) const;
506 Status CheckReadOperation(std::string_view key) const;
507
508 Status WriteEntryForExistingKey(EntryMetadata& metadata,
509 EntryState new_state,
510 std::string_view key,
512
513 Status WriteEntryForNewKey(std::string_view key, span<const std::byte> value);
514
515 Status WriteEntry(std::string_view key,
517 EntryState new_state,
518 EntryMetadata* prior_metadata = nullptr,
519 const internal::Entry* prior_entry = nullptr);
520
521 EntryMetadata CreateOrUpdateKeyDescriptor(const Entry& new_entry,
522 std::string_view key,
523 EntryMetadata* prior_metadata,
524 size_t prior_size);
525
526 EntryMetadata UpdateKeyDescriptor(const Entry& entry,
527 Address new_address,
528 EntryMetadata* prior_metadata,
529 size_t prior_size);
530
531 Status GetAddressesForWrite(Address* write_addresses, size_t write_size);
532
533 Status GetSectorForWrite(SectorDescriptor** sector,
534 size_t entry_size,
535 span<const Address> reserved_addresses);
536
537 Status MarkSectorCorruptIfNotOk(Status status, SectorDescriptor* sector);
538
539 Status AppendEntry(const Entry& entry,
540 std::string_view key,
542
543 StatusWithSize CopyEntryToSector(Entry& entry,
544 SectorDescriptor* new_sector,
545 Address new_address);
546
547 Status RelocateEntry(const EntryMetadata& metadata,
548 KeyValueStore::Address& address,
549 span<const Address> reserved_addresses);
550
551 // Perform all maintenance possible, including all neeeded repairing of
552 // corruption and garbage collection of reclaimable space in the KVS. When
553 // configured for manual recovery, this is the only way KVS repair is
554 // triggered.
555 //
556 // - Regular will not garbage collect sectors with valid data unless the KVS
557 // is mostly full.
558 // - Heavy will garbage collect all reclaimable space regardless of valid data
559 // in the sector.
560 enum class MaintenanceType {
561 kRegular,
562 kHeavy,
563 };
564 Status FullMaintenanceHelper(MaintenanceType maintenance_type);
565
566 // Find and garbage collect a singe sector that does not include a reserved
567 // address.
568 Status GarbageCollect(span<const Address> reserved_addresses);
569
570 Status RelocateKeyAddressesInSector(SectorDescriptor& sector_to_gc,
571 const EntryMetadata& metadata,
572 span<const Address> reserved_addresses);
573
574 Status GarbageCollectSector(SectorDescriptor& sector_to_gc,
575 span<const Address> reserved_addresses);
576
577 // Ensure that all entries are on the primary (first) format. Entries that are
578 // not on the primary format are rewritten.
579 //
580 // Return: status + number of entries updated.
581 StatusWithSize UpdateEntriesToPrimaryFormat();
582
583 Status AddRedundantEntries(EntryMetadata& metadata);
584
585 Status RepairCorruptSectors();
586
587 Status EnsureFreeSectorExists();
588
589 Status EnsureEntryRedundancy();
590
591 Status FixErrors();
592
593 Status Repair();
594
595 internal::Entry CreateEntry(Address address,
596 std::string_view key,
598 EntryState state);
599
600 void LogSectors() const;
601 void LogKeyDescriptor() const;
602
603 FlashPartition& partition_;
604 const internal::EntryFormats formats_;
605
606 // List of sectors used by this KVS.
607 internal::Sectors sectors_;
608
609 // Unordered list of KeyDescriptors. Finding a key requires scanning and
610 // verifying a match by reading the actual entry.
611 internal::EntryCache entry_cache_;
612
613 Options options_;
614
615 // Threshold value for when to garbage collect all stale data. Above the
616 // threshold, GC all reclaimable bytes regardless of if valid data is in
617 // sector. Below the threshold, only GC sectors with reclaimable bytes and no
618 // valid bytes. The threshold is based on the portion of KVS capacity used by
619 // valid bytes.
620 static constexpr size_t kGcUsageThresholdPercentage = 70;
621
622 enum class InitializationState {
623 // KVS Init() has not been called and KVS is not usable.
624 kNotInitialized,
625
626 // KVS Init() run but not all the needed invariants are met for an operating
627 // KVS. KVS is not writable, but read operaions are possible.
628 kNeedsMaintenance,
629
630 // KVS is fully ready for use.
631 kReady,
632 };
633 InitializationState initialized_;
634
635 // error_detected_ needs to be set from const KVS methods (such as Get), so
636 // make it mutable.
637 mutable bool error_detected_;
638
639 struct InternalStats {
640 size_t sector_erase_count;
641 size_t corrupt_sectors_recovered;
642 size_t missing_redundant_entries_recovered;
643 };
644 InternalStats internal_stats_;
645
646 uint32_t last_transaction_id_;
647};
648
649template <size_t kMaxEntries,
650 size_t kMaxUsableSectors,
651 size_t kRedundancy = 1,
652 size_t kEntryFormats = 1>
654 public:
655 // Constructs a KeyValueStore on the partition, with support for one
656 // EntryFormat (kEntryFormats must be 1).
657 KeyValueStoreBuffer(FlashPartition* partition,
658 const EntryFormat& format,
659 const Options& options = {})
661 partition,
663 reinterpret_cast<const EntryFormat (&)[1]>(format)),
664 options) {
665 static_assert(kEntryFormats == 1,
666 "kEntryFormats EntryFormats must be specified");
667 }
668
669 // Constructs a KeyValueStore on the partition. Supports multiple entry
670 // formats. The first EntryFormat is used for new entries.
671 KeyValueStoreBuffer(FlashPartition* partition,
673 const Options& options = {})
674 : KeyValueStore(partition,
675 formats_,
676 options,
677 kRedundancy,
678 sectors_,
679 temp_sectors_to_skip_,
680 key_descriptors_,
681 addresses_),
682 sectors_(),
683 key_descriptors_(),
684 formats_() {
685 std::copy(formats.begin(), formats.end(), formats_.begin());
686 }
687
688 private:
689 static_assert(kMaxEntries > 0u);
690 static_assert(kMaxUsableSectors > 0u);
691 static_assert(kRedundancy > 0u);
692 static_assert(kEntryFormats > 0u);
693
695
696 // Allocate space for an list of SectorDescriptors to avoid while searching
697 // for space to write entries. This is used to avoid changing sectors that are
698 // reserved for a new entry or marked as having other redundant copies of an
699 // entry. Up to kRedundancy sectors are reserved for a new entry, and up to
700 // kRedundancy - 1 sectors can have redundant copies of an entry, giving a
701 // maximum of 2 * kRedundancy - 1 sectors to avoid.
702 const SectorDescriptor* temp_sectors_to_skip_[2 * kRedundancy - 1];
703
704 // KeyDescriptors for use by the KVS's EntryCache.
705 Vector<KeyDescriptor, kMaxEntries> key_descriptors_;
706
707 // An array of addresses associated with the KeyDescriptors for use with the
708 // EntryCache. To support having KeyValueStores with different redundancies,
709 // the addresses are stored in a parallel array instead of inside
710 // KeyDescriptors.
711 internal::EntryCache::AddressList<kRedundancy, kMaxEntries> addresses_;
712
713 // EntryFormats that can be read by this KeyValueStore.
714 std::array<EntryFormat, kEntryFormats> formats_;
715};
716
717} // namespace kvs
718} // namespace pw
Definition: status.h:120
Definition: status_with_size.h:51
Definition: vector.h:65
Representation of a key-value entry during iteration.
Definition: key_value_store.h:259
Supported iteration methods.
Definition: key_value_store.h:300
Definition: key_value_store.h:653
Definition: key_value_store.h:111
Definition: span_impl.h:235
iterator end() const
Definition: key_value_store.h:347
size_t max_key_value_size_bytes() const
Definition: key_value_store.h:403
StatusWithSize ValueSize(std::string_view key) const
size_t max_size() const
Definition: key_value_store.h:359
StatusWithSize Get(span< std::byte > value_buffer, size_t offset_bytes=0) const
Definition: key_value_store.h:266
uint32_t transaction_count() const
Definition: key_value_store.h:367
const Item * operator->()
Reads the entry into the Item object.
Definition: key_value_store.h:319
iterator operator++(int)
Increments to the next key-value entry in the container.
Definition: key_value_store.h:306
const char * key() const
Definition: key_value_store.h:262
size_t sector_erase_count
The total count of individual sector erases that have been performed.
Definition: key_value_store.h:383
StatusWithSize Get(std::string_view key, span< std::byte > value, size_t offset_bytes=0) const
constexpr bool operator!=(const iterator &rhs) const
Inequality comparison of two entries.
Definition: key_value_store.h:327
const Item & operator*()
Reads the entry's key from flash.
Definition: key_value_store.h:313
size_t size() const
Definition: key_value_store.h:350
size_t corrupt_sectors_recovered
The number of corrupt sectors that have been recovered.
Definition: key_value_store.h:385
size_t redundancy() const
Definition: key_value_store.h:397
size_t writable_bytes
Definition: key_value_store.h:375
Status FullMaintenance()
Definition: key_value_store.h:243
bool error_detected() const
Definition: key_value_store.h:400
size_t empty() const
Definition: key_value_store.h:362
Status Delete(std::string_view key)
Status Put(const std::string_view &key, const T &value)
Definition: key_value_store.h:186
Status HeavyMaintenance()
Definition: key_value_store.h:232
iterator begin() const
size_t reclaimable_bytes
Definition: key_value_store.h:381
constexpr bool operator==(const iterator &rhs) const
Equality comparison of two entries.
Definition: key_value_store.h:322
iterator & operator++()
Increments to the next key-value entry in the container.
size_t in_use_bytes
The number of bytes in the KVS that are already in use.
Definition: key_value_store.h:377
Status Get(const std::string_view &key, const Pointer &pointer) const
Definition: key_value_store.h:159
StorageStats GetStorageStats() const
size_t missing_redundant_entries_recovered
Definition: key_value_store.h:388
size_t total_entries_with_deleted() const
Definition: key_value_store.h:354
static constexpr size_t max_key_value_size_bytes(size_t partition_sector_size_bytes)
Definition: key_value_store.h:409
The Pigweed namespace.
Definition: alignment.h:27
Definition: key_value_store.h:372
Definition: key_value_store.h:68