Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 {
36namespace kvs {
37
38enum class GargbageCollectOnWrite {
39 // Disable all automatic garbage collection on write.
40 kDisabled,
41
42 // Allow up to a single sector, if needed, to be garbage collected on write.
43 kOneSector,
44
45 // Allow as many sectors as needed be garbage collected on write.
46 kAsManySectorsNeeded,
47};
48
49enum class ErrorRecovery {
50 // Immediately do full recovery of any errors that are detected.
51 kImmediate,
52
53 // Recover from errors, but delay time consuming recovery steps until later
54 // as part of other time consuming operations. Such as waiting to garbage
55 // collect sectors with corrupt entries until the next garbage collection.
56 kLazy,
57
58 // Only recover from errors when manually triggered as part of maintenance
59 // operations. This is not recommended for normal use, only for test or
60 // read-only use.
61 kManual,
62};
63
64struct Options {
65 // Perform garbage collection if necessary when writing. If not kDisabled,
66 // garbage collection is attempted if space for an entry cannot be found. This
67 // is a relatively lengthy operation. If kDisabled, Put calls that would
68 // require garbage collection fail with RESOURCE_EXHAUSTED.
69 GargbageCollectOnWrite gc_on_write =
70 GargbageCollectOnWrite::kAsManySectorsNeeded;
71
72 // When the KVS handles errors that are discovered, such as corrupt entries,
73 // not enough redundant copys of an entry, etc.
74 ErrorRecovery recovery = ErrorRecovery::kLazy;
75
76 // Verify an entry's checksum after reading it from flash.
77 bool verify_on_read = true;
78
79 // Verify an in-flash entry's checksum after writing it.
80 bool verify_on_write = true;
81};
82
108 public:
124
125 bool initialized() const {
126 return initialized_ == InitializationState::kReady;
127 }
128
161 StatusWithSize Get(std::string_view key,
162 span<std::byte> value,
163 size_t offset_bytes = 0) const;
164
171 template <typename Pointer,
172 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
173 Status Get(const std::string_view& key, const Pointer& pointer) const {
174 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
175 CheckThatObjectCanBePutOrGet<T>();
176 return FixedSizeGet(key, pointer, sizeof(T));
177 }
178
209 template <typename T,
210 typename std::enable_if_t<ConvertsToSpan<T>::value>* = nullptr>
211 Status Put(const std::string_view& key, const T& value) {
212 return PutBytes(key, as_bytes(internal::make_span(value)));
213 }
214
215 template <typename T,
216 typename std::enable_if_t<!ConvertsToSpan<T>::value>* = nullptr>
217 Status Put(const std::string_view& key, const T& value) {
218 CheckThatObjectCanBePutOrGet<T>();
219 return PutBytes(key, as_bytes(span<const T>(&value, 1)));
220 }
221
245 Status Delete(std::string_view key);
246
267 StatusWithSize ValueSize(std::string_view key) const;
268
278 return FullMaintenanceHelper(MaintenanceType::kHeavy);
279 }
280
289 return FullMaintenanceHelper(MaintenanceType::kRegular);
290 }
291
297
298 void LogDebugInfo() const;
299
300 // Classes and functions to support STL-style iteration.
301 class iterator;
302
304 class Item {
305 public:
307 const char* key() const { return key_buffer_.data(); }
308
311 StatusWithSize Get(span<std::byte> value_buffer,
312 size_t offset_bytes = 0) const {
313 return kvs_.Get(key(), *iterator_, value_buffer, offset_bytes);
314 }
315
316 template <typename Pointer,
317 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
318 Status Get(const Pointer& pointer) const {
319 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
320 CheckThatObjectCanBePutOrGet<T>();
321 return kvs_.FixedSizeGet(key(), *iterator_, pointer, sizeof(T));
322 }
323
324 // Reads the size of the value referred to by this iterator. Equivalent to
325 // KeyValueStore::ValueSize.
326 StatusWithSize ValueSize() const { return kvs_.ValueSize(*iterator_); }
327
328 private:
329 friend class iterator;
330
331 constexpr Item(const KeyValueStore& kvs,
332 const internal::EntryCache::const_iterator& item_iterator)
333 : kvs_(kvs), iterator_(item_iterator), key_buffer_{} {}
334
335 void ReadKey();
336
337 const KeyValueStore& kvs_;
338 internal::EntryCache::const_iterator iterator_;
339
340 // Buffer large enough for a null-terminated version of any valid key.
341 std::array<char, internal::Entry::kMaxKeyLength + 1> key_buffer_;
342 };
343
345 class iterator {
346 public:
349
352 const iterator original(item_.kvs_, item_.iterator_);
353 operator++();
354 return original;
355 }
356
358 const Item& operator*() {
359 item_.ReadKey();
360 return item_;
361 }
362
364 const Item* operator->() { return &operator*(); }
365
367 constexpr bool operator==(const iterator& rhs) const {
368 return item_.iterator_ == rhs.item_.iterator_;
369 }
370
372 constexpr bool operator!=(const iterator& rhs) const {
373 return item_.iterator_ != rhs.item_.iterator_;
374 }
375
376 private:
377 friend class KeyValueStore;
378
379 constexpr iterator(
380 const KeyValueStore& kvs,
381 const internal::EntryCache::const_iterator& item_iterator)
382 : item_(kvs, item_iterator) {}
383
384 Item item_;
385 };
386
387 using const_iterator = iterator; // Standard alias for iterable types.
388
392 iterator end() const { return iterator(*this, entry_cache_.end()); }
393
395 size_t size() const { return entry_cache_.present_entries(); }
396
400 return entry_cache_.total_entries();
401 }
402
404 size_t max_size() const { return entry_cache_.max_entries(); }
405
407 size_t empty() const { return size() == 0u; }
408
412 uint32_t transaction_count() const { return last_transaction_id_; }
413
434 };
435
439
442 size_t redundancy() const { return entry_cache_.redundancy(); }
443
445 bool error_detected() const { return error_detected_; }
446
449 return max_key_value_size_bytes(partition_.sector_size_bytes());
450 }
451
454 static constexpr size_t max_key_value_size_bytes(
455 size_t partition_sector_size_bytes) {
456 return partition_sector_size_bytes - Entry::entry_overhead();
457 }
458
462
463 protected:
464 using Address = FlashPartition::Address;
465 using Entry = internal::Entry;
466 using KeyDescriptor = internal::KeyDescriptor;
467 using SectorDescriptor = internal::SectorDescriptor;
468
469 // In the future, will be able to provide additional EntryFormats for
470 // backwards compatibility.
471 KeyValueStore(FlashPartition* partition,
472 span<const EntryFormat> formats,
473 const Options& options,
474 size_t redundancy,
475 Vector<SectorDescriptor>& sector_descriptor_list,
476 const SectorDescriptor** temp_sectors_to_skip,
477 Vector<KeyDescriptor>& key_descriptor_list,
478 Address* addresses);
479
480 private:
481 using EntryMetadata = internal::EntryMetadata;
482 using EntryState = internal::EntryState;
483
484 template <typename T>
485 static constexpr void CheckThatObjectCanBePutOrGet() {
486 static_assert(
487 std::is_trivially_copyable<T>::value && !std::is_pointer<T>::value,
488 "Only trivially copyable, non-pointer objects may be Put and Get by "
489 "value. Any value may be stored by converting it to a byte span "
490 "with as_bytes(span(&value, 1)) or "
491 "as_writable_bytes(span(&value, 1)).");
492 }
493
494 Status InitializeMetadata();
495 Status LoadEntry(Address entry_address, Address* next_entry_address);
496 Status ScanForEntry(const SectorDescriptor& sector,
497 Address start_address,
498 Address* next_entry_address);
499
500 // Remove deleted keys from the entry cache, including freeing sector bytes
501 // used by those keys. This must only be done directly after a full garbage
502 // collection, otherwise the current deleted entry could be garbage
503 // collected before the older stale entry producing a window for an
504 // invalid/corrupted KVS state if there was a power-fault, crash or other
505 // interruption.
506 Status RemoveDeletedKeyEntries();
507
508 Status PutBytes(std::string_view key, span<const std::byte> value);
509
510 StatusWithSize ValueSize(const EntryMetadata& metadata) const;
511
512 Status ReadEntry(const EntryMetadata& metadata, Entry& entry) const;
513
514 // Finds the metadata for an entry matching a particular key. Searches for a
515 // KeyDescriptor that matches this key and sets *metadata_out to point to it
516 // if one is found.
517 //
518 // OK: there is a matching descriptor and *metadata is set
519 // NOT_FOUND: there is no descriptor that matches this key, but this key
520 // has a unique hash (and could potentially be added to the
521 // KVS)
522 // ALREADY_EXISTS: there is no descriptor that matches this key, but the
523 // key's hash collides with the hash for an existing
524 // descriptor
525 //
526 Status FindEntry(std::string_view key, EntryMetadata* metadata_out) const;
527
528 // Searches for a KeyDescriptor that matches this key and sets *metadata_out
529 // to point to it if one is found.
530 //
531 // OK: there is a matching descriptor and *metadata_out is set
532 // NOT_FOUND: there is no descriptor that matches this key
533 //
534 Status FindExisting(std::string_view key, EntryMetadata* metadata_out) const;
535
536 StatusWithSize Get(std::string_view key,
537 const EntryMetadata& metadata,
538 span<std::byte> value_buffer,
539 size_t offset_bytes) const;
540
541 Status FixedSizeGet(std::string_view key,
542 void* value,
543 size_t size_bytes) const;
544
545 Status FixedSizeGet(std::string_view key,
546 const EntryMetadata& metadata,
547 void* value,
548 size_t size_bytes) const;
549
550 Status CheckWriteOperation(std::string_view key) const;
551 Status CheckReadOperation(std::string_view key) const;
552
553 Status WriteEntryForExistingKey(EntryMetadata& metadata,
554 EntryState new_state,
555 std::string_view key,
556 span<const std::byte> value);
557
558 Status WriteEntryForNewKey(std::string_view key, span<const std::byte> value);
559
560 Status WriteEntry(std::string_view key,
561 span<const std::byte> value,
562 EntryState new_state,
563 EntryMetadata* prior_metadata = nullptr,
564 const internal::Entry* prior_entry = nullptr);
565
566 EntryMetadata CreateOrUpdateKeyDescriptor(const Entry& new_entry,
567 std::string_view key,
568 EntryMetadata* prior_metadata,
569 size_t prior_size);
570
571 EntryMetadata UpdateKeyDescriptor(const Entry& entry,
572 Address new_address,
573 EntryMetadata* prior_metadata,
574 size_t prior_size);
575
576 Status GetAddressesForWrite(Address* write_addresses, size_t write_size);
577
578 Status GetSectorForWrite(SectorDescriptor** sector,
579 size_t entry_size,
580 span<const Address> reserved_addresses);
581
582 Status MarkSectorCorruptIfNotOk(Status status, SectorDescriptor* sector);
583
584 Status AppendEntry(const Entry& entry,
585 std::string_view key,
586 span<const std::byte> value);
587
588 StatusWithSize CopyEntryToSector(Entry& entry,
589 SectorDescriptor* new_sector,
590 Address new_address);
591
592 Status RelocateEntry(const EntryMetadata& metadata,
593 KeyValueStore::Address& address,
594 span<const Address> reserved_addresses);
595
596 // Perform all maintenance possible, including all neeeded repairing of
597 // corruption and garbage collection of reclaimable space in the KVS. When
598 // configured for manual recovery, this is the only way KVS repair is
599 // triggered.
600 //
601 // - Regular will not garbage collect sectors with valid data unless the KVS
602 // is mostly full.
603 // - Heavy will garbage collect all reclaimable space regardless of valid data
604 // in the sector.
605 enum class MaintenanceType {
606 kRegular,
607 kHeavy,
608 };
609 Status FullMaintenanceHelper(MaintenanceType maintenance_type);
610
611 // Find and garbage collect a singe sector that does not include a reserved
612 // address.
613 Status GarbageCollect(span<const Address> reserved_addresses);
614
615 Status RelocateKeyAddressesInSector(SectorDescriptor& sector_to_gc,
616 const EntryMetadata& metadata,
617 span<const Address> reserved_addresses);
618
619 Status GarbageCollectSector(SectorDescriptor& sector_to_gc,
620 span<const Address> reserved_addresses);
621
622 // Ensure that all entries are on the primary (first) format. Entries that are
623 // not on the primary format are rewritten.
624 //
625 // Return: status + number of entries updated.
626 StatusWithSize UpdateEntriesToPrimaryFormat();
627
628 Status AddRedundantEntries(EntryMetadata& metadata);
629
630 Status RepairCorruptSectors();
631
632 Status EnsureFreeSectorExists();
633
634 Status EnsureEntryRedundancy();
635
636 Status FixErrors();
637
638 Status Repair();
639
640 internal::Entry CreateEntry(Address address,
641 std::string_view key,
642 span<const std::byte> value,
643 EntryState state);
644
645 void LogSectors() const;
646 void LogKeyDescriptor() const;
647
648 FlashPartition& partition_;
649 const internal::EntryFormats formats_;
650
651 // List of sectors used by this KVS.
652 internal::Sectors sectors_;
653
654 // Unordered list of KeyDescriptors. Finding a key requires scanning and
655 // verifying a match by reading the actual entry.
656 internal::EntryCache entry_cache_;
657
658 Options options_;
659
660 // Threshold value for when to garbage collect all stale data. Above the
661 // threshold, GC all reclaimable bytes regardless of if valid data is in
662 // sector. Below the threshold, only GC sectors with reclaimable bytes and no
663 // valid bytes. The threshold is based on the portion of KVS capacity used by
664 // valid bytes.
665 static constexpr size_t kGcUsageThresholdPercentage = 70;
666
667 enum class InitializationState {
668 // KVS Init() has not been called and KVS is not usable.
669 kNotInitialized,
670
671 // KVS Init() run but not all the needed invariants are met for an operating
672 // KVS. KVS is not writable, but read operaions are possible.
673 kNeedsMaintenance,
674
675 // KVS is fully ready for use.
676 kReady,
677 };
678 InitializationState initialized_;
679
680 // error_detected_ needs to be set from const KVS methods (such as Get), so
681 // make it mutable.
682 mutable bool error_detected_;
683
684 struct InternalStats {
685 size_t sector_erase_count;
686 size_t corrupt_sectors_recovered;
687 size_t missing_redundant_entries_recovered;
688 };
689 InternalStats internal_stats_;
690
691 uint32_t last_transaction_id_;
692};
693
694template <size_t kMaxEntries,
695 size_t kMaxUsableSectors,
696 size_t kRedundancy = 1,
697 size_t kEntryFormats = 1>
699 public:
700 // Constructs a KeyValueStore on the partition, with support for one
701 // EntryFormat (kEntryFormats must be 1).
702 KeyValueStoreBuffer(FlashPartition* partition,
703 const EntryFormat& format,
704 const Options& options = {})
706 partition,
707 span<const EntryFormat, kEntryFormats>(
708 reinterpret_cast<const EntryFormat (&)[1]>(format)),
709 options) {
710 static_assert(kEntryFormats == 1,
711 "kEntryFormats EntryFormats must be specified");
712 }
713
714 // Constructs a KeyValueStore on the partition. Supports multiple entry
715 // formats. The first EntryFormat is used for new entries.
716 KeyValueStoreBuffer(FlashPartition* partition,
717 span<const EntryFormat, kEntryFormats> formats,
718 const Options& options = {})
719 : KeyValueStore(partition,
720 formats_,
721 options,
722 kRedundancy,
723 sectors_,
724 temp_sectors_to_skip_,
725 key_descriptors_,
726 addresses_),
727 sectors_(),
728 key_descriptors_(),
729 formats_() {
730 std::copy(formats.begin(), formats.end(), formats_.begin());
731 }
732
733 private:
734 static_assert(kMaxEntries > 0u);
735 static_assert(kMaxUsableSectors > 0u);
736 static_assert(kRedundancy > 0u);
737 static_assert(kEntryFormats > 0u);
738
740
741 // Allocate space for an list of SectorDescriptors to avoid while searching
742 // for space to write entries. This is used to avoid changing sectors that are
743 // reserved for a new entry or marked as having other redundant copies of an
744 // entry. Up to kRedundancy sectors are reserved for a new entry, and up to
745 // kRedundancy - 1 sectors can have redundant copies of an entry, giving a
746 // maximum of 2 * kRedundancy - 1 sectors to avoid.
747 const SectorDescriptor* temp_sectors_to_skip_[2 * kRedundancy - 1];
748
749 // KeyDescriptors for use by the KVS's EntryCache.
750 Vector<KeyDescriptor, kMaxEntries> key_descriptors_;
751
752 // An array of addresses associated with the KeyDescriptors for use with the
753 // EntryCache. To support having KeyValueStores with different redundancies,
754 // the addresses are stored in a parallel array instead of inside
755 // KeyDescriptors.
756 internal::EntryCache::AddressList<kRedundancy, kMaxEntries> addresses_;
757
758 // EntryFormats that can be read by this KeyValueStore.
759 std::array<EntryFormat, kEntryFormats> formats_;
760};
761
762} // namespace kvs
763} // namespace pw
Definition: status.h:85
Definition: status_with_size.h:49
Definition: vector.h:65
Representation of a key-value entry during iteration.
Definition: key_value_store.h:304
StatusWithSize Get(span< std::byte > value_buffer, size_t offset_bytes=0) const
Definition: key_value_store.h:311
const char * key() const
Definition: key_value_store.h:307
Supported iteration methods.
Definition: key_value_store.h:345
const Item * operator->()
Reads the entry into the Item object.
Definition: key_value_store.h:364
iterator operator++(int)
Increments to the next key-value entry in the container.
Definition: key_value_store.h:351
constexpr bool operator!=(const iterator &rhs) const
Inequality comparison of two entries.
Definition: key_value_store.h:372
const Item & operator*()
Reads the entry's key from flash.
Definition: key_value_store.h:358
constexpr bool operator==(const iterator &rhs) const
Equality comparison of two entries.
Definition: key_value_store.h:367
iterator & operator++()
Increments to the next key-value entry in the container.
Definition: key_value_store.h:698
Definition: key_value_store.h:107
iterator end() const
Definition: key_value_store.h:392
size_t max_key_value_size_bytes() const
Definition: key_value_store.h:448
StatusWithSize ValueSize(std::string_view key) const
size_t max_size() const
Definition: key_value_store.h:404
uint32_t transaction_count() const
Definition: key_value_store.h:412
StatusWithSize Get(std::string_view key, span< std::byte > value, size_t offset_bytes=0) const
size_t size() const
Definition: key_value_store.h:395
size_t redundancy() const
Definition: key_value_store.h:442
Status FullMaintenance()
Definition: key_value_store.h:288
bool error_detected() const
Definition: key_value_store.h:445
size_t empty() const
Definition: key_value_store.h:407
Status Delete(std::string_view key)
Status Put(const std::string_view &key, const T &value)
Definition: key_value_store.h:211
Status HeavyMaintenance()
Definition: key_value_store.h:277
iterator begin() const
Status Get(const std::string_view &key, const Pointer &pointer) const
Definition: key_value_store.h:173
StorageStats GetStorageStats() const
size_t total_entries_with_deleted() const
Definition: key_value_store.h:399
static constexpr size_t max_key_value_size_bytes(size_t partition_sector_size_bytes)
Definition: key_value_store.h:454
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27
Definition: key_value_store.h:417
size_t sector_erase_count
The total count of individual sector erases that have been performed.
Definition: key_value_store.h:428
size_t corrupt_sectors_recovered
The number of corrupt sectors that have been recovered.
Definition: key_value_store.h:430
size_t writable_bytes
Definition: key_value_store.h:420
size_t reclaimable_bytes
Definition: key_value_store.h:426
size_t in_use_bytes
The number of bytes in the KVS that are already in use.
Definition: key_value_store.h:422
size_t missing_redundant_entries_recovered
Definition: key_value_store.h:433
Definition: key_value_store.h:64