Skip to content
This repository has been archived by the owner on Jul 6, 2023. It is now read-only.

Commit

Permalink
Merge pull request #224 from sherfert/4.0-bookmark-per-db
Browse files Browse the repository at this point in the history
Keep one bookmark per database.
  • Loading branch information
sherfert authored Jun 8, 2020
2 parents 3dce3ec + 9306196 commit 437244d
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ public void cypherWithOrder() throws CommandException {
String serverVersion = shell.getServerVersion();
assumeTrue((minorVersion(serverVersion) == 6 && majorVersion(serverVersion) == 3) || majorVersion(serverVersion) > 3);

// Make sure we are creating a new NEW index
try {
shell.execute( "DROP INDEX ON :Person(age)" );
} catch ( Exception e ) {
// ignore if the index didn't exist
}

shell.execute( "CREATE INDEX ON :Person(age)" );
shell.execute( "CALL db.awaitIndexes()" );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public interface DatabaseManager

String DATABASE_UNAVAILABLE_ERROR_CODE = "Neo.TransientError.General.DatabaseUnavailable";

/**
* Sets the active database name as set by the user.
* If the current state is connected, try to reconnect to that database.
* If the current state is disconnected, simply update `activeDatabaseAsSetByUser`.
*/
void setActiveDatabase(String databaseName) throws CommandException;

String getActiveDatabaseAsSetByUser();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.neo4j.shell.state;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -54,7 +55,7 @@ public class BoltStateHandler implements TransactionHandler, Connector, Database
private String activeDatabaseNameAsSetByUser;
private String actualDatabaseNameAsReportedByServer;
private final boolean isInteractive;
private Bookmark systemBookmark;
private final Map<String, Bookmark> bookmarks = new HashMap<>();

public BoltStateHandler(boolean isInteractive) {
this(GraphDatabase::driver, isInteractive);
Expand All @@ -77,15 +78,15 @@ public void setActiveDatabase(String databaseName) throws CommandException
activeDatabaseNameAsSetByUser = databaseName;
try {
if (isConnected()) {
reconnect(true);
reconnect(databaseName, previousDatabaseName);
}
}
catch (ClientException e) {
if (isInteractive) {
// We want to try to connect to the previous database
activeDatabaseNameAsSetByUser = previousDatabaseName;
try {
reconnect(true);
reconnect(previousDatabaseName, previousDatabaseName);
}
catch (Exception e2) {
e.addSuppressed(e2);
Expand Down Expand Up @@ -158,10 +159,11 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
}
final AuthToken authToken = AuthTokens.basic(connectionConfig.username(), connectionConfig.password());
try {
String previousDatabaseName = activeDatabaseNameAsSetByUser;
try {
setActiveDatabase(connectionConfig.database());
activeDatabaseNameAsSetByUser = connectionConfig.database();
driver = getDriver(connectionConfig, authToken);
reconnect(command);
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName, command);
} catch (org.neo4j.driver.exceptions.ServiceUnavailableException e) {
String scheme = connectionConfig.scheme();
String fallbackScheme;
Expand All @@ -188,7 +190,7 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
connectionConfig.encryption(),
connectionConfig.database());
driver = getDriver(connectionConfig, authToken);
reconnect();
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName);
}
} catch (Throwable t) {
try {
Expand All @@ -200,35 +202,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
}
}

private void reconnect() throws CommandException {
reconnect(true, null);
private void reconnect(String databaseToConnectTo, String previousDatabase) throws CommandException {
reconnect(databaseToConnectTo, previousDatabase, null);
}

private void reconnect(ThrowingAction<CommandException> command) throws CommandException {
reconnect(true, command);
}

private void reconnect(boolean keepBokmark) throws CommandException {
reconnect(keepBokmark, null);
}

private void reconnect( boolean keepBookmark, ThrowingAction<CommandException> command ) throws CommandException {
private void reconnect( String databaseToConnectTo,
String previousDatabase,
ThrowingAction<CommandException> command ) throws CommandException {
SessionConfig.Builder builder = SessionConfig.builder();
builder.withDefaultAccessMode( AccessMode.WRITE );
if ( !ABSENT_DB_NAME.equals( activeDatabaseNameAsSetByUser ) )
if ( !ABSENT_DB_NAME.equals( databaseToConnectTo ) )
{
builder.withDatabase( activeDatabaseNameAsSetByUser );
builder.withDatabase( databaseToConnectTo );
}
if ( session != null && keepBookmark )
closeSession( previousDatabase );
final Bookmark bookmarkForDBToConnectTo = bookmarks.get( databaseToConnectTo );
if ( bookmarkForDBToConnectTo != null )
{
// Save the last bookmark and close the session
final Bookmark bookmark = session.lastBookmark();
session.close();
builder.withBookmarks( bookmark );
}
else if ( systemBookmark != null )
{
builder.withBookmarks( systemBookmark );
builder.withBookmarks( bookmarkForDBToConnectTo );
}

session = driver.session( builder.build() );
Expand All @@ -237,6 +228,22 @@ else if ( systemBookmark != null )
connect(command);
}

/**
* Closes the session, if there is any.
* Saves a bookmark for the database currently connected to.
* @param databaseName the name of the database currently connected to
*/
private void closeSession( String databaseName )
{
if ( session != null )
{
// Save the last bookmark and close the session
final Bookmark bookmarkForPreviousDB = session.lastBookmark();
session.close();
bookmarks.put(databaseName, bookmarkForPreviousDB);
}
}

private void connect( ThrowingAction<CommandException> command) throws CommandException
{
ThrowingAction<CommandException> toCall = command == null ? getPing() : () ->
Expand Down Expand Up @@ -314,7 +321,7 @@ public Optional<BoltResult> runCypher(@Nonnull String cypher,
} catch (SessionExpiredException e) {
// Server is no longer accepting writes, reconnect and try again.
// If it still fails, leave it up to the user
reconnect();
reconnect(activeDatabaseNameAsSetByUser, activeDatabaseNameAsSetByUser);
return getBoltResult(cypher, queryParams);
}
}
Expand All @@ -339,10 +346,9 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
try {
driver = getDriver(connectionConfig, authToken);

SessionConfig.Builder builder = SessionConfig.builder()
.withDefaultAccessMode(AccessMode.WRITE)
.withDatabase(SYSTEM_DB_NAME);
session = driver.session(builder.build());
activeDatabaseNameAsSetByUser = SYSTEM_DB_NAME;
// Supply empty command, so that we do not run ping.
reconnect( SYSTEM_DB_NAME, SYSTEM_DB_NAME, () -> {} );

String command;
Value parameters;
Expand All @@ -361,17 +367,19 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
connectionConfig.setPassword(connectionConfig.newPassword());
connectionConfig.setNewPassword(null);

// Save a system bookmark to make sure we wait for the password change to propagate on reconnection
systemBookmark = session.lastBookmark();

silentDisconnect();
} catch (Throwable t) {
try {
silentDisconnect();
} catch (Exception e) {
t.addSuppressed(e);
}
throw t;
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
// The only checked exception is CommandException and we know that
// we cannot get that since we supply an empty command.
throw new RuntimeException(t);
}
}

Expand Down Expand Up @@ -404,9 +412,7 @@ private void resetActualDbName() {
*/
void silentDisconnect() {
try {
if (session != null) {
session.close();
}
closeSession( activeDatabaseNameAsSetByUser );
if (driver != null) {
driver.close();
}
Expand Down
Loading

0 comments on commit 437244d

Please sign in to comment.