Coverage Summary for Class: MultiTrieStore (co.rsk.trie)
Class |
Class, %
|
Method, %
|
Line, %
|
MultiTrieStore |
0%
(0/1)
|
0%
(0/9)
|
0%
(0/33)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2019 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package co.rsk.trie;
20
21 import org.ethereum.util.ByteUtil;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Optional;
27
28 public class MultiTrieStore implements TrieStore {
29
30 private int currentEpoch;
31 private final List<TrieStore> epochs;
32 private final TrieStoreFactory trieStoreFactory;
33 private final OnEpochDispose disposer;
34
35 /**
36 * Creates a MultiTrieStore
37 * @param currentEpoch number (>= 0) of epoch to begin
38 * @param liveEpochs number (>= 0) of concurrent living epochs
39 * @param trieStoreFactory a trie store factory
40 * @param disposer callback for when an epoch gets disposed
41 */
42 public MultiTrieStore(int currentEpoch, int liveEpochs, TrieStoreFactory trieStoreFactory, OnEpochDispose disposer) {
43 // if currentEpoch < liveEpochs the store for it will be in the expected index in the epochs list
44 this.currentEpoch = Math.max(currentEpoch, liveEpochs);
45 this.trieStoreFactory = trieStoreFactory;
46 this.disposer = disposer;
47 this.epochs = new ArrayList<>(liveEpochs);
48 for (int i = 1; i <= liveEpochs; i++) { // starting in 1 so it's easier to calculate epoch according index
49 epochs.add(trieStoreFactory.newInstance(String.valueOf(this.currentEpoch - i)));
50 }
51 }
52
53 /**
54 * This will save to the current store <b>only</b> the new nodes b/c if there is some child in an older
55 * epoch, it'll be reached by the {@link #retrieve(byte[]) retrieve} method breaking the recursion
56 *
57 * @param trie a {@link Trie} obtained from a {@link MultiTrieStore}
58 */
59 @Override
60 public void save(Trie trie) {
61 getCurrentStore().save(trie);
62 }
63
64 /**
65 * It's not enough to just flush the current one b/c it may occur, if the epoch size doesn't match the flush size,
66 * that some epoch may never get flushed
67 */
68 @Override
69 public void flush() {
70 epochs.forEach(TrieStore::flush);
71 }
72
73
74 /**
75 * This method will go through all epochs from newest to oldest retrieving the <code>rootHash</code>
76 */
77 @Override
78 public Optional<Trie> retrieve(byte[] rootHash) {
79 for (TrieStore epochTrieStore : epochs) {
80 byte[] message = epochTrieStore.retrieveValue(rootHash);
81 if (message == null) {
82 continue;
83 }
84 return Optional.of(Trie.fromMessage(message, this));
85 }
86
87 return Optional.empty();
88 }
89
90 @Override
91 public byte[] retrieveValue(byte[] hash) {
92 for (TrieStore epochTrieStore : epochs) {
93 byte[] value = epochTrieStore.retrieveValue(hash);
94 if (value != null) {
95 return value;
96 }
97 }
98 return null;
99 }
100
101 @Override
102 public void dispose() {
103 epochs.forEach(TrieStore::dispose);
104 }
105
106 /**
107 * Discards the oldest epoch.
108 *
109 * The children of <code>oldestTrieHashToKeep</code> stored in the oldest epoch will be saved
110 * into the previous one
111 *
112 * @param oldestTrieHashToKeep a trie root hash to ensure epoch survival
113 */
114 public void collect(byte[] oldestTrieHashToKeep) {
115 Trie oldestTrieToKeep = retrieve(oldestTrieHashToKeep)
116 .orElseThrow(() ->
117 new IllegalArgumentException(String.format("The trie with root %s is missing from every epoch",
118 ByteUtil.toHexString(oldestTrieHashToKeep)
119 )));
120
121 epochs.get(epochs.size() - 2).save(oldestTrieToKeep); // save into the upcoming last epoch
122 epochs.get(epochs.size() - 1).dispose(); // dispose last epoch
123 disposer.callback(currentEpoch - epochs.size());
124 Collections.rotate(epochs, 1); // move last epoch to first place
125 epochs.set(0, trieStoreFactory.newInstance(String.valueOf(currentEpoch))); // update current epoch
126 currentEpoch++;
127 }
128
129 private TrieStore getCurrentStore() {
130 return epochs.get(0);
131 }
132
133 public interface OnEpochDispose {
134 void callback(int disposedEpoch);
135 }
136 }