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 }