All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.kernel.impl.index.IndexProviderStore Maven / Gradle / Ivy

/*
 * Copyright (c) 2018-2020 "Graph Foundation,"
 * Graph Foundation, Inc. [https://graphfoundation.org]
 *
 * This file is part of ONgDB.
 *
 * ONgDB 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 .
 */
/*
 * Copyright (c) 2002-2020 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.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;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;

import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.fs.OpenMode;
import org.neo4j.io.fs.StoreChannel;
import org.neo4j.kernel.impl.store.NotCurrentStoreVersionException;
import org.neo4j.kernel.impl.storemigration.UpgradeNotAllowedByConfigurationException;

import static org.neo4j.kernel.impl.store.MetaDataStore.versionLongToString;

public class IndexProviderStore
{
    private static final int RECORD_SIZE = 8;
    private static final int RECORD_COUNT = 5;

    private final long creationTime;
    private final long randomIdentifier;
    private volatile long version;
    private final long indexVersion;

    private final StoreChannel fileChannel;
    private final ByteBuffer buf = ByteBuffer.allocate( RECORD_SIZE * RECORD_COUNT );
    private volatile long lastCommittedTx;
    private final File file;
    private final Random random;

    public IndexProviderStore( File file, FileSystemAbstraction fileSystem, long expectedVersion, boolean allowUpgrade )
    {
        this.file = file;
        this.random = new Random( System.currentTimeMillis() );
        StoreChannel channel = null;
        boolean success = false;
        try
        {
            // Create it if it doesn't exist
            if ( !fileSystem.fileExists( file ) || fileSystem.getFileSize( file ) == 0 )
            {
                create( file, fileSystem, expectedVersion );
            }

            // Read all the records in the file
            channel = fileSystem.open( file, OpenMode.READ_WRITE );
            Long[] records = readRecordsWithNullDefaults( channel, RECORD_COUNT, allowUpgrade );
            creationTime = records[0];
            randomIdentifier = records[1];
            version = records[2];
            lastCommittedTx = records[3];
            Long readIndexVersion = records[4];
            fileChannel = channel;

            // Compare version and throw exception if there's a mismatch, also considering "allow upgrade"
            boolean versionDiffers = compareExpectedVersionWithStoreVersion( expectedVersion, allowUpgrade, readIndexVersion );

            // Here we know that either the version matches or we just upgraded to the expected version
            indexVersion = expectedVersion;
            if ( versionDiffers )
            // We have upgraded the version, let's write it
            {
                writeOut();
            }
            success = true;
        }
        catch ( IOException e )
        {
            throw new RuntimeException( e );
        }
        finally
        {
            if ( !success && channel != null )
            {
                try
                {
                    channel.close();
                }
                catch ( IOException e )
                {   // What to do?
                }
            }
        }
    }

    private boolean compareExpectedVersionWithStoreVersion( long expectedVersion,
            boolean allowUpgrade, Long readIndexVersion )
    {
        boolean versionDiffers = readIndexVersion == null || readIndexVersion != expectedVersion;
        if ( versionDiffers )
        {
            // We can throw a more explicit exception if we see that we're trying to run
            // with an older version than the store is.
            if ( readIndexVersion != null && expectedVersion < readIndexVersion )
            {
                String expected = versionLongToString( expectedVersion );
                String readVersion = versionLongToString( readIndexVersion );
                throw new NotCurrentStoreVersionException( expected, readVersion,
                        "Your index has been upgraded to " + readVersion +
                        " and cannot run with an older version " + expected, false );
            }
            else if ( !allowUpgrade )
            {
                // We try to run with a newer version than the store is but isn't allowed to upgrade.
                throw new UpgradeNotAllowedByConfigurationException();
            }
        }
        return versionDiffers;
    }

    private Long[] readRecordsWithNullDefaults( StoreChannel fileChannel, int count, boolean allowUpgrade ) throws IOException
    {
        buf.clear();
        int bytesRead = fileChannel.read( buf );
        int wholeRecordsRead = bytesRead / RECORD_SIZE;
        if ( wholeRecordsRead < RECORD_COUNT && !allowUpgrade )
        {
            throw new UpgradeNotAllowedByConfigurationException( "Index version (managed by " + file + ") has changed and needs to be upgraded" );
        }

        buf.flip();
        Long[] result = new Long[count];
        for ( int i = 0; i < wholeRecordsRead; i++ )
        {
            result[i] = buf.getLong();
        }
        return result;
    }

    private void create( File file, FileSystemAbstraction fileSystem, long indexVersion ) throws IOException
    {
        if ( fileSystem.fileExists( file ) && fileSystem.getFileSize( file ) > 0 )
        {
            throw new IllegalArgumentException( file + " already exist" );
        }

        try ( StoreChannel fileChannel = fileSystem.open( file, OpenMode.READ_WRITE ) )
        {
            write( fileChannel, System.currentTimeMillis(), random.nextLong(), 0, 1, indexVersion );
        }
    }

    private void write( StoreChannel channel, long time, long identifier, long version, long lastCommittedTxId,
            long indexVersion ) throws IOException
    {
        buf.clear();
        buf.putLong( time ).putLong( identifier ).putLong( version ).putLong( lastCommittedTxId ).putLong( indexVersion );
        buf.flip();

        channel.writeAll( buf, 0 );
        channel.force( true );
    }

    public File getFile()
    {
        return file;
    }

    public long getCreationTime()
    {
        return creationTime;
    }

    public long getVersion()
    {
        return version;
    }

    public long getIndexVersion()
    {
        return indexVersion;
    }

    public synchronized void setVersion( long version )
    {
        this.version = version;
        writeOut();
    }

    public synchronized void setLastCommittedTx( long txId )
    {
        this.lastCommittedTx = txId;
    }

    public long getLastCommittedTx()
    {
        return this.lastCommittedTx;
    }

    private void writeOut()
    {
        try
        {
            write( fileChannel, creationTime, randomIdentifier, version, lastCommittedTx, indexVersion );
        }
        catch ( IOException e )
        {
            throw new RuntimeException( e );
        }
    }

    public void close()
    {
        if ( !fileChannel.isOpen() )
        {
            return;
        }

        writeOut();
        try
        {
            fileChannel.close();
        }
        catch ( IOException e )
        {
            throw new RuntimeException( e );
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy