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

Commit 437244d

Browse files
authored
Merge pull request #224 from sherfert/4.0-bookmark-per-db
Keep one bookmark per database.
2 parents 3dce3ec + 9306196 commit 437244d

File tree

5 files changed

+181
-50
lines changed

5 files changed

+181
-50
lines changed

cypher-shell/src/integration-test/java/org/neo4j/shell/commands/CypherShellVerboseIntegrationTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@ public void cypherWithOrder() throws CommandException {
195195
String serverVersion = shell.getServerVersion();
196196
assumeTrue((minorVersion(serverVersion) == 6 && majorVersion(serverVersion) == 3) || majorVersion(serverVersion) > 3);
197197

198+
// Make sure we are creating a new NEW index
199+
try {
200+
shell.execute( "DROP INDEX ON :Person(age)" );
201+
} catch ( Exception e ) {
202+
// ignore if the index didn't exist
203+
}
204+
198205
shell.execute( "CREATE INDEX ON :Person(age)" );
199206
shell.execute( "CALL db.awaitIndexes()" );
200207

cypher-shell/src/main/java/org/neo4j/shell/DatabaseManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ public interface DatabaseManager
1313

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

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

1823
String getActiveDatabaseAsSetByUser();

cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.neo4j.shell.state;
22

33
import java.util.ArrayList;
4+
import java.util.HashMap;
45
import java.util.List;
56
import java.util.Map;
67
import java.util.Optional;
@@ -54,7 +55,7 @@ public class BoltStateHandler implements TransactionHandler, Connector, Database
5455
private String activeDatabaseNameAsSetByUser;
5556
private String actualDatabaseNameAsReportedByServer;
5657
private final boolean isInteractive;
57-
private Bookmark systemBookmark;
58+
private final Map<String, Bookmark> bookmarks = new HashMap<>();
5859

5960
public BoltStateHandler(boolean isInteractive) {
6061
this(GraphDatabase::driver, isInteractive);
@@ -77,15 +78,15 @@ public void setActiveDatabase(String databaseName) throws CommandException
7778
activeDatabaseNameAsSetByUser = databaseName;
7879
try {
7980
if (isConnected()) {
80-
reconnect(true);
81+
reconnect(databaseName, previousDatabaseName);
8182
}
8283
}
8384
catch (ClientException e) {
8485
if (isInteractive) {
8586
// We want to try to connect to the previous database
8687
activeDatabaseNameAsSetByUser = previousDatabaseName;
8788
try {
88-
reconnect(true);
89+
reconnect(previousDatabaseName, previousDatabaseName);
8990
}
9091
catch (Exception e2) {
9192
e.addSuppressed(e2);
@@ -158,10 +159,11 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
158159
}
159160
final AuthToken authToken = AuthTokens.basic(connectionConfig.username(), connectionConfig.password());
160161
try {
162+
String previousDatabaseName = activeDatabaseNameAsSetByUser;
161163
try {
162-
setActiveDatabase(connectionConfig.database());
164+
activeDatabaseNameAsSetByUser = connectionConfig.database();
163165
driver = getDriver(connectionConfig, authToken);
164-
reconnect(command);
166+
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName, command);
165167
} catch (org.neo4j.driver.exceptions.ServiceUnavailableException e) {
166168
String scheme = connectionConfig.scheme();
167169
String fallbackScheme;
@@ -188,7 +190,7 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
188190
connectionConfig.encryption(),
189191
connectionConfig.database());
190192
driver = getDriver(connectionConfig, authToken);
191-
reconnect();
193+
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName);
192194
}
193195
} catch (Throwable t) {
194196
try {
@@ -200,35 +202,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
200202
}
201203
}
202204

203-
private void reconnect() throws CommandException {
204-
reconnect(true, null);
205+
private void reconnect(String databaseToConnectTo, String previousDatabase) throws CommandException {
206+
reconnect(databaseToConnectTo, previousDatabase, null);
205207
}
206208

207-
private void reconnect(ThrowingAction<CommandException> command) throws CommandException {
208-
reconnect(true, command);
209-
}
210-
211-
private void reconnect(boolean keepBokmark) throws CommandException {
212-
reconnect(keepBokmark, null);
213-
}
214-
215-
private void reconnect( boolean keepBookmark, ThrowingAction<CommandException> command ) throws CommandException {
209+
private void reconnect( String databaseToConnectTo,
210+
String previousDatabase,
211+
ThrowingAction<CommandException> command ) throws CommandException {
216212
SessionConfig.Builder builder = SessionConfig.builder();
217213
builder.withDefaultAccessMode( AccessMode.WRITE );
218-
if ( !ABSENT_DB_NAME.equals( activeDatabaseNameAsSetByUser ) )
214+
if ( !ABSENT_DB_NAME.equals( databaseToConnectTo ) )
219215
{
220-
builder.withDatabase( activeDatabaseNameAsSetByUser );
216+
builder.withDatabase( databaseToConnectTo );
221217
}
222-
if ( session != null && keepBookmark )
218+
closeSession( previousDatabase );
219+
final Bookmark bookmarkForDBToConnectTo = bookmarks.get( databaseToConnectTo );
220+
if ( bookmarkForDBToConnectTo != null )
223221
{
224-
// Save the last bookmark and close the session
225-
final Bookmark bookmark = session.lastBookmark();
226-
session.close();
227-
builder.withBookmarks( bookmark );
228-
}
229-
else if ( systemBookmark != null )
230-
{
231-
builder.withBookmarks( systemBookmark );
222+
builder.withBookmarks( bookmarkForDBToConnectTo );
232223
}
233224

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

231+
/**
232+
* Closes the session, if there is any.
233+
* Saves a bookmark for the database currently connected to.
234+
* @param databaseName the name of the database currently connected to
235+
*/
236+
private void closeSession( String databaseName )
237+
{
238+
if ( session != null )
239+
{
240+
// Save the last bookmark and close the session
241+
final Bookmark bookmarkForPreviousDB = session.lastBookmark();
242+
session.close();
243+
bookmarks.put(databaseName, bookmarkForPreviousDB);
244+
}
245+
}
246+
240247
private void connect( ThrowingAction<CommandException> command) throws CommandException
241248
{
242249
ThrowingAction<CommandException> toCall = command == null ? getPing() : () ->
@@ -314,7 +321,7 @@ public Optional<BoltResult> runCypher(@Nonnull String cypher,
314321
} catch (SessionExpiredException e) {
315322
// Server is no longer accepting writes, reconnect and try again.
316323
// If it still fails, leave it up to the user
317-
reconnect();
324+
reconnect(activeDatabaseNameAsSetByUser, activeDatabaseNameAsSetByUser);
318325
return getBoltResult(cypher, queryParams);
319326
}
320327
}
@@ -339,10 +346,9 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
339346
try {
340347
driver = getDriver(connectionConfig, authToken);
341348

342-
SessionConfig.Builder builder = SessionConfig.builder()
343-
.withDefaultAccessMode(AccessMode.WRITE)
344-
.withDatabase(SYSTEM_DB_NAME);
345-
session = driver.session(builder.build());
349+
activeDatabaseNameAsSetByUser = SYSTEM_DB_NAME;
350+
// Supply empty command, so that we do not run ping.
351+
reconnect( SYSTEM_DB_NAME, SYSTEM_DB_NAME, () -> {} );
346352

347353
String command;
348354
Value parameters;
@@ -361,17 +367,19 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
361367
connectionConfig.setPassword(connectionConfig.newPassword());
362368
connectionConfig.setNewPassword(null);
363369

364-
// Save a system bookmark to make sure we wait for the password change to propagate on reconnection
365-
systemBookmark = session.lastBookmark();
366-
367370
silentDisconnect();
368371
} catch (Throwable t) {
369372
try {
370373
silentDisconnect();
371374
} catch (Exception e) {
372375
t.addSuppressed(e);
373376
}
374-
throw t;
377+
if (t instanceof RuntimeException) {
378+
throw (RuntimeException) t;
379+
}
380+
// The only checked exception is CommandException and we know that
381+
// we cannot get that since we supply an empty command.
382+
throw new RuntimeException(t);
375383
}
376384
}
377385

@@ -404,9 +412,7 @@ private void resetActualDbName() {
404412
*/
405413
void silentDisconnect() {
406414
try {
407-
if (session != null) {
408-
session.close();
409-
}
415+
closeSession( activeDatabaseNameAsSetByUser );
410416
if (driver != null) {
411417
driver.close();
412418
}

0 commit comments

Comments
 (0)