blob: 8d75b9ac3aa844459c37dda150cba51f032d42c7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.db;
import java.security.MessageDigest;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.Util;
import org.apache.cassandra.db.composites.CellNameType;
import org.apache.cassandra.db.composites.SimpleDenseCellNameType;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.utils.*;
import static org.apache.cassandra.Util.cellname;
import static org.apache.cassandra.db.context.CounterContext.ContextState;
public class CounterCellTest
{
private static final CounterContext cc = new CounterContext();
private static final int idLength;
private static final int clockLength;
private static final int countLength;
private static final int stepLength;
static
{
idLength = CounterId.LENGTH;
clockLength = 8; // size of long
countLength = 8; // size of long
stepLength = idLength + clockLength + countLength;
}
@Test
public void testCreate()
{
long delta = 3L;
CounterCell cell = new BufferCounterCell(Util.cellname("x"),
CounterContext.instance().createLocal(delta),
1L,
Long.MIN_VALUE);
Assert.assertEquals(delta, cell.total());
Assert.assertEquals(1, cell.value().getShort(0));
Assert.assertEquals(0, cell.value().getShort(2));
Assert.assertTrue(CounterId.wrap(cell.value(), 4).isLocalId());
Assert.assertEquals(1L, cell.value().getLong(4 + idLength));
Assert.assertEquals(delta, cell.value().getLong(4 + idLength + clockLength));
}
@Test
public void testReconcile()
{
Cell left;
Cell right;
Cell reconciled;
ByteBuffer context;
// tombstone + tombstone
left = new BufferDeletedCell(cellname("x"), 1, 1L);
right = new BufferDeletedCell(cellname("x"), 2, 2L);
assert left.reconcile(right).timestamp() == right.timestamp();
assert right.reconcile(left).timestamp() == right.timestamp();
// tombstone > live
left = new BufferDeletedCell(cellname("x"), 1, 2L);
right = BufferCounterCell.createLocal(cellname("x"), 0L, 1L, Long.MIN_VALUE);
assert left.reconcile(right) == left;
// tombstone < live last delete
left = new BufferDeletedCell(cellname("x"), 1, 1L);
right = BufferCounterCell.createLocal(cellname("x"), 0L, 4L, 2L);
assert left.reconcile(right) == left;
// tombstone == live last delete
left = new BufferDeletedCell(cellname("x"), 1, 2L);
right = BufferCounterCell.createLocal(cellname("x"), 0L, 4L, 2L);
assert left.reconcile(right) == left;
// tombstone > live last delete
left = new BufferDeletedCell(cellname("x"), 1, 4L);
right = BufferCounterCell.createLocal(cellname("x"), 0L, 9L, 1L);
assert left.reconcile(right) == left;
// live < tombstone
left = BufferCounterCell.createLocal(cellname("x"), 0L, 1L, Long.MIN_VALUE);
right = new BufferDeletedCell(cellname("x"), 1, 2L);
assert left.reconcile(right) == right;
// live last delete > tombstone
left = BufferCounterCell.createLocal(cellname("x"), 0L, 4L, 2L);
right = new BufferDeletedCell(cellname("x"), 1, 1L);
assert left.reconcile(right) == right;
// live last delete == tombstone
left = BufferCounterCell.createLocal(cellname("x"), 0L, 4L, 2L);
right = new BufferDeletedCell(cellname("x"), 1, 2L);
assert left.reconcile(right) == right;
// live last delete < tombstone
left = BufferCounterCell.createLocal(cellname("x"), 0L, 9L, 1L);
right = new BufferDeletedCell(cellname("x"), 1, 4L);
assert left.reconcile(right) == right;
// live < live last delete
left = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 2L, 3L), 1L, Long.MIN_VALUE);
right = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 1L, 1L), 4L, 3L);
assert left.reconcile(right) == right;
// live last delete > live
left = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 2L, 3L), 6L, 5L);
right = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 1L, 1L), 4L, 3L);
assert left.reconcile(right) == left;
// live + live
left = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 1L, 1L), 4L, Long.MIN_VALUE);
right = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(1), 2L, 3L), 1L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
assert ((CounterCell)reconciled).total() == 3L;
assert reconciled.timestamp() == 4L;
left = reconciled;
right = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(2), 1L, 5L), 2L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
assert ((CounterCell)reconciled).total() == 8L;
assert reconciled.timestamp() == 4L;
left = reconciled;
right = new BufferCounterCell(cellname("x"), cc.createRemote(CounterId.fromInt(2), 2L, 2L), 6L, Long.MIN_VALUE);
reconciled = left.reconcile(right);
assert reconciled.name().equals(left.name());
assert ((CounterCell)reconciled).total() == 5L;
assert reconciled.timestamp() == 6L;
context = reconciled.value();
int hd = 2; // header
assert hd + 2 * stepLength == context.remaining();
assert Util.equalsCounterId(CounterId.fromInt(1), context, hd);
assert 2L == context.getLong(hd + idLength);
assert 3L == context.getLong(hd + idLength + clockLength);
assert Util.equalsCounterId(CounterId.fromInt(2), context, hd + stepLength);
assert 2L == context.getLong(hd + stepLength + idLength);
assert 2L == context.getLong(hd + stepLength + idLength + clockLength);
assert ((CounterCell)reconciled).timestampOfLastDelete() == Long.MIN_VALUE;
}
@Test
public void testDiff()
{
ContextState left;
ContextState right;
CounterCell leftCell;
CounterCell rightCell;
// timestamp
leftCell = BufferCounterCell.createLocal(cellname("x"), 0, 1L, Long.MIN_VALUE);
rightCell = BufferCounterCell.createLocal(cellname("x"), 0, 2L, Long.MIN_VALUE);
assert rightCell == leftCell.diff(rightCell);
assert null == rightCell.diff(leftCell);
// timestampOfLastDelete
leftCell = BufferCounterCell.createLocal(cellname("x"), 0, 1L, 1L);
rightCell = BufferCounterCell.createLocal(cellname("x"), 0, 1L, 2L);
assert rightCell == leftCell.diff(rightCell);
assert null == rightCell.diff(leftCell);
// equality: equal nodes, all counts same
left = ContextState.allocate(0, 0, 3);
left.writeRemote(CounterId.fromInt(3), 3L, 0L);
left.writeRemote(CounterId.fromInt(6), 2L, 0L);
left.writeRemote(CounterId.fromInt(9), 1L, 0L);
right = ContextState.wrap(ByteBufferUtil.clone(left.context));
leftCell = new BufferCounterCell(cellname("x"), left.context, 1L);
rightCell = new BufferCounterCell(cellname("x"), right.context, 1L);
assert leftCell.diff(rightCell) == null;
// greater than: left has superset of nodes (counts equal)
left = ContextState.allocate(0, 0, 4);
left.writeRemote(CounterId.fromInt(3), 3L, 0L);
left.writeRemote(CounterId.fromInt(6), 2L, 0L);
left.writeRemote(CounterId.fromInt(9), 1L, 0L);
left.writeRemote(CounterId.fromInt(12), 0L, 0L);
right = ContextState.allocate(0, 0, 3);
right.writeRemote(CounterId.fromInt(3), 3L, 0L);
right.writeRemote(CounterId.fromInt(6), 2L, 0L);
right.writeRemote(CounterId.fromInt(9), 1L, 0L);
leftCell = new BufferCounterCell(cellname("x"), left.context, 1L);
rightCell = new BufferCounterCell(cellname("x"), right.context, 1L);
assert leftCell.diff(rightCell) == null;
// less than: right has subset of nodes (counts equal)
assert leftCell == rightCell.diff(leftCell);
// disjoint: right and left have disjoint node sets
left = ContextState.allocate(0, 0, 3);
left.writeRemote(CounterId.fromInt(3), 1L, 0L);
left.writeRemote(CounterId.fromInt(4), 1L, 0L);
left.writeRemote(CounterId.fromInt(9), 1L, 0L);
right = ContextState.allocate(0, 0, 3);
right.writeRemote(CounterId.fromInt(3), 1L, 0L);
right.writeRemote(CounterId.fromInt(6), 1L, 0L);
right.writeRemote(CounterId.fromInt(9), 1L, 0L);
leftCell = new BufferCounterCell(cellname("x"), left.context, 1L);
rightCell = new BufferCounterCell(cellname("x"), right.context, 1L);
assert rightCell == leftCell.diff(rightCell);
assert leftCell == rightCell.diff(leftCell);
}
@Test
public void testSerializeDeserialize() throws IOException
{
CounterContext.ContextState state = CounterContext.ContextState.allocate(0, 2, 2);
state.writeRemote(CounterId.fromInt(1), 4L, 4L);
state.writeLocal(CounterId.fromInt(2), 4L, 4L);
state.writeRemote(CounterId.fromInt(3), 4L, 4L);
state.writeLocal(CounterId.fromInt(4), 4L, 4L);
CellNameType type = new SimpleDenseCellNameType(UTF8Type.instance);
CounterCell original = new BufferCounterCell(cellname("x"), state.context, 1L);
byte[] serialized;
try (DataOutputBuffer bufOut = new DataOutputBuffer())
{
type.columnSerializer().serialize(original, bufOut);
serialized = bufOut.getData();
}
ByteArrayInputStream bufIn = new ByteArrayInputStream(serialized, 0, serialized.length);
CounterCell deserialized = (CounterCell) type.columnSerializer().deserialize(new DataInputStream(bufIn));
Assert.assertEquals(original, deserialized);
bufIn = new ByteArrayInputStream(serialized, 0, serialized.length);
CounterCell deserializedOnRemote = (CounterCell) type.columnSerializer().deserialize(new DataInputStream(bufIn), ColumnSerializer.Flag.FROM_REMOTE);
Assert.assertEquals(deserializedOnRemote.name(), original.name());
Assert.assertEquals(deserializedOnRemote.total(), original.total());
Assert.assertEquals(deserializedOnRemote.value(), cc.clearAllLocal(original.value()));
Assert.assertEquals(deserializedOnRemote.timestamp(), deserialized.timestamp());
Assert.assertEquals(deserializedOnRemote.timestampOfLastDelete(), deserialized.timestampOfLastDelete());
}
@Test
public void testUpdateDigest() throws Exception
{
MessageDigest digest1 = MessageDigest.getInstance("md5");
MessageDigest digest2 = MessageDigest.getInstance("md5");
CounterContext.ContextState state = CounterContext.ContextState.allocate(0, 2, 2);
state.writeRemote(CounterId.fromInt(1), 4L, 4L);
state.writeLocal(CounterId.fromInt(2), 4L, 4L);
state.writeRemote(CounterId.fromInt(3), 4L, 4L);
state.writeLocal(CounterId.fromInt(4), 4L, 4L);
CounterCell original = new BufferCounterCell(cellname("x"), state.context, 1L);
CounterCell cleared = new BufferCounterCell(cellname("x"), cc.clearAllLocal(state.context), 1L);
original.updateDigest(digest1);
cleared.updateDigest(digest2);
assert Arrays.equals(digest1.digest(), digest2.digest());
}
}