-
Notifications
You must be signed in to change notification settings - Fork 258
Expand file tree
/
Copy pathstore.h
More file actions
1364 lines (1166 loc) · 37.7 KB
/
Copy pathstore.h
File metadata and controls
1364 lines (1166 loc) · 37.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "apply_changes.h"
#include "ccf/kv/read_only_store.h"
#include "ccf/pal/locking.h"
#include "deserialise.h"
#include "ds/internal_logger.h"
#include "kv/committable_tx.h"
#include "kv/ledger_chunker_interface.h"
#include "kv/snapshot.h"
#include "kv/untyped_map.h"
#include "kv_serialiser.h"
#include "kv_types.h"
#define FMT_HEADER_ONLY
#include <atomic>
#include <fmt/format.h>
#include <memory>
namespace ccf::kv
{
class StoreState
{
protected:
// All collections of Map must be ordered so that we lock their contained
// maps in a stable order. The order here is by map name. The version
// indicates the version at which the Map was created.
using Maps = std::map<
std::string,
std::pair<ccf::kv::Version, std::shared_ptr<untyped::Map>>>;
ccf::pal::Mutex maps_lock;
Maps maps;
ccf::pal::Mutex version_lock;
std::atomic<Version> version = 0;
Version last_new_map = ccf::kv::NoVersion;
std::atomic<Version> compacted = 0;
// Calls to Store::commit are made atomic by taking this lock.
ccf::pal::Mutex commit_lock;
// Term at which write future transactions should be committed.
std::atomic<Term> term_of_next_version = 0;
// Term at which the last entry was committed. Further transactions
// should read in that term. Note that it is assumed that the history of
// terms of past transactions is kept track of by and specified by the
// caller on rollback
Term term_of_last_version = 0;
Version last_replicated = 0;
// Version of the latest committable entry committed in this term and by
// _this_ store.
Version last_committable = 0;
Version rollback_count = 0;
std::unordered_map<Version, std::tuple<std::unique_ptr<PendingTx>, bool>>
pending_txs;
public:
void clear()
{
std::scoped_lock<ccf::pal::Mutex, ccf::pal::Mutex> mguard(
maps_lock, version_lock);
maps.clear();
pending_txs.clear();
version = 0;
last_new_map = ccf::kv::NoVersion;
compacted = 0;
term_of_next_version = 0;
term_of_last_version = 0;
last_replicated = 0;
last_committable = 0;
rollback_count = 0;
}
};
class Store : public AbstractStore,
public StoreState,
public ExecutionWrapperStore,
public ReadOnlyStore
{
private:
using Hooks = std::map<std::string, ccf::kv::untyped::Map::CommitHook>;
using MapHooks = std::map<std::string, ccf::kv::untyped::Map::MapHook>;
Hooks global_hooks;
MapHooks map_hooks;
std::shared_ptr<Consensus> consensus = nullptr;
std::shared_ptr<TxHistory> history = nullptr;
std::shared_ptr<ILedgerChunker> chunker = nullptr;
EncryptorPtr encryptor = nullptr;
SnapshotterPtr snapshotter = nullptr;
// Generally we will only accept deserialised views if they are contiguous -
// at Version N we reject everything but N+1. The exception is when a Store
// is used for historical queries, where it may deserialise arbitrary
// transactions. In this case the Store is a useful container for a set of
// Tables, but its versioning invariants are ignored.
const bool strict_versions = true;
// If true, use historical ledger secrets to deserialise entries
const bool is_historical = false;
// Store-level flags (AbstractStore::StoreFlag) influencing behaviour such
// as snapshot and ledger chunk decisions. Atomic because _unsafe accessors
// may be called from different threads without a common lock.
std::atomic<uint8_t> flags = 0;
bool commit_deserialised(
OrderedChanges& changes,
Version v,
Term term,
const MapCollection& new_maps,
ccf::kv::ConsensusHookPtrs& hooks,
bool track_deletes_on_missing_keys) override
{
auto c = apply_changes(
changes,
[v](bool) { return std::make_tuple(v, v - 1); },
hooks,
new_maps,
std::nullopt,
track_deletes_on_missing_keys);
if (!c.has_value())
{
LOG_FAIL_FMT("Failed to commit deserialised Tx at version {}", v);
return false;
}
{
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
version = v;
last_replicated = version;
term_of_last_version = term;
}
return true;
}
bool has_map_internal(const std::string& name)
{
return maps.contains(name);
}
Version next_version_unsafe()
{
// Get the next global version
++version;
// Version was previously signed, with negative values representing
// deletions. Maintain this restriction for compatibility with old code.
if (version > std::numeric_limits<int64_t>::max())
{
LOG_FAIL_FMT("KV version too large - wrapping to 0");
version = 0;
}
// Further transactions should read in the commit term
term_of_last_version = term_of_next_version;
return version;
}
TxID current_txid_unsafe()
{
// version_lock should be first acquired
return {term_of_last_version, version};
}
public:
Store(bool strict_versions_ = true, bool is_historical_ = false) :
strict_versions(strict_versions_),
is_historical(is_historical_)
{}
Store(const Store& that) = delete;
std::shared_ptr<Consensus> get_consensus() override
{
// We need to use std::atomic_load<std::shared_ptr<T>>
// after clang supports it.
// https://en.cppreference.com/w/Template:cpp/compiler_support/20
return std::atomic_load(&consensus);
}
void set_consensus(const std::shared_ptr<Consensus>& consensus_)
{
std::atomic_store(&consensus, consensus_);
}
std::shared_ptr<TxHistory> get_history() override
{
return history;
}
void set_history(const std::shared_ptr<TxHistory>& history_)
{
history = history_;
}
std::shared_ptr<ILedgerChunker> get_chunker() override
{
return chunker;
}
void set_chunker(const std::shared_ptr<ILedgerChunker>& chunker_)
{
chunker = chunker_;
}
void set_encryptor(const EncryptorPtr& encryptor_)
{
encryptor = encryptor_;
}
EncryptorPtr get_encryptor() override
{
return encryptor;
}
void set_snapshotter(const SnapshotterPtr& snapshotter_)
{
snapshotter = snapshotter_;
}
/** Get a map by name, iff it exists at the given version.
*
* This means a prior transaction must have created the map, and
* successfully committed at a version <= v. If this has not happened (the
* map has never been created, or never been committed, or committed at a
* later version) this will return nullptr.
*
* @param v Version at which the map must exist
* @param map_name Name of requested map
*
* @return Abstract shared-owning pointer to requested map, or nullptr if no
* such map exists
*/
std::shared_ptr<AbstractMap> get_map(
ccf::kv::Version v, const std::string& map_name) override
{
std::lock_guard<ccf::pal::Mutex> mguard(maps_lock);
return get_map_internal(v, map_name);
}
std::shared_ptr<AbstractMap> get_map_unsafe(
ccf::kv::Version v, const std::string& map_name) override
{
return get_map_internal(v, map_name);
}
std::shared_ptr<ccf::kv::untyped::Map> get_map_internal(
ccf::kv::Version v, const std::string& map_name)
{
auto search = maps.find(map_name);
if (search != maps.end())
{
const auto& [map_creation_version, map_ptr] = search->second;
if (v >= map_creation_version || map_creation_version == NoVersion)
{
return map_ptr;
}
}
return nullptr;
}
/** Transfer ownership of a dynamically created map to this Store.
*
* Should be called as part of the commit process, once a transaction is
* known to be conflict-free and has been assigned a unique Version. This
* publishes dynamically created Maps so they can be retrieved via get_map
* in future transactions.
*
* @param v Version at which map is being committed/created
* @param map_ Map to add
*/
void add_dynamic_map(
ccf::kv::Version v, const std::shared_ptr<AbstractMap>& map_) override
{
auto map = std::dynamic_pointer_cast<ccf::kv::untyped::Map>(map_);
if (map == nullptr)
{
throw std::logic_error(fmt::format(
"Can't add dynamic map - {} is not of expected type",
map_->get_name()));
}
const auto map_name = map->get_name();
if (get_map_unsafe(v, map_name) != nullptr)
{
throw std::logic_error(fmt::format(
"Can't add dynamic map - already have a map named {}", map_name));
}
LOG_DEBUG_FMT("Adding newly created map '{}' at version {}", map_name, v);
maps[map_name] = std::make_pair(v, map);
{
// If we have any hooks for the given map name, set them on this new map
const auto global_it = global_hooks.find(map_name);
if (global_it != global_hooks.end())
{
map->set_global_hook(global_it->second);
}
const auto map_it = map_hooks.find(map_name);
if (map_it != map_hooks.end())
{
map->set_map_hook(map_it->second);
}
}
}
std::unique_ptr<AbstractSnapshot> snapshot_unsafe_maps(Version v) override
{
auto cv = compacted_version();
if (v < cv)
{
throw std::logic_error(fmt::format(
"Cannot snapshot at version {} which is earlier than last "
"compacted version {} ",
v,
cv));
}
if (v > current_version())
{
throw std::logic_error(fmt::format(
"Cannot snapshot at version {} which is later than current "
"version {} ",
v,
current_version()));
}
auto snapshot = std::make_unique<StoreSnapshot>(v);
{
for (auto& it : maps)
{
auto& [_, map] = it.second;
snapshot->add_map_snapshot(map->snapshot(v));
}
auto h = get_history();
if (h)
{
snapshot->add_hash_at_snapshot(h->get_raw_leaf(v));
}
auto c = get_consensus();
if (c)
{
snapshot->add_view_history(c->get_view_history(v));
}
}
return snapshot;
}
void lock_maps() override
{
maps_lock.lock();
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->lock();
}
}
void unlock_maps() override
{
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->unlock();
}
maps_lock.unlock();
}
std::vector<uint8_t> serialise_snapshot(
std::unique_ptr<AbstractSnapshot> snapshot) override
{
auto e = get_encryptor();
return snapshot->serialise(e);
}
ApplyResult deserialise_snapshot(
const uint8_t* data,
size_t size,
ccf::kv::ConsensusHookPtrs& hooks,
std::vector<Version>* view_history = nullptr,
bool public_only = false) override
{
auto e = get_encryptor();
auto d = RawKvStoreDeserialiser(
e,
public_only ? ccf::kv::SecurityDomain::PUBLIC :
std::optional<ccf::kv::SecurityDomain>());
ccf::kv::Term term = 0;
ccf::kv::EntryFlags entry_flags = {};
auto v_ = d.init(data, size, term, entry_flags, is_historical);
if (!v_.has_value())
{
LOG_FAIL_FMT("Initialisation of deserialise object failed");
return ApplyResult::FAIL;
}
auto v = v_.value();
std::shared_ptr<TxHistory> h = nullptr;
std::vector<uint8_t> hash_at_snapshot;
std::vector<Version> view_history_;
{
std::lock_guard<ccf::pal::Mutex> mguard(maps_lock);
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->lock();
}
h = get_history();
if (h)
{
hash_at_snapshot = d.deserialise_raw();
}
if (view_history != nullptr)
{
view_history_ = d.deserialise_view_history();
}
OrderedChanges changes;
MapCollection new_maps;
for (auto r = d.start_map(); r.has_value(); r = d.start_map())
{
const auto map_name = r.value();
std::shared_ptr<ccf::kv::untyped::Map> map = nullptr;
auto search = maps.find(map_name);
if (search == maps.end())
{
map = std::make_shared<ccf::kv::untyped::Map>(
this, map_name, get_security_domain(map_name));
new_maps[map_name] = map;
LOG_DEBUG_FMT(
"Creating map {} while deserialising snapshot at version {}",
map_name,
v);
}
else
{
map = search->second.second;
}
auto changes_search = changes.find(map_name);
if (changes_search != changes.end())
{
LOG_FAIL_FMT("Failed to deserialise snapshot at version {}", v);
LOG_DEBUG_FMT("Multiple writes on map {}", map_name);
return ApplyResult::FAIL;
}
auto deserialised_snapshot_changes =
map->deserialise_snapshot_changes(d);
// Take ownership of the produced change set, store it to be committed
// later
changes.emplace_hint(
changes_search,
std::piecewise_construct,
std::forward_as_tuple(map_name),
std::forward_as_tuple(
map, std::move(deserialised_snapshot_changes)));
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->unlock();
}
if (!d.end())
{
LOG_FAIL_FMT("Unexpected content in snapshot at version {}", v);
return ApplyResult::FAIL;
}
// Each map is committed at a different version, independently of the
// overall snapshot version. The commit versions for each map are
// contained in the snapshot and applied when the snapshot is committed.
bool track_deletes_on_missing_keys = false;
auto r = apply_changes(
changes,
[](bool) { return std::make_tuple(NoVersion, NoVersion); },
hooks,
new_maps,
std::nullopt,
false,
track_deletes_on_missing_keys);
if (!r.has_value())
{
LOG_FAIL_FMT(
"Failed to commit deserialised snapshot at version {}", v);
return ApplyResult::FAIL;
}
{
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
version = v;
last_replicated = v;
}
}
if (h)
{
if (!h->init_from_snapshot(hash_at_snapshot))
{
return ApplyResult::FAIL;
}
}
if (view_history != nullptr)
{
*view_history = std::move(view_history_);
}
return ApplyResult::PASS;
}
void compact(Version v) override
{
// This is called when the store will never be rolled back to any
// state before the specified version.
// No transactions can be prepared or committed during compaction.
if (snapshotter)
{
auto c = get_consensus();
bool generate_snapshot = c && c->is_primary();
snapshotter->commit(v, generate_snapshot);
}
if (chunker)
{
chunker->compacted_to(v);
}
std::lock_guard<ccf::pal::Mutex> mguard(maps_lock);
if (v > current_version())
{
return;
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->lock();
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->compact(v);
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->unlock();
}
{
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
compacted = v;
auto h = get_history();
if (h)
{
h->compact(v);
}
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->post_compact();
}
}
void rollback(const TxID& tx_id, Term term_of_next_version_) override
{
// This is called to roll the store back to the state it was in
// at the specified version.
// No transactions can be prepared or committed during rollback.
if (snapshotter)
{
snapshotter->rollback(tx_id.seqno);
}
if (chunker)
{
chunker->rolled_back_to(tx_id.seqno);
}
std::lock_guard<ccf::pal::Mutex> mguard(maps_lock);
{
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
if (tx_id.seqno < compacted)
{
throw std::logic_error(fmt::format(
"Attempting rollback to {}, earlier than commit version {}",
tx_id.seqno,
compacted));
}
// The term should always be updated on rollback() when passed
// regardless of whether version needs to be updated or not
term_of_next_version = term_of_next_version_;
term_of_last_version = tx_id.view;
// History must be informed of the term_of_last_version change, even if
// no actual rollback is required
auto h = get_history();
if (h)
{
h->rollback(tx_id, term_of_next_version);
}
if (tx_id.seqno >= version)
{
return;
}
version = tx_id.seqno;
last_replicated = tx_id.seqno;
// In practice rollback is only called at signature seqnos, so
// clamping here restores the latest committable entry
last_committable = std::min(last_committable, tx_id.seqno);
unset_flag_unsafe(StoreFlag::SNAPSHOT_AT_NEXT_SIGNATURE);
rollback_count++;
pending_txs.clear();
auto e = get_encryptor();
if (e)
{
e->rollback(tx_id.seqno);
}
}
for (auto& it : maps)
{
auto& [_, map] = it.second;
map->lock();
}
auto it = maps.begin();
while (it != maps.end())
{
auto& [map_creation_version, map] = it->second;
// Rollback this map whether we're forgetting about it or not. Anyone
// else still holding it should see it has rolled back
map->rollback(tx_id.seqno);
if (map_creation_version > tx_id.seqno)
{
// Map was created more recently; its creation is being forgotten.
// Erase our knowledge of it
map->unlock();
it = maps.erase(it);
}
else
{
++it;
}
}
for (auto& map_it : maps)
{
auto& [_, map] = map_it.second;
map->unlock();
}
}
void initialise_term(Term t) override
{
// Note: This should only be called once, when the store is first
// initialised. term_of_next_version is later updated via rollback.
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
if (term_of_next_version != 0)
{
throw std::logic_error("term_of_next_version is already initialised");
}
term_of_next_version = t;
auto h = get_history();
if (h)
{
h->set_term(term_of_next_version);
}
}
bool fill_maps(
const std::vector<uint8_t>& data,
bool public_only,
ccf::kv::Version& v,
ccf::kv::Term& view,
ccf::kv::EntryFlags& entry_flags,
OrderedChanges& changes,
MapCollection& new_maps,
ccf::ClaimsDigest& claims_digest,
std::optional<ccf::crypto::Sha256Hash>& commit_evidence_digest,
bool ignore_strict_versions = false) override
{
// This will return FAILED if the serialised transaction is being
// applied out of order.
// Processing transactions locally and also deserialising to the
// same store will result in a store version mismatch and
// deserialisation will then fail.
auto e = get_encryptor();
auto d = RawKvStoreDeserialiser(
e,
public_only ? ccf::kv::SecurityDomain::PUBLIC :
std::optional<ccf::kv::SecurityDomain>());
auto v_ =
d.init(data.data(), data.size(), view, entry_flags, is_historical);
if (!v_.has_value())
{
LOG_FAIL_FMT("Initialisation of deserialise object failed");
return false;
}
v = v_.value();
claims_digest = std::move(d.consume_claims_digest());
LOG_TRACE_FMT(
"Deserialised claim digest {} {}",
claims_digest.value(),
claims_digest.empty());
commit_evidence_digest = std::move(d.consume_commit_evidence_digest());
if (commit_evidence_digest.has_value())
{
LOG_TRACE_FMT(
"Deserialised commit evidence digest {}",
commit_evidence_digest.value());
}
// Throw away any local commits that have not propagated via the
// consensus.
rollback({term_of_last_version, v - 1}, term_of_next_version);
if (strict_versions && !ignore_strict_versions)
{
// Make sure this is the next transaction.
auto cv = current_version();
if (cv != (v - 1))
{
LOG_FAIL_FMT(
"Tried to deserialise {} but current_version is {}", v, cv);
return false;
}
}
// Deserialised transactions express read dependencies as versions,
// rather than with the actual value read. As a result, they don't
// need snapshot isolation on the map state, and so do not need to
// lock each of the maps before creating the transaction.
std::lock_guard<ccf::pal::Mutex> mguard(maps_lock);
for (auto r = d.start_map(); r.has_value(); r = d.start_map())
{
const auto map_name = r.value();
auto map = get_map_internal(v, map_name);
if (map == nullptr)
{
auto new_map = std::make_shared<ccf::kv::untyped::Map>(
this, map_name, get_security_domain(map_name));
map = new_map;
new_maps[map_name] = new_map;
LOG_DEBUG_FMT(
"Creating map '{}' while deserialising transaction at version {}",
map_name,
v);
}
auto change_search = changes.find(map_name);
if (change_search != changes.end())
{
LOG_FAIL_FMT("Failed to deserialise transaction at version {}", v);
LOG_DEBUG_FMT("Multiple writes on map {}", map_name);
return false;
}
auto deserialised_changes = map->deserialise_changes(d, v);
// Take ownership of the produced change set, store it to be applied
// later
changes.emplace_hint(
change_search,
std::piecewise_construct,
std::forward_as_tuple(map_name),
std::forward_as_tuple(map, std::move(deserialised_changes)));
}
if (!d.end())
{
LOG_FAIL_FMT("Unexpected content in transaction at version {}", v);
return false;
}
return true;
}
std::unique_ptr<ccf::kv::AbstractExecutionWrapper> deserialize(
const std::vector<uint8_t>& data,
bool public_only = false,
const std::optional<TxID>& expected_txid = std::nullopt) override
{
auto exec = std::make_unique<CFTExecutionWrapper>(
this, get_history(), get_chunker(), data, public_only, expected_txid);
return exec;
}
bool operator==(const Store& that) const
{
// Only used for debugging, not thread safe.
if (version != that.version)
{
return false;
}
if (maps.size() != that.maps.size())
{
return false;
}
return std::ranges::all_of(maps, [&that](const auto& entry) {
const auto& [map_name, map_pair] = entry;
auto search = that.maps.find(map_name);
if (search == that.maps.end())
{
return false;
}
const auto& [this_v, this_map] = map_pair;
const auto& [that_v, that_map] = search->second;
if (this_v != that_v)
{
return false;
}
if (*this_map != *that_map)
{
return false;
}
return true;
});
}
Version current_version() override
{
return version;
}
ccf::TxID current_txid() override
{
// Must lock in case the version or read term is being incremented.
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
return current_txid_unsafe();
}
std::pair<TxID, Term> current_txid_and_commit_term() override
{
// Must lock in case the version or commit term is being incremented.
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
return {current_txid_unsafe(), term_of_next_version};
}
Version compacted_version() override
{
return compacted;
}
Term commit_view() override
{
// Must lock in case the commit_view is being incremented.
return term_of_next_version;
}
CommitResult commit(
const TxID& txid,
std::unique_ptr<PendingTx> pending_tx,
bool globally_committable) override
{
auto c = get_consensus();
if (!c)
{
return CommitResult::SUCCESS;
}
std::lock_guard<ccf::pal::Mutex> cguard(commit_lock);
LOG_DEBUG_FMT(
"Store::commit {}{}",
txid.seqno,
(globally_committable ? " globally_committable" : ""));
BatchVector batch;
Version previous_last_replicated = 0;
Version next_last_replicated = 0;
Version previous_rollback_count = 0;
ccf::View replication_view = 0;
std::vector<std::tuple<std::unique_ptr<PendingTx>, bool>>
contiguous_pending_txs;
auto h = get_history();
{
std::lock_guard<ccf::pal::Mutex> vguard(version_lock);
if (txid.view != term_of_next_version && get_consensus()->is_primary())
{
// This can happen when a transaction started before a view change,
// but tries to commit after the view change is complete.
LOG_DEBUG_FMT(
"Want to commit for term {} but term is {}",
txid.view,
term_of_next_version);
return CommitResult::FAIL_NO_REPLICATE;
}
if (globally_committable && txid.seqno > last_committable)
{
last_committable = txid.seqno;
}
pending_txs.insert(
{txid.seqno,
std::make_tuple(std::move(pending_tx), globally_committable)});
LOG_TRACE_FMT("Inserting pending tx at {}", txid.seqno);
for (Version offset = 1; true; ++offset)
{
auto search = pending_txs.find(last_replicated + offset);
if (search == pending_txs.end())
{
LOG_TRACE_FMT(
"Couldn't find {} = {} + {}, giving up on batch while committing "
"{}.{}",
last_replicated + offset,
last_replicated,
offset,
txid.view,
txid.seqno);
break;
}
contiguous_pending_txs.emplace_back(std::move(search->second));
pending_txs.erase(search);
}
previous_rollback_count = rollback_count;
previous_last_replicated = last_replicated;
next_last_replicated = last_replicated + contiguous_pending_txs.size();
replication_view = term_of_next_version;
}
// Release version lock
if (contiguous_pending_txs.empty())
{
return CommitResult::SUCCESS;
}
size_t offset = 1;
for (auto& [pending_tx_, committable_] : contiguous_pending_txs)
{
auto
[success_, data_, claims_digest_, commit_evidence_digest_, hooks_] =
pending_tx_->call();
auto data_shared =
std::make_shared<std::vector<uint8_t>>(std::move(data_));
auto hooks_shared =
std::make_shared<ccf::kv::ConsensusHookPtrs>(std::move(hooks_));
// A pending tx may fail here if rollback invalidated a reserved