From 1e705c8f47cadeb750613cb6a4f2e316bd5bce98 Mon Sep 17 00:00:00 2001 From: Olivia Ytterbrink Date: Wed, 28 Feb 2018 14:46:44 +0100 Subject: [PATCH] Enable online updating for temporal values --- .../schema/NativeSchemaIndexAccessor.java | 2 +- .../index/schema/TemporalIndexAccessor.java | 36 +++--- .../index/schema/TemporalIndexUpdater.java | 121 ++++++++++++++++++ .../IndexProviderCompatibilityTestSuite.java | 28 +++- .../SimpleIndexAccessorCompatibility.java | 19 +++ .../SimpleIndexPopulatorCompatibility.java | 25 ---- 6 files changed, 188 insertions(+), 43 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessor.java index 9722da2d18491..33e29514e47ad 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessor.java @@ -74,7 +74,7 @@ public void drop() throws IOException } @Override - public IndexUpdater newUpdater( IndexUpdateMode mode ) + public NativeSchemaIndexUpdater newUpdater( IndexUpdateMode mode ) { assertOpen(); try diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexAccessor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexAccessor.java index ee5f34ac80f3a..81b8522dad567 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexAccessor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexAccessor.java @@ -34,9 +34,8 @@ import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.pagecache.IOLimiter; import org.neo4j.io.pagecache.PageCache; -import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.index.IndexAccessor; -import org.neo4j.kernel.api.index.IndexEntryUpdate; +import org.neo4j.kernel.api.index.IndexPopulator; import org.neo4j.kernel.api.index.IndexUpdater; import org.neo4j.kernel.api.index.PropertyAccessor; import org.neo4j.kernel.api.index.SchemaIndexProvider; @@ -76,19 +75,7 @@ public void drop() throws IOException @Override public IndexUpdater newUpdater( IndexUpdateMode mode ) { - return new IndexUpdater() - { - @Override - public void process( IndexEntryUpdate update ) throws IOException, IndexEntryConflictException - { - throw new UnsupportedOperationException( "Not yet" ); - } - - @Override - public void close() throws IOException, IndexEntryConflictException - { - } - }; + return new TemporalIndexUpdater( this, mode ); } @Override @@ -243,7 +230,9 @@ static class PartFactory implements TemporalIndexCache.Factory, @Override public PartAccessor newDate() throws IOException { - return new PartAccessor<>( pageCache, fs, temporalIndexFiles.date(), + TemporalIndexFiles.FileLayout fileLayout = temporalIndexFiles.date(); + ensureIndexExists( fileLayout ); + return new PartAccessor<>( pageCache, fs, fileLayout, recoveryCleanupWorkCollector, monitor, descriptor, indexId, samplingConfig ); } @@ -276,5 +265,20 @@ public PartAccessor newDuration() throws IOException { throw new UnsupportedOperationException( "no comprende" ); } + + private void ensureIndexExists( TemporalIndexFiles.FileLayout fileLayout ) throws IOException + { + if ( !fs.fileExists( fileLayout.indexFile ) ) + { + createEmptyIndex( fileLayout ); + } + } + + private void createEmptyIndex( TemporalIndexFiles.FileLayout fileLayout ) throws IOException + { + IndexPopulator populator = new TemporalIndexPopulator.PartPopulator<>( pageCache, fs, fileLayout, monitor, descriptor, indexId ); + populator.create(); + populator.close( true ); + } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java new file mode 100644 index 0000000000000..c6842026af034 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.index.schema; + +import java.io.IOException; + +import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; +import org.neo4j.kernel.api.index.IndexEntryUpdate; +import org.neo4j.kernel.api.index.IndexUpdater; +import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexUtils; + +public class TemporalIndexUpdater extends TemporalIndexCache,IOException> implements IndexUpdater +{ + TemporalIndexUpdater( TemporalIndexAccessor accessor, IndexUpdateMode mode ) + { + super( new PartFactory( accessor, mode ) ); + } + + @Override + public void process( IndexEntryUpdate update ) throws IOException, IndexEntryConflictException + { + IndexUpdater to = select( update.values()[0].valueGroup() ); + switch ( update.updateMode() ) + { + case ADDED: + case REMOVED: + to.process( update ); + break; + case CHANGED: + IndexUpdater from = select( update.beforeValues()[0].valueGroup() ); + // There are two cases: + // - both before/after go into the same updater --> pass update into that updater + if ( from == to ) + { + from.process( update ); + } + // - before go into one and after into the other --> REMOVED from one and ADDED into the other + else + { + from.process( IndexEntryUpdate.remove( update.getEntityId(), update.indexKey(), update.beforeValues() ) ); + to.process( IndexEntryUpdate.add( update.getEntityId(), update.indexKey(), update.values() ) ); + } + break; + default: + throw new IllegalArgumentException( "Unknown update mode" ); + } + } + + @Override + public void close() throws IOException, IndexEntryConflictException + { + FusionIndexUtils.forAll( NativeSchemaIndexUpdater::close, this ); + } + + static class PartFactory implements TemporalIndexCache.Factory,IOException> + { + + private final TemporalIndexAccessor accessor; + private final IndexUpdateMode mode; + + PartFactory( TemporalIndexAccessor accessor, IndexUpdateMode mode ) + { + this.accessor = accessor; + this.mode = mode; + } + + @Override + public NativeSchemaIndexUpdater newDate() throws IOException + { + return accessor.date().newUpdater( mode ); + } + + @Override + public NativeSchemaIndexUpdater newDateTime() throws IOException + { + throw new UnsupportedOperationException( "ma-a-da dayo" ); + } + + @Override + public NativeSchemaIndexUpdater newDateTimeZoned() throws IOException + { + throw new UnsupportedOperationException( "ma-a-da dayo" ); + } + + @Override + public NativeSchemaIndexUpdater newTime() throws IOException + { + throw new UnsupportedOperationException( "ma-a-da dayo" ); + } + + @Override + public NativeSchemaIndexUpdater newTimeZoned() throws IOException + { + throw new UnsupportedOperationException( "ma-a-da dayo" ); + } + + @Override + public NativeSchemaIndexUpdater newDuration() throws IOException + { + throw new UnsupportedOperationException( "ma-a-da dayo" ); + } + } +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java b/community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java index 083c9d703cfd4..1003fee823adf 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/api/index/IndexProviderCompatibilityTestSuite.java @@ -25,6 +25,7 @@ import org.junit.runners.Suite; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -71,9 +72,10 @@ public abstract static class Compatibility protected File graphDbDir; protected FileSystemAbstraction fs; - protected final IndexProviderCompatibilityTestSuite testSuite; protected SchemaIndexProvider indexProvider; protected IndexDescriptor descriptor; + final IndexProviderCompatibilityTestSuite testSuite; + final List allValues; @Before public void setup() @@ -88,6 +90,7 @@ public Compatibility( IndexProviderCompatibilityTestSuite testSuite, IndexDescri { this.testSuite = testSuite; this.descriptor = descriptor; + this.allValues = allValues( testSuite.getSupportedValues() ); pageCacheAndDependenciesRule = new PageCacheAndDependenciesRule( DefaultFileSystemRule::new, testSuite.getClass() ); } @@ -115,5 +118,28 @@ protected void withPopulator( IndexPopulator populator, ThrowingConsumer allValues( Iterable supportedValues ) + { + long nodeIds = 0; + List result = new ArrayList<>(); + for ( Value value : supportedValues ) + { + result.add( new NodeAndValue( nodeIds++, value ) ); + } + return result; + } + + static class NodeAndValue + { + final long nodeId; + final Value value; + + NodeAndValue( long nodeId, Value value ) + { + this.nodeId = nodeId; + this.value = value; + } + } } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java b/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java index 22d007b7f0752..6bc74a4a01cbf 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexAccessorCompatibility.java @@ -22,7 +22,9 @@ import org.junit.Ignore; import org.junit.Test; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.index.IndexDescriptor; @@ -121,6 +123,23 @@ public void testIndexSeekByPrefixOnNonStrings() throws Exception assertThat( query( IndexQuery.stringPrefix( 1, "2" ) ), equalTo( EMPTY_LIST ) ); } + @Test + public void shouldUpdateWithAllValues() throws Exception + { + // GIVEN + List> updates = new ArrayList<>(); + allValues.forEach( entry -> updates.add( IndexEntryUpdate.add( entry.nodeId, descriptor.schema(), entry.value ) ) ); + updateAndCommit( updates ); + + // then + int propertyKeyId = descriptor.schema().getPropertyId(); + for ( NodeAndValue entry : allValues ) + { + List result = query( IndexQuery.exact( propertyKeyId, entry.value ) ); + assertThat( result, equalTo( Collections.singletonList( entry.nodeId ) ) ); + } + } + // This behaviour is expected by General indexes @Ignore( "Not a test. This is a compatibility suite" ) diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java b/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java index 5e1eeada14380..5b453600f9d02 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/api/index/SimpleIndexPopulatorCompatibility.java @@ -159,31 +159,6 @@ public void shouldApplyUpdatesIdempotently() throws Exception } } - private List allValues( Iterable supportedValues ) - { - long nodeIds = 0; - List result = new ArrayList<>(); - for ( Value value : supportedValues ) - { - result.add( new NodeAndValue( nodeIds++, value ) ); - } - return result; - } - - private List allValues = allValues( testSuite.getSupportedValues() ); - - static class NodeAndValue - { - final long nodeId; - final Value value; - - NodeAndValue( long nodeId, Value value ) - { - this.nodeId = nodeId; - this.value = value; - } - } - @Test public void shouldPopulateWithAllValues() throws Exception {