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