/*
* Copyright 2015 Edward Capriolo
*
* 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 io.teknek.nibiru.engine;
import io.teknek.nibiru.Store;
import io.teknek.nibiru.Token;
import io.teknek.nibiru.engine.atom.AtomKey;
import io.teknek.nibiru.engine.atom.AtomValue;
import io.teknek.nibiru.engine.atom.ColumnKey;
import io.teknek.nibiru.engine.atom.ColumnValue;
import io.teknek.nibiru.engine.atom.RowTombstoneKey;
import io.teknek.nibiru.engine.atom.TombstoneValue;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class Memtable extends AbstractMemtable {
private ConcurrentSkipListMap<Token, ConcurrentSkipListMap<AtomKey,AtomValue>> data;
public Memtable(Store columnFamily, CommitLog commitLog){
super(columnFamily, commitLog);
data = new ConcurrentSkipListMap<>();
}
public int size(){
return data.size();
}
public void put(Token rowkey, String column, String value, long stamp, long ttl) {
ColumnValue v = new ColumnValue(value, stamp, timeSource.getTimeInMillis(), ttl);
ColumnKey c = new ColumnKey(column);
ConcurrentSkipListMap<AtomKey, AtomValue> newMap = new ConcurrentSkipListMap<>();
newMap.put(c, v);
ConcurrentSkipListMap<AtomKey, AtomValue> foundRow = data.putIfAbsent(rowkey, newMap);
if (foundRow == null) {
return;
}
while (true) {
AtomValue foundColumn = foundRow.putIfAbsent(c, v);
if (foundColumn == null) {
return;
}
if (foundColumn instanceof TombstoneValue) {
TombstoneValue tomb = (TombstoneValue) foundColumn;
if (tomb.getTime() < v.getTime()) {
boolean res = foundRow.replace(c, foundColumn, v);
if (res) {
return;
}
} else {
return ;
}
}
if (foundColumn instanceof ColumnValue) {
ColumnValue orig = (ColumnValue) foundColumn;
if (orig.getTime() < v.getTime()) {
boolean res = foundRow.replace(c, foundColumn, v);
if (res) {
return;
}
}
if (orig.getTime() == v.getTime()){
int compare = v.getValue().compareTo(orig.getValue());
if (compare == 0){
return;
} if (compare > 0){
boolean res = foundRow.replace(c, foundColumn, v);
if (res) {
return;
}
} else {
return;
}
}
}
}
}
/**
*
* @param row
* @param column
* @return
* null if row does not exist
* TombstoneValue if row has row tombstone >= column
* TombstoneValue if row column is a tombstone column
* null if colun does not exist
*/
public AtomValue get(Token row, String column) {
ConcurrentSkipListMap<AtomKey, AtomValue> rowkeyAndColumns = data.get(row);
if (rowkeyAndColumns == null) {
return null;
} else {
TombstoneValue tomb = (TombstoneValue) rowkeyAndColumns.get(new RowTombstoneKey());
if (tomb == null) {
return rowkeyAndColumns.get(new ColumnKey(column));
} else {
AtomValue foundColumn = rowkeyAndColumns.get(new ColumnKey(column));
if (foundColumn == null) {
return tomb;
} else {
if (tomb.getTime() >= foundColumn.getTime()) {
return tomb;
} else {
return foundColumn;
}
}
}
}
}
public SortedMap<AtomKey,AtomValue> slice(Token rowkey, String start, String end){
SortedMap<AtomKey, AtomValue> row = data.get(rowkey);
if (row == null){
return new TreeMap<AtomKey,AtomValue>();
}
TombstoneValue tomb = (TombstoneValue) row.get(new RowTombstoneKey());
if (tomb == null){
return data.get(rowkey).subMap(new ColumnKey(start), new ColumnKey(end));
}
ConcurrentNavigableMap<AtomKey, AtomValue> copy = new ConcurrentSkipListMap<AtomKey,AtomValue> ();
copy.put(new RowTombstoneKey(), tomb);
ConcurrentNavigableMap<AtomKey, AtomValue> ret = data.get(rowkey).subMap(new ColumnKey(start), new ColumnKey(end));
for (Entry<AtomKey, AtomValue> entry : ret.entrySet()){
if (entry.getValue() instanceof TombstoneValue){
TombstoneValue tv = (TombstoneValue) entry.getValue();
if (tv.getTime() > tomb.getTime()){
copy.put(entry.getKey(), entry.getValue());
}
} else if (entry.getValue() instanceof ColumnValue){
ColumnValue v = (ColumnValue) entry.getValue();
if (v.getTime() > tomb.getTime()){
copy.put(entry.getKey(), entry.getValue());
}
} else {
throw new RuntimeException("do not know what X is");
}
}
return copy;
}
public void delete(Token row, long time){
ConcurrentSkipListMap<AtomKey, AtomValue> delete = new ConcurrentSkipListMap<AtomKey, AtomValue>();
delete.put(new RowTombstoneKey(), new TombstoneValue(time));
ConcurrentSkipListMap<AtomKey, AtomValue> returned = data.putIfAbsent(row, delete);
if (returned != null){
returned.put(new RowTombstoneKey(), new TombstoneValue(time));
}
}
public void delete (Token row, String column, long time){
TombstoneValue v = new TombstoneValue(time);
ColumnKey k = new ColumnKey(column);
ConcurrentSkipListMap<AtomKey, AtomValue> delete = new ConcurrentSkipListMap<AtomKey, AtomValue>();
delete.put(k, v);
ConcurrentSkipListMap<AtomKey, AtomValue> returned = data.putIfAbsent(row, delete);
if (returned !=null){
returned.put(k, v);
}
}
@Override
public Iterator<MemtablePair<Token, Map<AtomKey, Iterator<AtomValue>>>> getDataIterator() {
final Iterator<Entry<Token, ConcurrentSkipListMap<AtomKey, AtomValue>>> dataIterator = data.entrySet().iterator();
return new Iterator<MemtablePair<Token, Map<AtomKey, Iterator<AtomValue>>>>() {
@Override
public boolean hasNext() {
return dataIterator.hasNext();
}
@Override
public MemtablePair<Token, Map<AtomKey, Iterator<AtomValue>>> next() {
Entry<Token, ConcurrentSkipListMap<AtomKey, AtomValue>> row = dataIterator.next();
Map<AtomKey, Iterator<AtomValue>> reform = new TreeMap<>();
for (Entry<AtomKey, AtomValue> i : row.getValue().entrySet()){
reform.put(i.getKey(), Arrays.asList(i.getValue()).iterator());
}
return new MemtablePair<Token, Map<AtomKey, Iterator<AtomValue>>>(row.getKey(), reform);
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
};
}
}