Skip to content

Commit

Permalink
Make StoreLocker Closeable
Browse files Browse the repository at this point in the history
  • Loading branch information
spacecowboy committed Oct 20, 2016
1 parent 0e6c0bf commit 98a668b
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 114 deletions.
Expand Up @@ -39,13 +39,11 @@
import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.Args; import org.neo4j.helpers.Args;
import org.neo4j.kernel.StoreLockException; import org.neo4j.kernel.StoreLockException;
import org.neo4j.kernel.impl.transaction.command.Command;
import org.neo4j.kernel.internal.StoreLocker; import org.neo4j.kernel.internal.StoreLocker;
import org.neo4j.server.configuration.ConfigLoader; import org.neo4j.server.configuration.ConfigLoader;


import static java.lang.String.format; import static java.lang.String.format;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.database_path; import static org.neo4j.dbms.DatabaseManagementSystemSettings.database_path;
import static org.neo4j.helpers.collection.MapUtil.stringMap; import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.kernel.impl.util.Converters.identity; import static org.neo4j.kernel.impl.util.Converters.identity;
Expand Down Expand Up @@ -117,12 +115,13 @@ public void execute( String[] args ) throws IncorrectUsage, CommandFailed
} }
catch ( CannotWriteException e ) catch ( CannotWriteException e )
{ {
throw new CommandFailed( "you do not have permission to dump the database -- is Neo4j running as a " + throw new CommandFailed(
"different user?", e ); "you do not have permission to dump the database -- is Neo4j running as a " + "different user?",
e );
} }
} }


