LMDBAL 0.6.2
LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer
Loading...
Searching...
No Matches
cache.hpp
1/*
2 * LMDB Abstraction Layer.
3 * Copyright (C) 2023 Yury Gubich <blue@macaw.me>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "cache.h"
22#include "exceptions.h"
23
37
45template<class K, class V>
46LMDBAL::Cache<K, V>::Cache(Base* parent, const std::string& name, bool duplicates):
47 Storage<K, V>(parent, name, duplicates),
48 mode(Mode::nothing),
49 cache(new std::map<K, V>()),
50 abscent(new std::set<K>()),
52 transactionCache(new TransactionCache) {}
53
57namespace LMDBAL {
58template<class K, class V>
59Cache<K, V>::~Cache() {
60 delete transactionCache;
61 delete cache;
62 delete abscent;
63}
64}
65
66template<class K, class V>
67void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
69
70 if (cache->count(key) > 0)
72
73 Storage<K, V>::addRecord(key, value);
74 handleAddRecord(key, value);
75}
76
77template<class K, class V>
78void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
79 if (cache->count(key) > 0)
81
82 Storage<K, V>::addRecord(key, value, txn);
83
84 typename TransactionCache::iterator tc = transactionCache->find(txn);
85 if (tc != transactionCache->end()) {
86 std::pair<K, V>* pair = new std::pair<K, V>(key, value);
87 tc->second.emplace_back(Operation::add, pair);
88 }
89}
90
91template<class K, class V>
92void LMDBAL::Cache<K, V>::handleAddRecord(const K& key, const V& value) {
93 cache->insert(std::make_pair(key, value));
94
95 if (mode != Mode::full)
96 abscent->erase(key);
97}
98
99template<class K, class V>
100bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value) {
102
103 bool added = Storage<K, V>::forceRecord(key, value);
104 handleForceRecord(key, value, added);
105
106 return added;
107}
108
109template<class K, class V>
110bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
111 bool added = Storage<K, V>::forceRecord(key, value, txn);
112
113 typename TransactionCache::iterator tc = transactionCache->find(txn);
114 if (tc != transactionCache->end()) {
115 std::tuple<bool, K, V>* t = new std::tuple<bool, K, V>(added, key, value);
116 tc->second.emplace_back(Operation::force, t);
117 }
118
119 return added;
120}
121
122template<class K, class V>
123void LMDBAL::Cache<K, V>::handleForceRecord(const K& key, const V& value, bool added) {
124 if (mode == Mode::full) {
125 (*cache)[key] = value;
126 } else {
127 if (added)
128 abscent->erase(key);
129
130 std::pair<typename std::map<K, V>::iterator, bool> result =
131 cache->insert(std::make_pair(key, value));
132 if (!result.second)
133 result.first->second = value;
134 else if (!added) //this way database had value but cache didn't, so, need to decrease sizeDifference
135 handleMode();
136 }
137}
138
139template<class K, class V>
140void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value) {
142
143 if (mode == Mode::full) {
144 typename std::map<K, V>::iterator itr = cache->find(key);
145 if (itr == cache->end())
147
148 Storage<K, V>::changeRecord(key, value);
149 itr->second = value;
150 } else {
151 if (abscent->count(key) > 0)
153
154 try {
155 Storage<K, V>::changeRecord(key, value);
156 typename std::pair<typename std::map<K, V>::iterator, bool> res =
157 cache->insert(std::make_pair(key, value));
158 if (!res.second)
159 res.first->second = value;
160 else
161 handleMode();
162 } catch (const NotFound& error) {
163 abscent->insert(key);
164 throw error;
165 }
166 }
167}
168
169template<class K, class V>
170void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
171 if (mode == Mode::full) {
172 typename std::map<K, V>::iterator itr = cache->find(key);
173 if (itr == cache->end())
175
176 Storage<K, V>::changeRecord(key, value, txn);
177 } else {
178 if (abscent->count(key) > 0)
180
181 try {
182 Storage<K, V>::changeRecord(key, value, txn);
183 } catch (const NotFound& error) {
184 abscent->insert(key);
185 throw error;
186 }
187 }
188
189 typename TransactionCache::iterator tc = transactionCache->find(txn);
190 if (tc != transactionCache->end()) {
191 std::pair<K, V>* pair = new std::pair<K, V>(key, value);
192 tc->second.emplace_back(Operation::add, pair);
193 }
194}
195
196template<class K, class V>
197void LMDBAL::Cache<K, V>::handleChangeRecord(const K& key, const V& value) {
198 if (mode == Mode::full) {
199 cache->at(key) = value;
200 } else {
201 typename std::pair<typename std::map<K, V>::iterator, bool> res =
202 cache->insert(std::make_pair(key, value));
203 if (!res.second)
204 res.first->second = value;
205 else
206 handleMode();
207 }
208}
209
210template<class K, class V>
211V LMDBAL::Cache<K, V>::getRecord(const K& key) const {
213
214 V value;
215 Cache<K, V>::getRecord(key, value);
216 return value;
217}
218
219template<class K, class V>
220void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
222
223 typename std::map<K, V>::const_iterator itr = cache->find(key);
224 if (itr != cache->end()) {
225 out = itr->second;
226 return;
227 }
228
229 if (mode == Mode::full || abscent->count(key) != 0)
231
232 try {
233 Storage<K, V>::getRecord(key, out);
234 appendToCache(key, out);
235 return;
236 } catch (const NotFound& error) {
237 if (mode != Mode::full)
238 abscent->insert(key);
239
240 throw error;
241 }
242}
243
244template<class K, class V>
246 V value;
247 Cache<K, V>::getRecord(key, value, txn);
248 return value;
249}
250
251template<class K, class V>
252void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) const {
253 //if there are any changes made within this transaction
254 //I will be able to see them among pending changes
255 //so, I'm going to go through them in reverse order
256 //and check every key. If it has anything to do this requested key
257 //there is a way to tell...
258 bool currentTransaction = false;
259 typename TransactionCache::const_iterator tc = transactionCache->find(txn);
260 if (tc != transactionCache->end()) {
261 currentTransaction = true;
262 const Queue& queue = tc->second;
263 for (typename Queue::const_reverse_iterator i = queue.rbegin(), end = queue.rend(); i != end; ++i) {
264 const Entry& entry = *i;
265
266 switch (entry.first) {
267 case Operation::add:
268 if (static_cast<std::pair<K, V>*>(entry.second)->first == key) {
269 out = static_cast<std::pair<K, V>*>(entry.second)->second;
270 return;
271 }
272 break;
273 case Operation::remove:
275 break;
276 case Operation::change:
277 if (static_cast<std::pair<K, V>*>(entry.second)->first == key) {
278 out = static_cast<std::pair<K, V>*>(entry.second)->second;
279 return;
280 }
281
282 break;
283 case Operation::force:
284 if (std::get<1>(*static_cast<std::tuple<bool, K, V>*>(entry.second)) == key) {
285 out = std::get<2>(*static_cast<std::tuple<bool, K, V>*>(entry.second));
286 return;
287 }
288 break;
289 case Operation::drop:
291 break;
292 case Operation::replace: {
293 std::map<K, V>* newMap = static_cast<std::map<K, V>*>(entry.second);
294 typename std::map<K, V>::const_iterator vitr = newMap->find(key);
295 if (vitr != newMap->end()) {
296 out = vitr->second;
297 return;
298 } else {
300 }
301 }
302 break;
303 case Operation::addMany: {
304 const std::tuple<bool, SizeType, std::map<K, V>>& tuple =
305 *static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second);
306 const std::map<K, V>& newElements = std::get<2>(tuple);
307 typename std::map<K, V>::const_iterator vitr = newElements.find(key);
308 if (vitr != newElements.end()) {
309 out = vitr->second;
310 return;
311 }
312 }
313 break;
314 }
315 }
316 }
317 //... but if nothing was found or if the transaction is not the one
318 //which caused the changes i just need to check it among local cache
319
320 typename std::map<K, V>::const_iterator itr = cache->find(key);
321 if (itr != cache->end()) {
322 out = itr->second;
323 return;
324 }
325
326 if (mode == Mode::full || abscent->count(key) != 0)
328
329 try {
330 Storage<K, V>::getRecord(key, out, txn);
331 if (!currentTransaction)
332 appendToCache(key, out);
333
334 return;
335 } catch (const NotFound& error) {
336 if (!currentTransaction && mode != Mode::full)
337 abscent->insert(key);
338
339 throw error;
340 }
341}
342
343template<class K, class V>
344void LMDBAL::Cache<K, V>::discoveredRecord(const K& key, const V& value) const {
345 appendToCache(key, value);
346}
347
348template<class K, class V>
349void LMDBAL::Cache<K, V>::discoveredRecord(const K& key, const V& value, TransactionID txn) const {
350 typename TransactionCache::const_iterator tc = transactionCache->find(txn);
351 if (tc == transactionCache->end()) //there is a way to look though all the records in transaction log and cache the new pair
352 discoveredRecord(key, value); //if there is nothing in transaction log about it, but it seems like too much for a small gain
353}
354
355template<class K, class V>
356bool LMDBAL::Cache<K, V>::checkRecord(const K& key) const {
358
359 typename std::map<K, V>::const_iterator itr = cache->find(key);
360 if (itr != cache->end())
361 return true;
362
363 if (mode == Mode::full || abscent->count(key) != 0)
364 return false;
365
366 try {
367 V value = Storage<K, V>::getRecord(key);
368 appendToCache(key, value);
369
370 return true;
371 } catch (const NotFound& error) {
372 if (mode != Mode::full)
373 abscent->insert(key);
374
375 return false;
376 }
377}
378
379template<class K, class V>
380bool LMDBAL::Cache<K, V>::checkRecord(const K& key, TransactionID txn) const {
381 //if there are any changes made within this transaction
382 //I will be able to see them among pending changes
383 //so, I'm going to go through them in reverse order
384 //and check every key. If it has anything to do this requested key
385 //there is a way to tell...
386 bool currentTransaction = false;
387 typename TransactionCache::const_iterator tc = transactionCache->find(txn);
388 if (tc != transactionCache->end()) {
389 currentTransaction = true;
390 const Queue& queue = tc->second;
391 for (typename Queue::const_reverse_iterator i = queue.rbegin(), end = queue.rend(); i != end; ++i) {
392 const Entry& entry = *i;
393
394 switch (entry.first) {
395 case Operation::add:
396 if (static_cast<std::pair<K, V>*>(entry.second)->first == key)
397 return true;
398 break;
399 case Operation::remove:
400 if (*static_cast<K*>(entry.second) == key)
401 return false;
402 break;
403 case Operation::change:
404 if (static_cast<std::pair<K, V>*>(entry.second)->first == key)
405 return true;
406 break;
407 case Operation::force:
408 if (std::get<1>(*static_cast<std::tuple<bool, K, V>*>(entry.second)) == key)
409 return true;
410 break;
411 case Operation::drop:
412 return false;
413 break;
414 case Operation::replace:
415 if (static_cast<std::map<K, V>*>(entry.second)->count(key) > 0)
416 return true;
417 else
418 return false;
419 break;
420 case Operation::addMany:
421 if (std::get<2>(
422 *static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second)
423 ).count(key) > 0)
424 return true;
425 break;
426 }
427 }
428 }
429 //... but if nothing was found or if the transaction is not the one
430 //which caused the changes i just need to check it among local cache
431
432 typename std::map<K, V>::const_iterator itr = cache->find(key);
433 if (itr != cache->end())
434 return true;
435
436 if (mode == Mode::full || abscent->count(key) != 0)
437 return false;
438
439 try {
440 V value = Storage<K, V>::getRecord(key, txn);
441 if (!currentTransaction)
442 appendToCache(key, value);
443
444 return true;
445 } catch (const NotFound& error) {
446 if (!currentTransaction && mode != Mode::full)
447 abscent->insert(key);
448
449 return false;
450 }
451}
452
453template<class K, class V>
454void LMDBAL::Cache<K, V>::appendToCache(const K& key, const V& value) const {
455 typename std::pair<typename std::map<K, V>::const_iterator, bool> pair = cache->insert(std::make_pair(key, value));
456 if (pair.second)
457 handleMode();
458}
459
460template<class K, class V>
461std::map<K, V> LMDBAL::Cache<K, V>::readAll() const {
463
464 if (mode != Mode::full) { //there is a room for optimization
465 mode = Mode::full; //I can read and deserialize only those values
466 *cache = Storage<K, V>::readAll(); //that are missing in the cache
467 abscent->clear();
468 sizeDifference = 0;
469 }
470
471 return *cache;
472}
473
474template<class K, class V>
475void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out) const {
477
478 if (mode != Mode::full) { //there is a room for optimization
479 mode = Mode::full; //I can read and deserialize only those values
480 Storage<K, V>::readAll(out); //that are missing in the cache
481 *cache = out;
482 abscent->clear();
483 sizeDifference = 0;
484 }
485}
486
487template<class K, class V>
489 std::map<K, V> out;
490 readAll(out, txn);
491
492 return out;
493}
494
495template<class K, class V>
496void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out, TransactionID txn) const {
497 typename TransactionCache::iterator tc = transactionCache->find(txn);
498 if (tc != transactionCache->end()) {
499 Queue& queue = tc->second;
500 if (mode != Mode::full) {
501 out = *cache;
502
503 for (typename Queue::const_iterator i = queue.begin(), end = queue.end(); i != end; ++i) {
504 const Entry& entry = *i;
505 switch (entry.first) {
506 case Operation::add:
507 out.insert(*static_cast<std::pair<K, V>*>(entry.second));
508 break;
509 case Operation::remove:
510 out.erase(*static_cast<K*>(entry.second));
511 break;
512 case Operation::change: {
513 std::pair<K, V>* pair = static_cast<std::pair<K, V>*>(entry.second);
514 out.at(pair->first) = pair->second;
515 }
516 break;
517 case Operation::force:{
518 const std::tuple<bool, K, V>& tuple =
519 *static_cast<std::tuple<bool, K, V>*>(entry.second);
520 out[std::get<1>(tuple)] = std::get<2>(tuple);
521 }
522 break;
523 case Operation::drop:
524 out.clear();
525 break;
526 case Operation::replace:
527 out = *static_cast<std::map<K, V>*>(entry.second);
528 break;
529 case Operation::addMany: {
530 const std::tuple<bool, SizeType, std::map<K, V>>& t =
531 *static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second);
532 const std::map<K, V>& added = std::get<2>(t);
533 bool overwrite = std::get<0>(t);
534 for (const std::pair<const K, V>& pair : added) {
535 if (overwrite)
536 out[pair.first] = pair.second;
537 else
538 out.insert(pair);
539 }
540 }
541 break;
542 }
543 }
544 } else {
545 Storage<K, V>::readAll(out, txn);
546
547 //queue.clear(); //since I'm getting a complete state of the database
548 queue.emplace_back(Operation::replace, new std::map<K, V>(out)); //I can as well erase all previous cache entries
549 }
550 } else {
551 if (mode != Mode::full) { //there is a room for optimization
552 mode = Mode::full; //I can read and deserialize only those values
553 Storage<K, V>::readAll(out, txn); //that are missing in the cache
554 *cache = out;
555 abscent->clear();
556 sizeDifference = 0;
557 }
558 }
559}
560
561template<class K, class V>
562void LMDBAL::Cache<K, V>::replaceAll(const std::map<K, V>& data) {
564 *cache = data;
565
566 if (mode != Mode::full) {
567 mode = Mode::full;
568 abscent->clear();
569 sizeDifference = 0;
570 }
571}
572
573template<class K, class V>
574void LMDBAL::Cache<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) {
575 Storage<K, V>::replaceAll(data, txn);
576
577 typename TransactionCache::iterator tc = transactionCache->find(txn);
578 if (tc != transactionCache->end()) {
579 //queue.clear();
580 std::map<K, V>* map = new std::map<K, V>(data); //since I'm getting a complete state of the database
581 tc->second.emplace_back(Operation::replace, map); //I can as well erase all previous cache entries
582 }
583}
584
585template<class K, class V>
586void LMDBAL::Cache<K, V>::handleReplaceAll(std::map<K, V>* data) {
587 delete cache;
588 cache = data;
589
590 if (mode != Mode::full) {
591 mode = Mode::full;
592 abscent->clear();
593 sizeDifference = 0;
594 }
595}
596
597template<class K, class V>
598LMDBAL::SizeType LMDBAL::Cache<K, V>::addRecords(const std::map<K, V>& data, bool overwrite) {
599 SizeType newSize = Storage<K, V>::addRecords(data, overwrite);
600 handleAddRecords(data, overwrite, newSize);
601
602 return newSize;
603}
604
605template<class K, class V>
606LMDBAL::SizeType LMDBAL::Cache<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) {
607 SizeType newSize = Storage<K, V>::addRecords(data, txn, overwrite);
608
609 typename TransactionCache::iterator tc = transactionCache->find(txn);
610 if (tc != transactionCache->end()) {
611 std::tuple<bool, SizeType, std::map<K, V>>* tuple =
612 new std::tuple<bool, SizeType, std::map<K, V>>(overwrite, newSize, data);
613 tc->second.emplace_back(Operation::addMany, tuple);
614 }
615
616 return newSize;
617}
618
619template<class K, class V>
620void LMDBAL::Cache<K, V>::handleAddRecords(const std::map<K, V>& data, bool overwrite, SizeType newSize) {
621 if (mode == Mode::nothing)
622 mode = Mode::size;
623
624 std::map<K, V>& c = *cache;
625 std::set<K>& a = *abscent;
626 for (const std::pair<const K, V>& pair : data) {
627 std::pair<typename std::map<K, V>::iterator, bool> res = c.insert(pair);
628 if (!res.second) {
629 if (overwrite)
630 res.first->second = pair.second;
631 } else if (mode != Mode::full) {
632 a.erase(pair.first);
633 }
634 }
635
636 if (mode != Mode::full) {
637 sizeDifference = newSize - static_cast<SizeType>(c.size());
638 if (sizeDifference == 0) {
639 mode = Mode::full;
640 abscent->clear();
641 }
642 }
643}
644
645template<class K, class V>
648
649 bool noKey = false;
650 if (mode != Mode::full)
651 noKey = cache->count(key) == 0;
652 else
653 noKey = abscent->count(key) > 0;
654
655 if (noKey)
657
659 handleRemoveRecord(key);
660}
661
662template<class K, class V>
664 bool noKey = false;
665 if (mode != Mode::full)
666 noKey = cache->count(key) == 0;
667 else
668 noKey = abscent->count(key) > 0;
669
670 if (noKey)
672
674
675 typename TransactionCache::iterator tc = transactionCache->find(txn);
676 if (tc != transactionCache->end())
677 tc->second.emplace_back(Operation::remove, new K(key));
678}
679
680template<class K, class V>
681void LMDBAL::Cache<K, V>::handleRemoveRecord(const K& key) {
682 if (cache->erase(key) == 0) //if it was not cached and we are now in size mode then the sizeDifference would decrease
683 handleMode();
684
685 if (mode != Mode::full)
686 abscent->insert(key);
687}
688
689template<class K, class V>
691 size_t cacheSize = cache->size();
692 if (cacheSize > std::numeric_limits<LMDBAL::SizeType>::max())
693 throw std::overflow_error("Cache size is too big");
694
695 switch (mode) {
696 case Mode::nothing:
697 {
699 sizeDifference = sz - static_cast<SizeType>(cacheSize);
700 if (sz == 0) {
701 mode = Mode::full;
702 abscent->clear();
703 } else {
704 mode = Mode::size;
705 }
706 return sz;
707 }
708 case Mode::size:
709 return static_cast<SizeType>(cacheSize) + sizeDifference;
710 case Mode::full:
711 return static_cast<SizeType>(cacheSize);
712 default:
713 return 0; //unreachable, no such state, just to suppress the warning
714 }
715}
716
717template<class K, class V>
719 int32_t diff = 0;
720 bool currentTransaction = false;
721 typename TransactionCache::const_iterator tc = transactionCache->find(txn);
722 if (tc != transactionCache->end()) {
723 currentTransaction = true;
724 const Queue& queue = tc->second;
725 for (typename Queue::const_reverse_iterator i = queue.rbegin(), end = queue.rend(); i != end; ++i) {
726 const Entry& entry = *i;
727
728 switch (entry.first) {
729 case Operation::add:
730 ++diff;
731 break;
732 case Operation::remove:
733 --diff;
734 break;
735 case Operation::change:
736 break;
737 case Operation::force:
738 if (std::get<0>(*static_cast<std::tuple<bool, K, V>*>(entry.second)))
739 ++diff;
740 break;
741 case Operation::drop:
742 return false;
743 break;
744 case Operation::replace: {
745 auto result = static_cast<int32_t>(static_cast<std::map<K, V>*>(entry.second)->size()) + diff;
746 if (result < 0)
747 throw std::overflow_error("Invalid cache size");
748
749 return static_cast<SizeType>(result);
750 }
751 case Operation::addMany:{
752 auto result = static_cast<int32_t>(std::get<1>(*static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second))) + diff;
753 if (result < 0)
754 throw std::overflow_error("Invalid cache size");
755
756 return static_cast<SizeType>(result);
757 }
758 }
759 }
760 }
761
762 size_t cacheSize = cache->size();
763 if (cacheSize > std::numeric_limits<SizeType>::max())
764 throw std::overflow_error("Cache size is too big");
765
766 switch (mode) {
767 case Mode::nothing: {
769 if (!currentTransaction) {
770 sizeDifference = sz - static_cast<SizeType>(cacheSize);
771 if (sz == 0) {
772 mode = Mode::full;
773 abscent->clear();
774 } else {
775 mode = Mode::size;
776 }
777 }
778 return sz;
779 }
780 case Mode::size: {
781 auto result = static_cast<int32_t>(cacheSize) + static_cast<int32_t>(sizeDifference) + diff;
782
783 if (result < 0)
784 throw std::overflow_error("Invalid cache size");
785 return static_cast<SizeType>(result);
786 }
787 case Mode::full: {
788 auto result = static_cast<int32_t>(cacheSize) + diff;
789
790 if (result < 0)
791 throw std::overflow_error("Invalid cache size");
792
793 return static_cast<SizeType>(result);
794 }
795 default:
796 return 0; //unreachable, no such state, just to suppress the waring
797 }
798}
799
800template<class K, class V>
801void LMDBAL::Cache<K, V>::handleMode() const {
802 if (mode == Mode::size) {
803 --sizeDifference;
804 if (sizeDifference == 0) {
805 mode = Mode::full;
806 abscent->clear();
807 }
808 }
809}
810
811template<class K, class V>
815 int res = Storage<K, V>::drop(txn);
816
817 if (res != MDB_SUCCESS)
818 return res;
819
820 typename TransactionCache::iterator tc = transactionCache->find(txn);
821 if (tc != transactionCache->end())
822 tc->second.emplace_back(Operation::drop, nullptr);
823
824 return res;
825}
826
827template<class K, class V>
829 cache->clear();
830 abscent->clear();
831 mode = Mode::full;
832 sizeDifference = 0;
833}
834
835template<class K, class V>
837 if (!readOnly)
838 transactionCache->emplace(txn, Queue());
839}
840
841template<class K, class V>
843 typename TransactionCache::iterator itr = transactionCache->find(txn);
844 if (itr != transactionCache->end()) {
845 Queue& queue = itr->second;
846 for (const Entry& entry : queue)
847 handleTransactionEntry(entry);
848
849 transactionCache->erase(itr);
850 }
851}
852
853template<class K, class V>
855 typename TransactionCache::iterator itr = transactionCache->find(txn);
856 if (itr != transactionCache->end()) {
857 Queue& queue = itr->second;
858 for (const Entry& entry : queue)
859 destroyTransactionEntry(entry);
860
861 transactionCache->erase(itr);
862 }
863}
864
865template<class K, class V>
866void LMDBAL::Cache<K, V>::handleTransactionEntry(const Entry& entry) {
867 switch (entry.first) {
868 case Operation::add: {
869 std::pair<K, V>* pair = static_cast<std::pair<K, V>*>(entry.second);
870 handleAddRecord(pair->first, pair->second);
871 delete pair;
872 } break;
873 case Operation::remove: {
874 K* key = static_cast<K*>(entry.second);
875 handleRemoveRecord(*key);
876 delete key;
877 } break;
878 case Operation::change: {
879 std::pair<K, V>* pair = static_cast<std::pair<K, V>*>(entry.second);
880 handleChangeRecord(pair->first, pair->second);
881 delete pair;
882 } break;
883 case Operation::force: {
884 std::tuple<bool, K, V>* tuple = static_cast<std::tuple<bool, K, V>*>(entry.second);
885 const std::tuple<bool, K, V>& t = *tuple;
886 handleForceRecord(std::get<1>(t), std::get<2>(t), std::get<0>(t));
887 delete tuple;
888 } break;
889 case Operation::drop:
890 handleDrop();
891 break;
892 case Operation::replace:
893 handleReplaceAll(static_cast<std::map<K, V>*>(entry.second)); //I take ownership, no need to delete
894 break;
895 case Operation::addMany: {
896 std::tuple<bool, SizeType, std::map<K, V>>* tuple = static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second);
897 const std::tuple<bool, SizeType, std::map<K, V>>& t = * tuple;
898 handleAddRecords(std::get<2>(t), std::get<0>(t), std::get<1>(t));
899 delete tuple;
900 } break;
901 }
902}
903
904template<class K, class V>
905void LMDBAL::Cache<K, V>::destroyTransactionEntry(const Entry& entry) const {
906 switch (entry.first) {
907 case Operation::add:
908 delete static_cast<std::pair<K, V>*>(entry.second);
909 break;
910 case Operation::remove:
911 delete static_cast<K*>(entry.second);
912 break;
913 case Operation::change:
914 delete static_cast<std::pair<K, V>*>(entry.second);
915 break;
916 case Operation::force:
917 delete static_cast<std::tuple<bool, K, V>*>(entry.second);
918 break;
919 case Operation::drop:
920 break;
921 case Operation::replace:
922 delete static_cast<std::map<K, V>*>(entry.second);
923 break;
924 case Operation::addMany:
925 delete static_cast<std::tuple<bool, SizeType, std::map<K, V>>*>(entry.second);
926 break;
927 }
928}
SizeType sizeDifference
Difference of size between cached data and amount of records in the lmdb storage.
Definition cache.h:129
Mode mode
Cache mode.
Definition cache.h:126
virtual bool forceRecord(const K &key, const V &value, TransactionID txn) override
Adds a key-value record to the storage, overwrites if it already exists (private transaction variant)...
Definition cache.hpp:110
virtual std::map< K, V > readAll(TransactionID txn) const override
Reads whole storage into a map (private transaction variant).
Definition cache.hpp:488
virtual void transactionAborted(TransactionID txn) const override
called on abortion of public transaction
Definition cache.hpp:854
virtual std::map< K, V > readAll() const override
Reads whole storage into a map.
Definition cache.hpp:461
virtual bool checkRecord(const K &key, TransactionID txn) const override
Chechs if storage has value (private transaction variant).
Definition cache.hpp:380
virtual void discoveredRecord(const K &key, const V &value) const override
A private virtual method that cursor calls when he reads a record, does nothing here but populates th...
Definition cache.hpp:344
std::map< K, V > * cache
Cached data.
Definition cache.h:127
Cache(Base *parent, const std::string &name, bool duplicates=false)
Creates a cache.
Definition cache.hpp:46
TransactionCache * transactionCache
All changes made under under uncommited transactions.
Definition cache.h:130
virtual void replaceAll(const std::map< K, V > &data, TransactionID txn) override
Replaces the content of the whole storage with the given (private transaction variant).
Definition cache.hpp:574
virtual SizeType count() const override
Storage size.
Definition cache.hpp:690
virtual void transactionCommited(TransactionID txn) override
called on commitment of public transaction
Definition cache.hpp:842
virtual void changeRecord(const K &key, const V &value, TransactionID txn) override
Changes key-value record to the storage (private transaction variant).
Definition cache.hpp:170
virtual SizeType addRecords(const std::map< K, V > &data, TransactionID txn, bool overwrite=false) override
Adds records in bulk (private transaction variant).
Definition cache.hpp:606
virtual void getRecord(const K &key, V &out, TransactionID txn) const override
Gets the record from the database (private transaction, reference variant).
Definition cache.hpp:252
virtual void transactionStarted(TransactionID txn, bool readOnly) const override
called on beginning of public transaction
Definition cache.hpp:836
virtual void handleDrop() override
A method where database additionally handles drop.
Definition cache.hpp:828
virtual void removeRecord(const K &key, TransactionID txn) override
Removes one of the records (private transaction variant).
Definition cache.hpp:663
std::set< K > * abscent
Set of keys that are definitely not in the cache.
Definition cache.h:128
virtual void addRecord(const K &key, const V &value, TransactionID txn) override
Adds a key-value record to the storage (private transaction variant).
Definition cache.hpp:78
Thrown if something in the database was not found.
Definition exceptions.h:135
void throwNotFound(std::string_view key) const
Throws LMDBAL::NotFound.
Definition storagecommon.cpp:406
static constexpr std::string_view forceRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:120
static constexpr std::string_view removeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:122
static std::string toString(const T &value)
A method to cast a value (which can be a value or a key) to string.
Definition storagecommon.hpp:71
static constexpr std::string_view addRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:119
virtual SizeType count() const
Storage size.
Definition storagecommon.cpp:148
static constexpr std::string_view getRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:124
static constexpr std::string_view changeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:121
const std::string name
this storage name
Definition storagecommon.h:112
TransactionID extractTransactionId(const Transaction &txn, std::string_view action={}) const
Checks if the transaction is still active, returns inner TransactionID.
Definition storagecommon.cpp:72
static constexpr std::string_view readAllMethodName
member function name, just for exceptions
Definition storagecommon.h:125
static constexpr std::string_view checkRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:123
void ensureOpened(std::string_view methodName) const
Helper function; throws an exception if the database is not opened.
Definition storagecommon.cpp:135
const bool duplicates
true if storage supports duplicates
Definition storagecommon.h:113
virtual void drop()
Drops content of a storage interface.
Definition storagecommon.cpp:87
void throwDuplicate(std::string_view key) const
Throws LMDBAL::Exist.
Definition storagecommon.cpp:394
static constexpr std::string_view dropMethodName
member function name, just for exceptions
Definition storagecommon.h:115
virtual void addRecord(const K &key, const V &value, TransactionID txn)
Adds a key-value record to the storage (private transaction variant).
Definition storage.hpp:109
virtual std::map< K, V > readAll() const
Reads whole storage into a map.
Definition storage.hpp:640
virtual void replaceAll(const std::map< K, V > &data, TransactionID txn)
Replaces the content of the whole storage with the given (private transaction variant).
Definition storage.hpp:811
virtual void removeRecord(const K &key, TransactionID txn)
Removes one of the records (private transaction variant).
Definition storage.hpp:976
virtual uint32_t addRecords(const std::map< K, V > &data, TransactionID txn, bool overwrite=false)
Adds records in bulk (private transaction variant).
Definition storage.hpp:889
virtual bool forceRecord(const K &key, const V &value, TransactionID txn)
Adds a key-value record to the storage, overwrites if it already exists (private transaction variant)...
Definition storage.hpp:200
virtual void changeRecord(const K &key, const V &value, TransactionID txn)
Changes key-value record to the storage (private transaction variant).
Definition storage.hpp:316
virtual void getRecord(const K &key, V &value, TransactionID txn) const
Gets the record from the database (private transaction, reference variant).
Definition storage.hpp:517
Storage(Base *parent, const std::string &name, bool duplicates=false)
Creates a storage.
Definition storage.hpp:48
virtual void drop()
Drops content of a storage interface.
Definition storagecommon.cpp:87
Public writable transaction.
Definition transaction.h:54
Destroys a cache.
Definition base.h:36
MDB_txn * TransactionID
I'm going to use transaction pointers as transaction IDs.
Definition base.h:52
uint32_t SizeType
All LMDBAL sizes are uint32.
Definition base.h:53