/**
* Copyright 2013 Oak Ridge National Laboratory
* Author: James Horey <horeyjl@ornl.gov>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package gov.ornl.keva.table;
/**
* Java libs.
**/
import java.util.Collection;
import java.util.Map;
import java.util.NavigableMap;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Iterator;
/**
* Keva libs.
**/
import gov.ornl.keva.core.VectorClock;
import gov.ornl.keva.core.StreamIterator;
import gov.ornl.keva.core.PruneOptions;
/**
* A non-thread-safe bucket implementation used by the sstable
* readers. When reading from an sstable we know that there is
* only a single writer.
*
* @author James Horey
*/
public class UnsafeBucket extends TableBucket {
private volatile int num = 0;
/**
* Store the actual value histories, and also a separate branch name
* index. Both of these datastructures must be thread safe since we may
* perform concurrent iterations while items are added.
**/
private final NavigableMap<String, TableValueHistory> entries =
new TreeMap<>();
/**
* @param comp Compare independent table values
*/
public UnsafeBucket() {
super();
}
/**
* Commit the value identified by vector clock and branch.
* Not implemented.
*
* @param value Table value to commit
* @param branch Specific branch where the value resides (optional)
*/
@Override public boolean commit(final TableValue value,
final String branch) {
return false;
}
/**
* Add a new value to this bucket to a specific branch.
* This means that the vector clock associated with the
* value should be appended to the branch vector clock.
*
* @param value Table value to add to bucket
* @param branch Specific branch where the value resides (optional)
*/
@Override public void add(final TableValue value,
final String branch) {
// Find the latest vector clock associated with the branch,
// and then the latest value history.
TableValueHistory history = null;
history = entries.get(branch);
if(history == null) {
if(value.getFlags() != TableValue.TENTATIVE) {
// This must be a new branch.
history = new UnsafeTableValueHistory();
history.setBranchName(branch);
entries.put(branch, history);
}
}
// Add the value to our history.
history.add(value);
// Increase our committed count
if(value.getFlags() != TableValue.TENTATIVE) {
num++;
}
}
/**
* Add a new value to this bucket.
* This method is not implemented.
*
* @param value Table value to add to bucket
*/
@Override public void add(final TableValue value) {
// Not implemented.
}
/**
* Bulk add the values supplied into this bucket.
* This method is not implemented.
*
* @param values Tables values to add to bucket
*/
@Override public void addAll(final Collection<TableValue> values) {
// Not implemented.
}
/**
* Used to iterate over all the values across all the branches.
*
* @param pruneOptions Options to restrict viewing particular values (e.g., deletes)
* @return Iterator over table values
*/
@Override public Map<String,StreamIterator<TableValue>> getComplete(final PruneOptions pruneOptions) {
Map<String,StreamIterator<TableValue>> fm =
new HashMap<>();
for(String b : entries.keySet()) {
fm.putAll(getUncollapsed(b, pruneOptions));
}
return fm;
}
/**
* Return the historical list of values from the supplied branch name.
*
* @param branch Specific branch where the value resides
* @param pruneOptions Options to restrict viewing particular values (e.g., deletes)
* @return Iterator over table values
*/
@Override public Map<String,StreamIterator<TableValue>> getUncollapsed(final String branch,
final PruneOptions pruneOptions) {
Map<String,StreamIterator<TableValue>> fm =
new HashMap<>();
if(pruneOptions != null) {
pruneOptions.delete =
hasDeleteOp &&
pruneOptions.delete;
}
TableValueHistory history = entries.get(branch);
if(history != null) {
TableValueHistory pruned =
new UnsafeTableValueHistory(history, pruneOptions);
fm.put(branch, pruned.iterator());
}
return fm;
}
/**
* Iterate over the set of independent values. Each independent value
* is collapsed so that only the latest value is returned.
*
* @return Iterator over table values
*/
@Override public Map<String,StreamIterator<TableValue>> getCollapsed() {
Map<String,StreamIterator<TableValue>> fm =
new HashMap<>();
for(String branch : entries.keySet()) {
TableValueHistory history = entries.get(branch);
TableValue v = history.getCollapsedValue();
TableValueHistory singleton = new UnsafeTableValueHistory();
singleton.add(v);
fm.put(branch, singleton.iterator());
}
return fm;
}
/**
* Return the latest value associated with this branch.
*
* @param branch Specific branch where the value resides
* @return Table value
*/
@Override public Map<String,StreamIterator<TableValue>> getCollapsed(final String branch) {
Map<String,StreamIterator<TableValue>> fm =
new HashMap<>(1);
TableValueHistory history = entries.get(branch);
if(history != null) {
TableValueHistory singleton = new UnsafeTableValueHistory();
TableValue v = history.getCollapsedValue();
singleton.add(v);
fm.put(branch, singleton.iterator());
}
return fm;
}
/**
* Return the values that were recorded before or during the supplied time.
*
* @param time Last time recorded
* @return Iterator over table values
*/
@Override public Map<String,StreamIterator<TableValue>> getCollapsed(final long time) {
Map<String,StreamIterator<TableValue>> fm =
new HashMap<>(1);
for(String branch : entries.keySet()) {
TableValueHistory history = entries.get(branch);
// Go through the branch and prune off all
// the values with higher vector clocks.
TableValueHistory pruned = new UnsafeTableValueHistory();
for(Iterator<? extends TableValue> iter = history.iterator();
iter.hasNext(); ) {
TableValue v = iter.next();
long c = v.getClock().getLocalTime();
// Only include items that were recorded before
// the supplied time.
if(c <= time) {
pruned.add(v);
}
}
// Then collapse the remaining values and place
// into its own history.
TableValue v = pruned.getCollapsedValue();
TableValueHistory singleton = new UnsafeTableValueHistory();
singleton.add(v);
// Save the new branch.
fm.put(branch, singleton.iterator());
}
return fm;
}
/**
* Lock the bucket to all writes.
*/
@Override public void lockBucket() {
// Not implemented.
}
/**
* Unlock the bucket to all writes.
*/
@Override public void unlockBucket() {
// Not implemented.
}
/**
* Return number of entries in the bucket. Useful to check if additional
* items have been added after compaction/flushing.
*
* @return Number of bucket elements
*/
@Override public long size() {
return num;
}
/**
* Get the memory usage of the entire bucket.
*
* @return Number of bytes used by the bucket
*/
@Override public long memory() {
long s = 0;
for(String b : entries.keySet()) {
TableValueHistory h = entries.get(b);
s += b.length() + h.memory();
}
return s;
}
/**
* Return the vector clock for this bucket. The clock is a merger
* of all the subclocks.
*
* @return Merged vector clock
*/
@Override public VectorClock getClock() {
return null;
}
/**
* Return an iterator of vector clocks from each of the data values
* in the bucket.
*
* @return Iterator over vector clocks
*/
@Override public Iterator<VectorClock> getAllClocks() {
return null;
}
/**
* Get all the data associated with this bucket.
*
* @return Serialized data
*/
@Override public byte[] getData() {
return null;
}
}