private <T> T parse( String[] args, String argument, Function<String, T> converter ) throws IncorrectUsage private <T> T parse( String[] args, String argument, Function<String,T> converter ) throws IncorrectUsage
{ {
try try
{ {
Expand All @@ -138,8 +137,7 @@ private Path toDatabaseDirectory( String databaseName )
{ {
//noinspection unchecked //noinspection unchecked
return new ConfigLoader( asList( DatabaseManagementSystemSettings.class, GraphDatabaseSettings.class ) ) return new ConfigLoader( asList( DatabaseManagementSystemSettings.class, GraphDatabaseSettings.class ) )
.loadOfflineConfig( .loadOfflineConfig( Optional.of( homeDir.toFile() ),
Optional.of( homeDir.toFile() ),
Optional.of( configDir.resolve( "neo4j.conf" ).toFile() ) ) Optional.of( configDir.resolve( "neo4j.conf" ).toFile() ) )
.with( stringMap( DatabaseManagementSystemSettings.active_database.name(), databaseName ) ) .with( stringMap( DatabaseManagementSystemSettings.active_database.name(), databaseName ) )
.get( database_path ).toPath(); .get( database_path ).toPath();
Expand Down
Expand Up @@ -40,7 +40,7 @@ Closeable withLock( Path databaseDirectory ) throws CommandFailed, CannotWriteEx


storeLocker.checkLock( databaseDirectory.toFile() ); storeLocker.checkLock( databaseDirectory.toFile() );


return storeLocker::release; return storeLocker;
} }
else else
{ {
Expand Down
Expand Up @@ -33,11 +33,9 @@ public class Util
{ {
public static void checkLock( Path databaseDirectory ) throws CommandFailed public static void checkLock( Path databaseDirectory ) throws CommandFailed
{ {
try try ( StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() ) )
{ {
StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() );
storeLocker.checkLock( databaseDirectory.toFile() ); storeLocker.checkLock( databaseDirectory.toFile() );
storeLocker.release();
} }
catch ( StoreLockException e ) catch ( StoreLockException e )
{ {
Expand Down
Expand Up @@ -19,6 +19,11 @@
*/ */
package org.neo4j.commandline.dbms; package org.neo4j.commandline.dbms;


import org.apache.commons.lang3.SystemUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
Expand All @@ -30,11 +35,6 @@
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.function.Predicate; import java.util.function.Predicate;


import org.apache.commons.lang3.SystemUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import org.neo4j.commandline.admin.CommandFailed; import org.neo4j.commandline.admin.CommandFailed;
import org.neo4j.commandline.admin.IncorrectUsage; import org.neo4j.commandline.admin.IncorrectUsage;
import org.neo4j.dbms.archive.Dumper; import org.neo4j.dbms.archive.Dumper;
Expand All @@ -45,7 +45,6 @@
import static java.lang.String.format; import static java.lang.String.format;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptySet; import static java.util.Collections.emptySet;

import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
Expand All @@ -57,7 +56,6 @@
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;

import static org.neo4j.dbms.DatabaseManagementSystemSettings.data_directory; import static org.neo4j.dbms.DatabaseManagementSystemSettings.data_directory;
import static org.neo4j.dbms.archive.TestUtils.withPermissions; import static org.neo4j.dbms.archive.TestUtils.withPermissions;


Expand Down Expand Up @@ -122,22 +120,18 @@ public void shouldRespectTheStoreLock() throws IOException, IncorrectUsage, Comm
{ {
Path databaseDirectory = homeDir.resolve( "data/databases/foo.db" ); Path databaseDirectory = homeDir.resolve( "data/databases/foo.db" );
Files.createDirectories( databaseDirectory ); Files.createDirectories( databaseDirectory );
StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() );
storeLocker.checkLock( databaseDirectory.toFile() );


try try ( StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() ) )
{ {
storeLocker.checkLock( databaseDirectory.toFile() );

execute( "foo.db" ); execute( "foo.db" );
fail( "expected exception" ); fail( "expected exception" );
} }
catch ( CommandFailed e ) catch ( CommandFailed e )
{ {
assertThat( e.getMessage(), equalTo( "the database is in use -- stop Neo4j and try again" ) ); assertThat( e.getMessage(), equalTo( "the database is in use -- stop Neo4j and try again" ) );
} }
finally
{
storeLocker.release();
}
} }


@Test @Test
Expand Down Expand Up @@ -184,30 +178,28 @@ public void shouldNotAccidentallyCreateTheDatabaseDirectoryAsASideEffectOfStoreL
} }


@Test @Test
public void shouldReportAHelpfulErrorIfWeDontHaveWritePermissionsForLock() public void shouldReportAHelpfulErrorIfWeDontHaveWritePermissionsForLock() throws IOException, IncorrectUsage
throws IOException, IncorrectUsage
{ {
assumeFalse( "We haven't found a way to reliably tests permissions on Windows", SystemUtils.IS_OS_WINDOWS ); assumeFalse( "We haven't found a way to reliably tests permissions on Windows", SystemUtils.IS_OS_WINDOWS );


Path databaseDirectory = homeDir.resolve( "data/databases/foo.db" ); Path databaseDirectory = homeDir.resolve( "data/databases/foo.db" );
Files.createDirectories( databaseDirectory ); Files.createDirectories( databaseDirectory );
StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() );
storeLocker.checkLock( databaseDirectory.toFile() );


try ( Closeable ignored = try ( StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() ) )
withPermissions( databaseDirectory.resolve( StoreLocker.STORE_LOCK_FILENAME ), emptySet() ) )
{ {
execute( "foo.db" ); storeLocker.checkLock( databaseDirectory.toFile() );
fail( "expected exception" );
} try ( Closeable ignored = withPermissions( databaseDirectory.resolve( StoreLocker.STORE_LOCK_FILENAME ),
catch ( CommandFailed e ) emptySet() ) )
{ {
assertThat( e.getMessage(), equalTo( "you do not have permission to dump the database -- is Neo4j " + execute( "foo.db" );
"running as a different user?" ) ); fail( "expected exception" );
} }
finally catch ( CommandFailed e )
{ {
storeLocker.release(); assertThat( e.getMessage(), equalTo( "you do not have permission to dump the database -- is Neo4j " +
"running as a different user?" ) );
}
} }
} }


Expand Down Expand Up @@ -273,8 +265,8 @@ public void shouldGiveAClearErrorIfTheArchiveAlreadyExists() throws IOException,
@Test @Test
public void shouldGiveAClearMessageIfTheDatabaseDoesntExist() throws IOException, IncorrectUsage public void shouldGiveAClearMessageIfTheDatabaseDoesntExist() throws IOException, IncorrectUsage
{ {
doThrow( new NoSuchFileException( homeDir.resolve( "data/databases/foo.db" ).toString() ) ) doThrow( new NoSuchFileException( homeDir.resolve( "data/databases/foo.db" ).toString() ) ).when( dumper )
.when( dumper ).dump( any(), any(), any() ); .dump( any(), any(), any() );
try try
{ {
execute( "foo.db" ); execute( "foo.db" );
Expand Down Expand Up @@ -303,8 +295,7 @@ public void shouldGiveAClearMessageIfTheArchivesParentDoesntExist() throws IOExc
} }


@Test @Test
public void public void shouldWrapIOExceptionsCarefulllyBecauseCriticalInformationIsOftenEncodedInTheirNameButMissingFromTheirMessage()
shouldWrapIOExceptionsCarefulllyBecauseCriticalInformationIsOftenEncodedInTheirNameButMissingFromTheirMessage()
throws IOException, IncorrectUsage throws IOException, IncorrectUsage
{ {
doThrow( new IOException( "the-message" ) ).when( dumper ).dump( any(), any(), any() ); doThrow( new IOException( "the-message" ) ).when( dumper ).dump( any(), any(), any() );
Expand All @@ -327,8 +318,9 @@ private void execute( final String database ) throws IncorrectUsage, CommandFail


private void assertCanLockStore( Path databaseDirectory ) throws IOException private void assertCanLockStore( Path databaseDirectory ) throws IOException
{ {
StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() ); try ( StoreLocker storeLocker = new StoreLocker( new DefaultFileSystemAbstraction() ) )
storeLocker.checkLock( databaseDirectory.toFile() ); {
storeLocker.release(); storeLocker.checkLock( databaseDirectory.toFile() );
}
} }
} }
Expand Up @@ -19,6 +19,7 @@
*/ */
package org.neo4j.kernel.internal; package org.neo4j.kernel.internal;


import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
Expand All @@ -28,7 +29,7 @@
import org.neo4j.io.fs.StoreChannel; import org.neo4j.io.fs.StoreChannel;
import org.neo4j.kernel.StoreLockException; import org.neo4j.kernel.StoreLockException;


public class StoreLocker public class StoreLocker implements Closeable
{ {
public static final String STORE_LOCK_FILENAME = "store_lock"; public static final String STORE_LOCK_FILENAME = "store_lock";


Expand All @@ -46,6 +47,9 @@ public StoreLocker( FileSystemAbstraction fileSystemAbstraction )
* Obtains lock on store file so that we can ensure the store is not shared between database instances * Obtains lock on store file so that we can ensure the store is not shared between database instances
* <p> * <p>
* Creates store dir if necessary, creates store lock file if necessary * Creates store dir if necessary, creates store lock file if necessary
* <p>
* Please note that this lock is only valid for as long the {@link #storeLockFileChannel} lives, so make sure the
* lock cannot be garbage collected as long as the lock should be valid.
* *
* @throws StoreLockException if lock could not be acquired * @throws StoreLockException if lock could not be acquired
*/ */
Expand Down Expand Up @@ -86,11 +90,12 @@ public void checkLock( File storeDir ) throws StoreLockException
private StoreLockException storeLockException( String message, Exception e ) private StoreLockException storeLockException( String message, Exception e )
{ {
String help = "Please ensure no other process is using this database, and that the directory is writable " + String help = "Please ensure no other process is using this database, and that the directory is writable " +
"(required even for read-only access)"; "(required even for read-only access)";
return new StoreLockException( message + ". " + help, e ); return new StoreLockException( message + ". " + help, e );
} }


public void release() throws IOException @Override
public void close() throws IOException
{ {
if ( storeLockFileLock != null ) if ( storeLockFileLock != null )
{ {
Expand Down
Expand Up @@ -43,6 +43,6 @@ public void start() throws Throwable
@Override @Override
public void stop() throws Throwable public void stop() throws Throwable
{ {
storeLocker.release(); storeLocker.close();
} }
} }
Expand Up @@ -976,7 +976,7 @@ public void shutdown()


try try
{ {
storeLocker.release(); storeLocker.close();
} }
catch ( IOException e ) catch ( IOException e )
{ {
Expand Down
Expand Up @@ -60,9 +60,8 @@ public boolean fileExists( File fileName )
return true; return true;
} }
}; };
StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction );


try try ( StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction ) )
{ {
storeLocker.checkLock( target.directory( "unused" ) ); storeLocker.checkLock( target.directory( "unused" ) );


Expand All @@ -72,10 +71,6 @@ public boolean fileExists( File fileName )
{ {
fail(); fail();
} }
finally
{
storeLocker.release();
}
} }


@Test @Test
Expand All @@ -89,17 +84,12 @@ public boolean fileExists( File fileName )
return false; return false;
} }
}; };
StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction );


try try ( StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction ) )
{ {
storeLocker.checkLock( target.directory( "unused" ) ); storeLocker.checkLock( target.directory( "unused" ) );
// Ok // Ok
} }
finally
{
storeLocker.release();
}
} }


