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
116 public:
125
126 bool initialized() const {
127 return initialized_ == InitializationState::kReady;
128 }
129
151 StatusWithSize Get(std::string_view key,
152 span<std::byte> value,
153 size_t offset_bytes = 0) const;
154
161 template <typename Pointer,
162 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
163 Status Get(const std::string_view& key, const Pointer& pointer) const {
164 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
165 CheckThatObjectCanBePutOrGet<T>();
166 return FixedSizeGet(key, pointer, sizeof(T));
167 }
168
188 template <typename T,
189 typename std::enable_if_t<ConvertsToSpan<T>::value>* = nullptr>
190 Status Put(const std::string_view& key, const T& value) {
191 return PutBytes(key, as_bytes(internal::make_span(value)));
192 }
193
194 template <typename T,
195 typename std::enable_if_t<!ConvertsToSpan<T>::value>* = nullptr>
196 Status Put(const std::string_view& key, const T& value) {
197 CheckThatObjectCanBePutOrGet<T>();
198 return PutBytes(key, as_bytes(span<const T>(&value, 1)));
199 }
200
213 Status Delete(std::string_view key);
214
226 StatusWithSize ValueSize(std::string_view key) const;
227
237 return FullMaintenanceHelper(MaintenanceType::kHeavy);
238 }
239
248 return FullMaintenanceHelper(MaintenanceType::kRegular);
249 }
250
256
257 void LogDebugInfo() const;
258
259 // Classes and functions to support STL-style iteration.
260 class iterator;
261
263 class Item {
264 public:
266 const char* key() const { return key_buffer_.data(); }
267
271 size_t offset_bytes = 0) const {
272 return kvs_.Get(key(), *iterator_, value_buffer, offset_bytes);
273 }
274
275 template <typename Pointer,
276 typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
277 Status Get(const Pointer& pointer) const {
278 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>;
279 CheckThatObjectCanBePutOrGet<T>();
280 return kvs_.FixedSizeGet(key(), *iterator_, pointer, sizeof(T));
281 }
282
283 // Reads the size of the value referred to by this iterator. Equivalent to
284 // KeyValueStore::ValueSize.
285 StatusWithSize ValueSize() const { return kvs_.ValueSize(*iterator_); }
286
287 private:
288 friend class iterator;
289
290 constexpr Item(const KeyValueStore& kvs,
291 const internal::EntryCache::const_iterator& item_iterator)
292 : kvs_(kvs), iterator_(item_iterator), key_buffer_{} {}
293
294 void ReadKey();
295
296 const KeyValueStore& kvs_;
297 internal::EntryCache::const_iterator iterator_;
298
299 // Buffer large enough for a null-terminated version of any valid key.
300 std::array<char, internal::Entry::kMaxKeyLength + 1> key_buffer_;
301 };
302
304 class iterator {
305 public:
308
311 const iterator original(item_.kvs_, item_.iterator_);
312 operator++();
313 return original;
314 }
315
317 const Item& operator*() {
318 item_.ReadKey();
319 return item_;
320 }
321
323 const Item* operator->() { return &operator*(); }
324
326 constexpr bool operator==(const iterator& rhs) const {
327 return item_.iterator_ == rhs.item_.iterator_;
328 }
329
331 constexpr bool operator!=(const iterator& rhs) const {
332 return item_.iterator_ != rhs.item_.iterator_;
333 }
334
335 private:
336 friend class KeyValueStore;
337
338 constexpr iterator(
339 const KeyValueStore& kvs,
340 const internal::EntryCache::const_iterator& item_iterator)
341 : item_(kvs, item_iterator) {}
342
343 Item item_;
344 };
345
346 using const_iterator = iterator; // Standard alias for iterable types.
347
351 iterator end() const { return iterator(*this, entry_cache_.end()); }
352
354 size_t size() const { return entry_cache_.present_entries(); }
355
359 return entry_cache_.total_entries();
360 }
361
363 size_t max_size() const { return entry_cache_.max_entries(); }
364
366 size_t empty() const { return size() == 0u; }
367
371 uint32_t transaction_count() const { return last_transaction_id_; }
372
393 };
394
398
401 size_t redundancy() const { return entry_cache_.redundancy(); }
402
404 bool error_detected() const { return error_detected_; }
405
408 return max_key_value_size_bytes(partition_.sector_size_bytes());
409 }
410
413 static constexpr size_t max_key_value_size_bytes(
414 size_t partition_sector_size_bytes) {
415 return partition_sector_size_bytes - Entry::entry_overhead();
416 }
417
421
422 protected:
423 using Address = FlashPartition::Address;
424 using Entry = internal::Entry;
425 using KeyDescriptor = internal::KeyDescriptor;
426 using SectorDescriptor = internal::SectorDescriptor;
427
428 // In the future, will be able to provide additional EntryFormats for
429 // backwards compatibility.
430 KeyValueStore(FlashPartition* partition,
432 const Options& options,
433 size_t redundancy,
434 Vector<SectorDescriptor>& sector_descriptor_list,
435 const SectorDescriptor** temp_sectors_to_skip,
436 Vector<KeyDescriptor>& key_descriptor_list,
437 Address* addresses);
438
439 private:
440 using EntryMetadata = internal::EntryMetadata;
441 using EntryState = internal::EntryState;
442
443 template <typename T>
444 static constexpr void CheckThatObjectCanBePutOrGet() {
445 static_assert(
446 std::is_trivially_copyable<T>::value && !std::is_pointer<T>::value,
447 "Only trivially copyable, non-pointer objects may be Put and Get by "
448 "value. Any value may be stored by converting it to a byte span "
449 "with as_bytes(span(&value, 1)) or "
450 "as_writable_bytes(span(&value, 1)).");
451 }
452
453 Status InitializeMetadata();
454 Status LoadEntry(Address entry_address, Address* next_entry_address);
455 Status ScanForEntry(const SectorDescriptor& sector,
456 Address start_address,
457 Address* next_entry_address);
458
459 // Remove deleted keys from the entry cache, including freeing sector bytes
460 // used by those keys. This must only be done directly after a full garbage
461 // collection, otherwise the current deleted entry could be garbage
462 // collected before the older stale entry producing a window for an
463 // invalid/corrupted KVS state if there was a power-fault, crash or other
464 // interruption.
465 Status RemoveDeletedKeyEntries();
466
467 Status PutBytes(std::string_view key, span<const std::byte> value);
468
469 StatusWithSize ValueSize(const EntryMetadata& metadata) const;
470
471 Status ReadEntry(const EntryMetadata& metadata, Entry& entry) const;
472
473 // Finds the metadata for an entry matching a particular key. Searches for a
474 // KeyDescriptor that matches this key and sets *metadata_out to point to it
475 // if one is found.
476 //
477 // OK: there is a matching descriptor and *metadata is set
478 // NOT_FOUND: there is no descriptor that matches this key, but this key
479 // has a unique hash (and could potentially be added to the
480 // KVS)
481 // ALREADY_EXISTS: there is no descriptor that matches this key, but the
482 // key's hash collides with the hash for an existing
483 // descriptor
484 //
485 Status FindEntry(std::string_view key, EntryMetadata* metadata_out) const;
486
487 // Searches for a KeyDescriptor that matches this key and sets *metadata_out
488 // to point to it if one is found.
489 //
490 // OK: there is a matching descriptor and *metadata_out is set
491 // NOT_FOUND: there is no descriptor that matches this key
492 //
493 Status FindExisting(std::string_view key, EntryMetadata* metadata_out) const;
494
495 StatusWithSize Get(std::string_view key,
496 const EntryMetadata& metadata,
497 span<std::byte> value_buffer,
498 size_t offset_bytes) const;
499
500 Status FixedSizeGet(std::string_view key,
501 void* value,
502 size_t size_bytes) const;
503
504 Status FixedSizeGet(std::string_view key,
505 const EntryMetadata& metadata,
506 void* value,
507 size_t size_bytes) const;
508
509 Status CheckWriteOperation(std::string_view key) const;
510 Status CheckReadOperation(std::string_view key) const;
511
512 Status WriteEntryForExistingKey(EntryMetadata& metadata,
513 EntryState new_state,
514 std::string_view key,
516
517 Status WriteEntryForNewKey(std::string_view key, span<const std::byte> value);
518
519 Status WriteEntry(std::string_view key,
521 EntryState new_state,
522 EntryMetadata* prior_metadata = nullptr,
523 const internal::Entry* prior_entry = nullptr);
524
525 EntryMetadata CreateOrUpdateKeyDescriptor(const Entry& new_entry,
526 std::string_view key,
527 EntryMetadata* prior_metadata,
528 size_t prior_size);
529
530 EntryMetadata UpdateKeyDescriptor(const Entry& entry,
531 Address new_address,
532 EntryMetadata* prior_metadata,
533 size_t prior_size);
534
535 Status GetAddressesForWrite(Address* write_addresses, size_t write_size);
536
537 Status GetSectorForWrite(SectorDescriptor** sector,
538 size_t entry_size,
539 span<const Address> reserved_addresses);
540
541 Status MarkSectorCorruptIfNotOk(Status status, SectorDescriptor* sector);
542
543 Status AppendEntry(const Entry& entry,
544 std::string_view key,
546
547 StatusWithSize CopyEntryToSector(Entry& entry,
548 SectorDescriptor* new_sector,
549 Address new_address);
550
551 Status RelocateEntry(const EntryMetadata& metadata,
552 KeyValueStore::Address& address,
553 span<const Address> reserved_addresses);
554
555 // Perform all maintenance possible, including all neeeded repairing of
556 // corruption and garbage collection of reclaimable space in the KVS. When
557 // configured for manual recovery, this is the only way KVS repair is
558 // triggered.
559 //
560 // - Regular will not garbage collect sectors with valid data unless the KVS
561 // is mostly full.
562 // - Heavy will garbage collect all reclaimable space regardless of valid data
563 // in the sector.
564 enum class MaintenanceType {
565 kRegular,
566 kHeavy,
567 };
568 Status FullMaintenanceHelper(MaintenanceType maintenance_type);
569
570 // Find and garbage collect a singe sector that does not include a reserved
571 // address.
572 Status GarbageCollect(span<const Address> reserved_addresses);
573
574 Status RelocateKeyAddressesInSector(SectorDescriptor& sector_to_gc,
575 const EntryMetadata& metadata,
576 span<const Address> reserved_addresses);
577
578 Status GarbageCollectSector(SectorDescriptor& sector_to_gc,
579 span<const Address> reserved_addresses);
580
581 // Ensure that all entries are on the primary (first) format. Entries that are
582 // not on the primary format are rewritten.
583 //
584 // Return: status + number of entries updated.
585 StatusWithSize UpdateEntriesToPrimaryFormat();
586
587 Status AddRedundantEntries(EntryMetadata& metadata);
588
589 Status RepairCorruptSectors();
590
591 Status EnsureFreeSectorExists();
592
593 Status EnsureEntryRedundancy();
594
595 Status FixErrors();
596
597 Status Repair();
598
599 internal::Entry CreateEntry(Address address,
600 std::string_view key,
602 EntryState state);
603
604 void LogSectors() const;
605 void LogKeyDescriptor() const;
606
607 FlashPartition& partition_;
608 const internal::EntryFormats formats_;
609
610 // List of sectors used by this KVS.
611 internal::Sectors sectors_;
612
613 // Unordered list of KeyDescriptors. Finding a key requires scanning and
614 // verifying a match by reading the actual entry.
615 internal::EntryCache entry_cache_;
616
617 Options options_;
618
619 // Threshold value for when to garbage collect all stale data. Above the
620 // threshold, GC all reclaimable bytes regardless of if valid data is in
621 // sector. Below the threshold, only GC sectors with reclaimable bytes and no
622 // valid bytes. The threshold is based on the portion of KVS capacity used by
623 // valid bytes.
624 static constexpr size_t kGcUsageThresholdPercentage = 70;
625
626 enum class InitializationState {
627 // KVS Init() has not been called and KVS is not usable.
628 kNotInitialized,
629
630 // KVS Init() run but not all the needed invariants are met for an operating
631 // KVS. KVS is not writable, but read operaions are possible.
632 kNeedsMaintenance,
633
634 // KVS is fully ready for use.
635 kReady,
636 };
637 InitializationState initialized_;
638
639 // error_detected_ needs to be set from const KVS methods (such as Get), so
640 // make it mutable.
641 mutable bool error_detected_;
642
643 struct InternalStats {
644 size_t sector_erase_count;
645 size_t corrupt_sectors_recovered;
646 size_t missing_redundant_entries_recovered;
647 };
648 InternalStats internal_stats_;
649
650 uint32_t last_transaction_id_;
651};
652
653template <size_t kMaxEntries,
654 size_t kMaxUsableSectors,
655 size_t kRedundancy = 1,
656 size_t kEntryFormats = 1>
658 public:
659 // Constructs a KeyValueStore on the partition, with support for one
660 // EntryFormat (kEntryFormats must be 1).
661 KeyValueStoreBuffer(FlashPartition* partition,
662 const EntryFormat& format,
663 const Options& options = {})
665 partition,
667 reinterpret_cast<const EntryFormat (&)[1]>(format)),
668 options) {
669 static_assert(kEntryFormats == 1,
670 "kEntryFormats EntryFormats must be specified");
671 }
672
673 // Constructs a KeyValueStore on the partition. Supports multiple entry
674 // formats. The first EntryFormat is used for new entries.
675 KeyValueStoreBuffer(FlashPartition* partition,
677 const Options& options = {})
678 : KeyValueStore(partition,
679 formats_,
680 options,
681 kRedundancy,
682 sectors_,
683 temp_sectors_to_skip_,
684 key_descriptors_,
685 addresses_),
686 sectors_(),
687 key_descriptors_(),
688 formats_() {
689 std::copy(formats.begin(), formats.end(), formats_.begin());
690 }
691
692 private:
693 static_assert(kMaxEntries > 0u);
694 static_assert(kMaxUsableSectors > 0u);
695 static_assert(kRedundancy > 0u);
696 static_assert(kEntryFormats > 0u);
697
699
700 // Allocate space for an list of SectorDescriptors to avoid while searching
701 // for space to write entries. This is used to avoid changing sectors that are
702 // reserved for a new entry or marked as having other redundant copies of an
703 // entry. Up to kRedundancy sectors are reserved for a new entry, and up to
704 // kRedundancy - 1 sectors can have redundant copies of an entry, giving a
705 // maximum of 2 * kRedundancy - 1 sectors to avoid.
706 const SectorDescriptor* temp_sectors_to_skip_[2 * kRedundancy - 1];
707
708 // KeyDescriptors for use by the KVS's EntryCache.
709 Vector<KeyDescriptor, kMaxEntries> key_descriptors_;
710
711 // An array of addresses associated with the KeyDescriptors for use with the
712 // EntryCache. To support having KeyValueStores with different redundancies,
713 // the addresses are stored in a parallel array instead of inside
714 // KeyDescriptors.
715 internal::EntryCache::AddressList<kRedundancy, kMaxEntries> addresses_;
716
717 // EntryFormats that can be read by this KeyValueStore.
718 std::array<EntryFormat, kEntryFormats> formats_;
719};
720
721} // namespace kvs
722} // 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:263
Supported iteration methods.
Definition: key_value_store.h:304
Definition: key_value_store.h:657
Definition: key_value_store.h:115
Definition: span_impl.h:235
iterator end() const
Definition: key_value_store.h:351
size_t max_key_value_size_bytes() const
Definition: key_value_store.h:407
StatusWithSize ValueSize(std::string_view key) const
size_t max_size() const
Definition: key_value_store.h:363
StatusWithSize Get(span< std::byte > value_buffer, size_t offset_bytes=0) const
Definition: key_value_store.h:270
uint32_t transaction_count() const
Definition: key_value_store.h:371
const Item * operator->()
Reads the entry into the Item object.
Definition: key_value_store.h:323
iterator operator++(int)
Increments to the next key-value entry in the container.
Definition: key_value_store.h:310
const char * key() const
Definition: key_value_store.h:266
size_t sector_erase_count
The total count of individual sector erases that have been performed.
Definition: key_value_store.h:387
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:331
const Item & operator*()
Reads the entry's key from flash.
Definition: key_value_store.h:317
size_t size() const
Definition: key_value_store.h:354
size_t corrupt_sectors_recovered
The number of corrupt sectors that have been recovered.
Definition: key_value_store.h:389
size_t redundancy() const
Definition: key_value_store.h:401
size_t writable_bytes
Definition: key_value_store.h:379
Status FullMaintenance()
Definition: key_value_store.h:247
bool error_detected() const
Definition: key_value_store.h:404
size_t empty() const
Definition: key_value_store.h:366
Status Delete(std::string_view key)
Status Put(const std::string_view &key, const T &value)
Definition: key_value_store.h:190
Status HeavyMaintenance()
Definition: key_value_store.h:236
iterator begin() const
size_t reclaimable_bytes
Definition: key_value_store.h:385
constexpr bool operator==(const iterator &rhs) const
Equality comparison of two entries.
Definition: key_value_store.h:326
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:381
Status Get(const std::string_view &key, const Pointer &pointer) const
Definition: key_value_store.h:163
StorageStats GetStorageStats() const
size_t missing_redundant_entries_recovered
Definition: key_value_store.h:392
size_t total_entries_with_deleted() const
Definition: key_value_store.h:358
static constexpr size_t max_key_value_size_bytes(size_t partition_sector_size_bytes)
Definition: key_value_store.h:413
The Pigweed namespace.
Definition: alignment.h:27
Definition: key_value_store.h:376
Definition: key_value_store.h:68