Skip to content

Commit

Permalink
Unify unique and non-unique index key and value
Browse files Browse the repository at this point in the history
The only difference between unique key:value and non-unique
key:value was that entityId was stored in key for non-unique
and in value for unique.
The only positive thing about this was that we saved a tiny bit
of storage in the unique case, under 0.1%. The trade off was two different
class hierarchies for key, value and layout and lot's of complicated
generic types in index populator and tests.

Now those hierarchies are collapsed as much as possible.
Note especially that the ValueMerger now need both KEY
and VALUE to correctly report conflicting updates.
  • Loading branch information
burqen authored and tinwelint committed Jun 26, 2017
1 parent fcb5cd0 commit c156753
Show file tree
Hide file tree
Showing 25 changed files with 260 additions and 488 deletions.
Expand Up @@ -1095,7 +1095,7 @@ public void put( KEY key, VALUE value ) throws IOException
} }


@Override @Override
public void merge( KEY key, VALUE value, ValueMerger<VALUE> valueMerger ) throws IOException public void merge( KEY key, VALUE value, ValueMerger<KEY,VALUE> valueMerger ) throws IOException
{ {
try try
{ {
Expand Down
Expand Up @@ -341,7 +341,7 @@ private void moveToCorrectLeaf( PageCursor cursor, KEY key, long stableGeneratio
* @throws IOException on cursor failure * @throws IOException on cursor failure
*/ */
void insert( PageCursor cursor, StructurePropagation<KEY> structurePropagation, KEY key, VALUE value, void insert( PageCursor cursor, StructurePropagation<KEY> structurePropagation, KEY key, VALUE value,
ValueMerger<VALUE> valueMerger, long stableGeneration, long unstableGeneration ) throws IOException ValueMerger<KEY,VALUE> valueMerger, long stableGeneration, long unstableGeneration ) throws IOException
{ {
assert cursorIsAtExpectedLocation( cursor ); assert cursorIsAtExpectedLocation( cursor );
moveToCorrectLeaf( cursor, key, stableGeneration, unstableGeneration ); moveToCorrectLeaf( cursor, key, stableGeneration, unstableGeneration );
Expand Down Expand Up @@ -600,7 +600,7 @@ private static int middle( int keyCountAfterInsert )
* @throws IOException on cursor failure * @throws IOException on cursor failure
*/ */
private void insertInLeaf( PageCursor cursor, StructurePropagation<KEY> structurePropagation, private void insertInLeaf( PageCursor cursor, StructurePropagation<KEY> structurePropagation,
KEY key, VALUE value, ValueMerger<VALUE> valueMerger, KEY key, VALUE value, ValueMerger<KEY,VALUE> valueMerger,
long stableGeneration, long unstableGeneration ) throws IOException long stableGeneration, long unstableGeneration ) throws IOException
{ {
int keyCount = TreeNode.keyCount( cursor ); int keyCount = TreeNode.keyCount( cursor );
Expand All @@ -610,7 +610,7 @@ private void insertInLeaf( PageCursor cursor, StructurePropagation<KEY> structur
{ {
// this key already exists, what shall we do? ask the valueMerger // this key already exists, what shall we do? ask the valueMerger
bTreeNode.valueAt( cursor, readValue, pos ); bTreeNode.valueAt( cursor, readValue, pos );
VALUE mergedValue = valueMerger.merge( readValue, value ); VALUE mergedValue = valueMerger.merge( readKey, key, readValue, value );
if ( mergedValue != null ) if ( mergedValue != null )
{ {
createSuccessorIfNeeded( cursor, structurePropagation, UPDATE_MID_CHILD, createSuccessorIfNeeded( cursor, structurePropagation, UPDATE_MID_CHILD,
Expand Down
Expand Up @@ -25,15 +25,17 @@
* *
* @param <VALUE> type of values to merge. * @param <VALUE> type of values to merge.
*/ */
public interface ValueMerger<VALUE> public interface ValueMerger<KEY,VALUE>
{ {
/** /**
* Merge an existing value with a new value, returning potentially a combination of the two, or {@code null} * Merge an existing value with a new value, returning potentially a combination of the two, or {@code null}
* if no merge was done effectively meaning that nothing should be written. * if no merge was done effectively meaning that nothing should be written.
* *
* @param existingKey existing key
* @param newKey new key
* @param existingValue existing value * @param existingValue existing value
* @param newValue new value * @param newValue new value
* @return {@code value}, now merged with {@code withValue}, or {@code null} if no merge was done. * @return {@code newValue}, now merged with {@code existingValue}, or {@code null} if no merge was done.
*/ */
VALUE merge( VALUE existingValue, VALUE newValue ); VALUE merge( KEY existingKey, KEY newKey, VALUE existingValue, VALUE newValue );
} }
Expand Up @@ -24,9 +24,9 @@
*/ */
public class ValueMergers public class ValueMergers
{ {
private static final ValueMerger OVERWRITE = ( existingValue, newValue ) -> newValue; private static final ValueMerger OVERWRITE = ( existingKey, newKey, existingValue, newValue ) -> newValue;


private static final ValueMerger KEEP_EXISTING = ( existingValue, newValue ) -> null; private static final ValueMerger KEEP_EXISTING = ( existingKey, newKey, existingValue, newValue ) -> null;


private ValueMergers() private ValueMergers()
{ {
Expand All @@ -37,7 +37,7 @@ private ValueMergers()
* This merger guarantees unique keys in index. * This merger guarantees unique keys in index.
*/ */
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public static <VALUE> ValueMerger<VALUE> overwrite() public static <KEY,VALUE> ValueMerger<KEY,VALUE> overwrite()
{ {
return OVERWRITE; return OVERWRITE;
} }
Expand All @@ -47,7 +47,7 @@ public static <VALUE> ValueMerger<VALUE> overwrite()
* This merger guarantees unique keys in index. * This merger guarantees unique keys in index.
*/ */
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public static <VALUE> ValueMerger<VALUE> keepExisting() public static <KEY,VALUE> ValueMerger<KEY,VALUE> keepExisting()
{ {
return KEEP_EXISTING; return KEEP_EXISTING;
} }
Expand Down
Expand Up @@ -54,7 +54,7 @@ public interface Writer<KEY,VALUE> extends Closeable
* @param valueMerger {@link ValueMerger} to consult if key already exists. * @param valueMerger {@link ValueMerger} to consult if key already exists.
* @throws IOException on index access error. * @throws IOException on index access error.
*/ */
void merge( KEY key, VALUE value, ValueMerger<VALUE> valueMerger ) throws IOException; void merge( KEY key, VALUE value, ValueMerger<KEY,VALUE> valueMerger ) throws IOException;


/** /**
* Removes a key, returning it's associated value, if found. * Removes a key, returning it's associated value, if found.
Expand Down
Expand Up @@ -52,7 +52,7 @@
@RunWith( Parameterized.class ) @RunWith( Parameterized.class )
public class InternalTreeLogicTest public class InternalTreeLogicTest
{ {
private static final ValueMerger<MutableLong> ADDER = ( base, add ) -> private static final ValueMerger<MutableLong,MutableLong> ADDER = ( existingKey, newKey, base, add ) ->
{ {
base.add( add.longValue() ); base.add( add.longValue() );
return base; return base;
Expand Down Expand Up @@ -1601,7 +1601,7 @@ private void insert( long key, long value ) throws IOException
insert( key, value, overwrite() ); insert( key, value, overwrite() );
} }


private void insert( long key, long value, ValueMerger<MutableLong> valueMerger ) throws IOException private void insert( long key, long value, ValueMerger<MutableLong,MutableLong> valueMerger ) throws IOException
{ {
structurePropagation.hasRightKeyInsert = false; structurePropagation.hasRightKeyInsert = false;
structurePropagation.hasMidChildUpdate = false; structurePropagation.hasMidChildUpdate = false;
Expand Down
Expand Up @@ -62,12 +62,14 @@ class NativeLabelScanWriter implements LabelScanWriter
/** /**
* {@link ValueMerger} used for adding label->node mappings, see {@link LabelScanValue#add(LabelScanValue)}. * {@link ValueMerger} used for adding label->node mappings, see {@link LabelScanValue#add(LabelScanValue)}.
*/ */
private static final ValueMerger<LabelScanValue> ADD_MERGER = LabelScanValue::add; private static final ValueMerger<LabelScanKey,LabelScanValue> ADD_MERGER =
( existingKey, newKey, existingValue, newValue ) -> existingValue.add( newValue );


/** /**
* {@link ValueMerger} used for removing label->node mappings, see {@link LabelScanValue#remove(LabelScanValue)}. * {@link ValueMerger} used for removing label->node mappings, see {@link LabelScanValue#remove(LabelScanValue)}.
*/ */
private static final ValueMerger<LabelScanValue> REMOVE_MERGER = LabelScanValue::remove; private static final ValueMerger<LabelScanKey,LabelScanValue> REMOVE_MERGER =
( existingKey, newKey, existingValue, newValue ) -> existingValue.remove( newValue );


/** /**
* {@link Writer} acquired when acquiring this {@link NativeLabelScanWriter}, * {@link Writer} acquired when acquiring this {@link NativeLabelScanWriter},
Expand Down
Expand Up @@ -30,18 +30,18 @@
* *
* @param <VALUE> type of values being merged. * @param <VALUE> type of values being merged.
*/ */
class ConflictDetectingValueMerger<VALUE extends NumberValue> implements ValueMerger<VALUE> class ConflictDetectingValueMerger<KEY extends NumberKey, VALUE extends NumberValue> implements ValueMerger<KEY,VALUE>
{ {
private boolean conflict; private boolean conflict;
private long existingNodeId; private long existingNodeId;
private long addedNodeId; private long addedNodeId;


@Override @Override
public VALUE merge( VALUE existingValue, VALUE newValue ) public VALUE merge( KEY existingKey, KEY newKey, VALUE existingValue, VALUE newValue )
{ {
conflict = true; conflict = true;
existingNodeId = existingValue.getEntityId(); existingNodeId = existingKey.entityId;
addedNodeId = newValue.getEntityId(); addedNodeId = newKey.entityId;
return null; return null;
} }


Expand Down
Expand Up @@ -51,7 +51,7 @@ public abstract class NativeSchemaNumberIndexPopulator<KEY extends NumberKey, VA


private final KEY treeKey; private final KEY treeKey;
private final VALUE treeValue; private final VALUE treeValue;
private final ConflictDetectingValueMerger<VALUE> conflictDetectingValueMerger; private final ConflictDetectingValueMerger<KEY,VALUE> conflictDetectingValueMerger;
private final NativeSchemaNumberIndexUpdater<KEY,VALUE> singleUpdater; private final NativeSchemaNumberIndexUpdater<KEY,VALUE> singleUpdater;


private Writer<KEY,VALUE> singleTreeWriter; private Writer<KEY,VALUE> singleTreeWriter;
Expand Down
Expand Up @@ -33,7 +33,7 @@ class NativeSchemaNumberIndexUpdater<KEY extends NumberKey, VALUE extends Number
{ {
private final KEY treeKey; private final KEY treeKey;
private final VALUE treeValue; private final VALUE treeValue;
private final ConflictDetectingValueMerger<VALUE> conflictDetectingValueMerger; private final ConflictDetectingValueMerger<KEY,VALUE> conflictDetectingValueMerger;
private Writer<KEY,VALUE> writer; private Writer<KEY,VALUE> writer;


private boolean closed = true; private boolean closed = true;
Expand All @@ -43,7 +43,7 @@ class NativeSchemaNumberIndexUpdater<KEY extends NumberKey, VALUE extends Number
{ {
this.treeKey = treeKey; this.treeKey = treeKey;
this.treeValue = treeValue; this.treeValue = treeValue;
this.conflictDetectingValueMerger = new ConflictDetectingValueMerger<VALUE>(); this.conflictDetectingValueMerger = new ConflictDetectingValueMerger<>();
} }


NativeSchemaNumberIndexUpdater<KEY,VALUE> initialize( Writer<KEY,VALUE> writer, boolean manageClosingOfWriter ) NativeSchemaNumberIndexUpdater<KEY,VALUE> initialize( Writer<KEY,VALUE> writer, boolean manageClosingOfWriter )
Expand All @@ -63,7 +63,19 @@ NativeSchemaNumberIndexUpdater<KEY,VALUE> initialize( Writer<KEY,VALUE> writer,
public void process( IndexEntryUpdate update ) throws IOException, IndexEntryConflictException public void process( IndexEntryUpdate update ) throws IOException, IndexEntryConflictException
{ {
assertOpen(); assertOpen();
processAdd( treeKey, treeValue, update, writer, conflictDetectingValueMerger ); switch ( update.updateMode() )
{
case ADDED:
processAdd( treeKey, treeValue, update, writer, conflictDetectingValueMerger );
break;
case CHANGED:
processChange( treeKey, treeValue, update, writer, conflictDetectingValueMerger );
break;
case REMOVED:
throw new UnsupportedOperationException( "Implement me" );
default:
throw new IllegalArgumentException();
}
} }


@Override @Override
Expand All @@ -90,14 +102,35 @@ private void assertOpen()
} }
} }


private static <KEY extends NumberKey, VALUE extends NumberValue> void processChange( KEY treeKey, VALUE treeValue,
IndexEntryUpdate update, Writer<KEY,VALUE> singleWriter,
ConflictDetectingValueMerger<KEY,VALUE> conflictDetectingValueMerger )
throws IOException, IndexEntryConflictException
{
// Remove old entry
treeKey.from( update.getEntityId(), update.beforeValues() );
singleWriter.remove( treeKey );
// Insert new entrty
treeKey.from( update.getEntityId(), update.values() );
treeValue.from( update.values() );
singleWriter.merge( treeKey, treeValue, conflictDetectingValueMerger );
assertNoConflict( update, conflictDetectingValueMerger );
}

static <KEY extends NumberKey, VALUE extends NumberValue> void processAdd( KEY treeKey, VALUE treeValue, static <KEY extends NumberKey, VALUE extends NumberValue> void processAdd( KEY treeKey, VALUE treeValue,
IndexEntryUpdate update, Writer<KEY,VALUE> singleWriter, IndexEntryUpdate update, Writer<KEY,VALUE> singleWriter,
ConflictDetectingValueMerger<VALUE> conflictDetectingValueMerger ) ConflictDetectingValueMerger<KEY,VALUE> conflictDetectingValueMerger )
throws IOException, IndexEntryConflictException throws IOException, IndexEntryConflictException
{ {
treeKey.from( update.getEntityId(), update.values() ); treeKey.from( update.getEntityId(), update.values() );
treeValue.from( update.getEntityId(), update.values() ); treeValue.from( update.values() );
singleWriter.merge( treeKey, treeValue, conflictDetectingValueMerger ); singleWriter.merge( treeKey, treeValue, conflictDetectingValueMerger );
assertNoConflict( update, conflictDetectingValueMerger );
}

private static <KEY extends NumberKey, VALUE extends NumberValue> void assertNoConflict( IndexEntryUpdate update,
ConflictDetectingValueMerger<KEY,VALUE> conflictDetectingValueMerger ) throws IndexEntryConflictException
{
if ( conflictDetectingValueMerger.wasConflict() ) if ( conflictDetectingValueMerger.wasConflict() )
{ {
long existingNodeId = conflictDetectingValueMerger.existingNodeId(); long existingNodeId = conflictDetectingValueMerger.existingNodeId();
Expand Down

This file was deleted.

0 comments on commit c156753

Please sign in to comment.