@Test @Test
Expand All @@ -119,23 +109,21 @@ public boolean fileExists( File fileName )
return false; return false;
} }
}; };
StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction );
File storeDir = target.directory( "unused" ); File storeDir = target.directory( "unused" );


try try ( StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction ) )
{ {
storeLocker.checkLock( storeDir ); storeLocker.checkLock( storeDir );
fail(); fail();
} }
catch ( StoreLockException e ) catch ( StoreLockException e )
{ {
String msg = format( "Unable to create path for store dir: %s. Please ensure no other process is using this database, and that the directory is writable (required even for read-only access)", storeDir ); String msg = format( "Unable to create path for store dir: %s. " +
"Please ensure no other process is using this database, and that " +
"the directory is writable (required even for read-only access)", storeDir );
assertThat( e.getMessage(), is( msg ) ); assertThat( e.getMessage(), is( msg ) );
} }
finally
{
storeLocker.release();
}
} }


@Test @Test
Expand All @@ -155,24 +143,22 @@ public boolean fileExists( File fileName )
return false; return false;
} }
}; };
StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction );
File storeDir = target.directory( "unused" ); File storeDir = target.directory( "unused" );


try try ( StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction ) )
{ {
storeLocker.checkLock( storeDir ); storeLocker.checkLock( storeDir );
fail(); fail();
} }
catch ( StoreLockException e ) catch ( StoreLockException e )
{ {
String msg = format( "Unable to obtain lock on store lock file: %s. Please ensure no other process is using this database, and that the directory is writable (required even for read-only access)", new File( storeDir, String msg = format( "Unable to obtain lock on store lock file: %s. " +
STORE_LOCK_FILENAME ) ); "Please ensure no other process is using this database, and that the " +
"directory is writable (required even for read-only access)",
new File( storeDir, STORE_LOCK_FILENAME ) );
assertThat( e.getMessage(), is( msg ) ); assertThat( e.getMessage(), is( msg ) );
} }
finally
{
storeLocker.release();
}
} }


@Test @Test
Expand All @@ -199,20 +185,16 @@ public FileLock tryLock() throws IOException
}; };
} }
}; };
StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction );


try try ( StoreLocker storeLocker = new StoreLocker( fileSystemAbstraction ) )
{ {
storeLocker.checkLock( target.directory( "unused" ) ); storeLocker.checkLock( target.directory( "unused" ) );
fail(); fail();
} }
catch ( StoreLockException e ) catch ( StoreLockException e )
{ {
assertThat( e.getMessage(), containsString( "Store and its lock file has been locked by another process" ) ); assertThat( e.getMessage(),
} containsString( "Store and its lock file has been locked by another process" ) );
finally
{
storeLocker.release();
} }
} }


Expand Down

0 comments on commit 98a668b

Please sign in to comment.