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

Commit 7839901

Browse files
authored
Merge pull request #225 from sherfert/4.1-bookmark-per-db
Keep one bookmark per database.
2 parents 67a72b9 + 18f3a2e commit 7839901

File tree

5 files changed

+182
-51
lines changed

5 files changed

+182
-51
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ public void cypherWithOrder() throws CommandException {
190190
String serverVersion = shell.getServerVersion();
191191
assumeThat( version(serverVersion), greaterThanOrEqualTo(version("3.6")));
192192

193+
// Make sure we are creating a new NEW index
194+
try {
195+
shell.execute( "DROP INDEX ON :Person(age)" );
196+
} catch ( Exception e ) {
197+
// ignore if the index didn't exist
198+
}
199+
193200
shell.execute( "CREATE INDEX ON :Person(age)" );
194201
shell.execute( "CALL db.awaitIndexes()" );
195202

@@ -262,8 +269,8 @@ public void cypherWithProfileWithMemory() throws CommandException {
262269

263270
//then
264271
String actual = linePrinter.output();
265-
assertThat(actual, containsString("| Plan | Statement | Version | Planner | Runtime | Time | DbHits | Rows | Memory (Bytes) |")); // First table
266-
assertThat(actual, containsString("| Operator | Details | Estimated Rows | Rows | DB Hits | Cache H/M | Memory (Bytes) |")); // Second table
272+
assertThat(actual.replace( " ", "" ), containsString("|Plan|Statement|Version|Planner|Runtime|Time|DbHits|Rows|Memory(Bytes)|")); // First table
273+
assertThat(actual.replace( " ", "" ), containsString("|Operator|Details|EstimatedRows|Rows|DBHits|CacheH/M|Memory(Bytes)|")); // Second table
267274
}
268275

269276
@Test

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,5 +1,6 @@
11
package org.neo4j.shell.state;
22

3+
import java.util.HashMap;
34
import java.util.List;
45
import java.util.Map;
56
import java.util.Optional;
@@ -51,7 +52,7 @@ public class BoltStateHandler implements TransactionHandler, Connector, Database
5152
private String activeDatabaseNameAsSetByUser;
5253
private String actualDatabaseNameAsReportedByServer;
5354
private final boolean isInteractive;
54-
private Bookmark systemBookmark;
55+
private final Map<String, Bookmark> bookmarks = new HashMap<>();
5556
private Transaction tx = null;
5657

5758
public BoltStateHandler(boolean isInteractive) {
@@ -75,15 +76,15 @@ public void setActiveDatabase(String databaseName) throws CommandException
7576
activeDatabaseNameAsSetByUser = databaseName;
7677
try {
7778
if (isConnected()) {
78-
reconnect(true);
79+
reconnect(databaseName, previousDatabaseName);
7980
}
8081
}
8182
catch (ClientException e) {
8283
if (isInteractive) {
8384
// We want to try to connect to the previous database
8485
activeDatabaseNameAsSetByUser = previousDatabaseName;
8586
try {
86-
reconnect(true);
87+
reconnect(previousDatabaseName, previousDatabaseName);
8788
}
8889
catch (Exception e2) {
8990
e.addSuppressed(e2);
@@ -161,10 +162,11 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
161162
}
162163
final AuthToken authToken = AuthTokens.basic(connectionConfig.username(), connectionConfig.password());
163164
try {
165+
String previousDatabaseName = activeDatabaseNameAsSetByUser;
164166
try {
165-
setActiveDatabase(connectionConfig.database());
167+
activeDatabaseNameAsSetByUser = connectionConfig.database();
166168
driver = getDriver(connectionConfig, authToken);
167-
reconnect(command);
169+
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName, command);
168170
} catch (org.neo4j.driver.exceptions.ServiceUnavailableException e) {
169171
String scheme = connectionConfig.scheme();
170172
String fallbackScheme;
@@ -191,7 +193,7 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
191193
connectionConfig.encryption(),
192194
connectionConfig.database());
193195
driver = getDriver(connectionConfig, authToken);
194-
reconnect(command);
196+
reconnect(activeDatabaseNameAsSetByUser, previousDatabaseName, command);
195197
}
196198
} catch (Throwable t) {
197199
try {
@@ -203,35 +205,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
203205
}
204206
}
205207

206-
private void reconnect() throws CommandException {
207-
reconnect(true, null);
208+
private void reconnect(String databaseToConnectTo, String previousDatabase) throws CommandException {
209+
reconnect(databaseToConnectTo, previousDatabase, null);
208210
}
209211

210-
private void reconnect(ThrowingAction<CommandException> command) throws CommandException {
211-
reconnect(true, command);
212-
}
213-
214-
private void reconnect(boolean keepBokmark) throws CommandException {
215-
reconnect(keepBokmark, null);
216-
}
217-
218-
private void reconnect( boolean keepBookmark, ThrowingAction<CommandException> command ) throws CommandException {
212+
private void reconnect( String databaseToConnectTo,
213+
String previousDatabase,
214+
ThrowingAction<CommandException> command ) throws CommandException {
219215
SessionConfig.Builder builder = SessionConfig.builder();
220216
builder.withDefaultAccessMode( AccessMode.WRITE );
221-
if ( !ABSENT_DB_NAME.equals( activeDatabaseNameAsSetByUser ) )
217+
if ( !ABSENT_DB_NAME.equals( databaseToConnectTo ) )
222218
{
223-
builder.withDatabase( activeDatabaseNameAsSetByUser );
219+
builder.withDatabase( databaseToConnectTo );
224220
}
225-
if ( session != null && keepBookmark )
221+
closeSession( previousDatabase );
222+
final Bookmark bookmarkForDBToConnectTo = bookmarks.get( databaseToConnectTo );
223+
if ( bookmarkForDBToConnectTo != null )
226224
{
227-
// Save the last bookmark and close the session
228-
final Bookmark bookmark = session.lastBookmark();
229-
session.close();
230-
builder.withBookmarks( bookmark );
231-
}
232-
else if ( systemBookmark != null )
233-
{
234-
builder.withBookmarks( systemBookmark );
225+
builder.withBookmarks( bookmarkForDBToConnectTo );
235226
}
236227

237228
session = driver.session( builder.build() );
@@ -240,6 +231,22 @@ else if ( systemBookmark != null )
240231
connect(command);
241232
}
242233

234+
/**
235+
* Closes the session, if there is any.
236+
* Saves a bookmark for the database currently connected to.
237+
* @param databaseName the name of the database currently connected to
238+
*/
239+
private void closeSession( String databaseName )
240+
{
241+
if ( session != null )
242+
{
243+
// Save the last bookmark and close the session
244+
final Bookmark bookmarkForPreviousDB = session.lastBookmark();
245+
session.close();
246+
bookmarks.put(databaseName, bookmarkForPreviousDB);
247+
}
248+
}
249+
243250
private void connect( ThrowingAction<CommandException> command) throws CommandException
244251
{
245252
ThrowingAction<CommandException> toCall = command == null ? getPing() : () ->
@@ -323,7 +330,7 @@ public Optional<BoltResult> runCypher(@Nonnull String cypher,
323330
} catch (SessionExpiredException e) {
324331
// Server is no longer accepting writes, reconnect and try again.
325332
// If it still fails, leave it up to the user
326-
reconnect();
333+
reconnect(activeDatabaseNameAsSetByUser, activeDatabaseNameAsSetByUser);
327334
return getBoltResult(cypher, queryParams);
328335
}
329336
}
@@ -348,10 +355,9 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
348355
try {
349356
driver = getDriver(connectionConfig, authToken);
350357

351-
SessionConfig.Builder builder = SessionConfig.builder()
352-
.withDefaultAccessMode(AccessMode.WRITE)
353-
.withDatabase(SYSTEM_DB_NAME);
354-
session = driver.session(builder.build());
358+
activeDatabaseNameAsSetByUser = SYSTEM_DB_NAME;
359+
// Supply empty command, so that we do not run ping.
360+
reconnect( SYSTEM_DB_NAME, SYSTEM_DB_NAME, () -> {} );
355361

356362
String command;
357363
Value parameters;
@@ -370,17 +376,19 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
370376
connectionConfig.setPassword(connectionConfig.newPassword());
371377
connectionConfig.setNewPassword(null);
372378

373-
// Save a system bookmark to make sure we wait for the password change to propagate on reconnection
374-
systemBookmark = session.lastBookmark();
375-
376379
silentDisconnect();
377380
} catch (Throwable t) {
378381
try {
379382
silentDisconnect();
380383
} catch (Exception e) {
381384
t.addSuppressed(e);
382385
}
383-
throw t;
386+
if (t instanceof RuntimeException) {
387+
throw (RuntimeException) t;
388+
}
389+
// The only checked exception is CommandException and we know that
390+
// we cannot get that since we supply an empty command.
391+
throw new RuntimeException(t);
384392
}
385393
}
386394

@@ -419,9 +427,7 @@ private void resetActualDbName() {
419427
*/
420428
void silentDisconnect() {
421429
try {
422-
if (session != null) {
423-
session.close();
424-
}
430+
closeSession( activeDatabaseNameAsSetByUser );
425431
if (driver != null) {
426432
driver.close();
427433
}

0 commit comments

Comments
 (0)