Skip to content

JAVA-3041 Guava session - cloud bundle support. replaced deprecated constructors#1606

Merged
absurdfarce merged 2 commits into
apache:4.xfrom
smatvienko-tb:guava-session-cloud-bundle-support
Sep 7, 2022
Merged

JAVA-3041 Guava session - cloud bundle support. replaced deprecated constructors#1606
absurdfarce merged 2 commits into
apache:4.xfrom
smatvienko-tb:guava-session-cloud-bundle-support

Conversation

@smatvienko-tb

Copy link
Copy Markdown
Contributor

Guava session - cloud bundle support

Hey from ThingsBoard team!
I want to share my experience connecting Datastax Astra DB cloud with GuavaSession using Thingsboard.

The old GuavaSession implementation does not connect with cloud Astra DB.
This is because deprecated constructors were in place.
The paper that describes how to implement GuavaSession instead CqlSession is here:
https://docs.datastax.com/en/developer/java-driver/4.4/manual/developer/request_execution/

Here are the screenshots before I tried to connect with the old version:

image

com.datastax.oss.driver.api.core.AllNodesFailedException: Could not reach any contact point, make sure you've provided valid addresses (showing first 3 nodes, use getAllErrors() for more): Node(endPoint=8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com:29042:a837fe38-9fd5-49a6-958e-7f7ca71ae890, hostId=null, hashCode=30c14db4): [com.datastax.oss.driver.api.core.connection.ConnectionInitException: [s0|control|id: 0x70374974, L:/10.8.0.66:19408 - R:8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com/34.77.135.39:29042] Protocol initialization request, step 1 (OPTIONS): unexpected failure (com.datastax.oss.driver.api.core.connection.ClosedConnectionException: Lost connection to remote peer)], Node(endPoint=8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com:29042:ba266e06-dfa4-4171-88cb-75758881bcba, hostId=null, hashCode=4ea6381e): [com.datastax.oss.driver.api.core.connection.ConnectionInitException: [s0|control|id: 0xd498c0a3, L:/10.8.0.66:12650 - R:8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com/34.78.6.86:29042] Protocol initialization request, step 1 (OPTIONS): unexpected failure (com.datastax.oss.driver.api.core.connection.ClosedConnectionException: Lost connection to remote peer)], Node(endPoint=8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com:29042:b16cc260-ee81-4017-b6cf-6b793a5b9935, hostId=null, hashCode=61928b01): [com.datastax.oss.driver.api.core.connection.ConnectionInitException: [s0|control|id: 0x0509e0f8, L:/10.8.0.66:39028 - R:8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com/34.140.171.129:29042] Protocol initialization request, step 1 (OPTIONS): unexpected failure (com.datastax.oss.driver.api.core.connection.ClosedConnectionException: Lost connection to remote peer)]

	at com.datastax.oss.driver.api.core.AllNodesFailedException.copy(AllNodesFailedException.java:141)
	at com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures.getUninterruptibly(CompletableFutures.java:149)
	at com.datastax.oss.driver.api.core.session.SessionBuilder.build(SessionBuilder.java:835)
	at com.datastax.oss.driver.core.session.GuavaToAstraConnectTest.astraConnectWithGuava(GuavaToAstraConnectTest.java:59)
...	
	Suppressed: com.datastax.oss.driver.api.core.connection.ConnectionInitException: [s0|control|id: 0x70374974, L:/10.8.0.66:19408 - R:8ca57896-18c4-4a61-bb26-e2fc05985081-europe-west1.db.astra.datastax.com/34.77.135.39:29042] Protocol initialization request, step 1 (OPTIONS): unexpected failure (com.datastax.oss.driver.api.core.connection.ClosedConnectionException: Lost connection to remote peer)
		at com.datastax.oss.driver.internal.core.channel.ProtocolInitHandler$InitRequest.fail(ProtocolInitHandler.java:356)
		at com.datastax.oss.driver.internal.core.channel.ChannelHandlerRequest.onFailure(ChannelHandlerRequest.java:104)
		at com.datastax.oss.driver.internal.core.channel.InFlightHandler.fail(InFlightHandler.java:381)
		at com.datastax.oss.driver.internal.core.channel.InFlightHandler.abortAllInFlight(InFlightHandler.java:371)
		at com.datastax.oss.driver.internal.core.channel.InFlightHandler.abortAllInFlight(InFlightHandler.java:353)
		at com.datastax.oss.driver.internal.core.channel.InFlightHandler.channelInactive(InFlightHandler.java:331)
...
	Caused by: com.datastax.oss.driver.api.core.connection.ClosedConnectionException: Lost connection to remote peer

Now it works like a charm.

image

Here is the code example of how I tested the Astra cloud connection with CqlSession and guavaSession

package com.datastax.oss.driver.core.session;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.example.guava.api.GuavaSession;
import com.datastax.oss.driver.example.guava.api.GuavaSessionUtils;
import org.junit.Test;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;

public class GuavaToAstraConnectTest {

  static final Path CLOUD_CONFIG_PATH = Paths.get("/home/your_user/projects/astra/secure-connect-thingsboard.zip");
  static final String CLIENT_ID = "tQPHMztQPHMztQPHMztQPHMz";
  static final String SECRET_ID = "5ejkcF9F.j6f64j71Sr.tiRe0Fsq25ejkcF9F.j6f64j71Sr.tiRe0Fsq25ejkcF9F.j6f64j71Sr.tiRe0Fsq2";

  @Test
  public void astraConnectCqlSession() {
    try (CqlSession session = CqlSession.builder()
            .withCloudSecureConnectBundle(CLOUD_CONFIG_PATH)
            .withAuthCredentials(CLIENT_ID, SECRET_ID)
            .build()) {
      ResultSet rs = session.execute("select release_version from system.local");
      Row row = rs.one();
      
      if (row != null) {
        System.out.println(row.getString("release_version"));
      } else {
        System.out.println("An error occurred.");
      }
    }
  }

  @Test
  public void astraConnectWithGuava() throws ExecutionException, InterruptedException {
    try (GuavaSession session = GuavaSessionUtils.builder()
            .withCloudSecureConnectBundle(CLOUD_CONFIG_PATH)
            .withAuthCredentials(CLIENT_ID, SECRET_ID)
            .build()) {
      AsyncResultSet rs = session.executeAsync("select release_version from system.local").get();
      Row row = rs.one();
      if (row != null) {
        System.out.println(row.getString("release_version"));
      } else {
        System.out.println("An error occurred.");
      }
    }
  }
}

@ErickRamirezAU

Copy link
Copy Markdown

Hello, @smatvienko-tb. Mica brought this to our attention. Thanks for submitting a PR. And thanks for completing the contributor license agreement (CLA).

Just wondering if you've logged a jira for this. I tried to do a search but couldn't find one. You can log a ticket here and link the PR.

We're a little behind but I'll get someone to do a review and let you know. Cheers! 🍻

@smatvienko-tb

Copy link
Copy Markdown
Contributor Author

Hi, @ErickRamirezAU!
Thanks for the feedback!

Here is the new Jira ticket for this PR: https://datastax-oss.atlassian.net/browse/JAVA-3041

Stay tuned!

@smatvienko-tb smatvienko-tb changed the title Guava session - cloud bundle support. replaced deprecated constructors JAVA-3041 Guava session - cloud bundle support. replaced deprecated constructors Aug 21, 2022
@ErickRamirezAU

Copy link
Copy Markdown

Here is the new Jira ticket for this PR: https://datastax-oss.atlassian.net/browse/JAVA-3041

Thanks! 👍

@absurdfarce

absurdfarce commented Aug 22, 2022

Copy link
Copy Markdown
Contributor

So what appears to be happening here is that the necessary config vals for Astra aren't passed through by the old constructor-based mechanism. SessionBuilder.buildContext() will prefer to use the constructor option if it returns a non-null value; this allows subclasses to define their own context impls. Problem is that the constructor option doesn't allow for every option to be passed; it looks to have been deprecated before some were added, most relevant to our case before the Astra config values were updated. And the calling code in this case builds up most of it's data in programmatic arguments before calling buildContext(). So if buildContext() doesn't have every value to be preserved passed as an argument it won't be able to create a ProgrammaticBuilder instance that includes them. Upshot: any values that aren't passed as args to buildContext() are basically left out.

To prove this out try the following change:

diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java
index 966372fa2..a6eaa0c99 100644
--- a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java
+++ b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java
@@ -60,6 +60,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Callable;
@@ -900,8 +901,10 @@ public abstract class SessionBuilder<SelfT extends SessionBuilder, SessionT> {
             CqlIdentifier.fromCql(defaultConfig.getString(DefaultDriverOption.SESSION_KEYSPACE));
       }
 
+      DriverContext ctx = buildContext(configLoader, programmaticArguments);
+      Optional<SslEngineFactory> sslFactory = ctx.getSslEngineFactory();
       return DefaultSession.init(
-          (InternalDriverContext) buildContext(configLoader, programmaticArguments),
+              (InternalDriverContext) ctx,
           contactPoints,
           keyspace);

Take a look at sslFactory in a debugger. When using the current 4.14.1 impl this value is an empty option; the SSL factory configs aren't preserved in the args passed to GuavaSessionBuilder.buildSession(). Applying the changes in this PR you'll see that the sslFactory is now a non-empty value. This also explains the observed error; because the SSL configs aren't included Astra hangs up before we even get a chance to pass in an OPTIONS message.

Note that it's the combination of the Guava session with Astra that brings out the problem here. The example in the docs should work fine if Astra isn't involved.

Need to think a bit about the best way to address this problem. It's probably okay to make the change contained in this PR but at a minimum we'll also want to update the docs at the same time.

@smatvienko-tb

Copy link
Copy Markdown
Contributor Author

You are right. If the Guava uses the old super constructor and returns non-null, the new features are not reachable.
image
image

@smatvienko-tb

Copy link
Copy Markdown
Contributor Author

The documentation seems to be fine. It does not expose the details and refers directly to the git repo. Probably updating the repo is just fine for this page: https://docs.datastax.com/en/developer/java-driver/4.4/manual/developer/request_execution/
image

Connecting to the Astra example is welcomed.
We found that cloud Keyspace has to be created manually, and install scripts have to not create keyspace for the cloud.
It is ok, but not clear when we have a first experience with the cloud.
And for the cloud we should not use the local DC, keyspace, URL, and SSL parameters that come from the cloud.
It is also not clear on the first steps after using the local Cassandra.

Probably, the better compatibility with the install scripts for local Cassandra will be accepting "CREATE KEYSPACE IF NOT EXISTS" and report success if the keyspace already exists.

@absurdfarce

Copy link
Copy Markdown
Contributor

Apologies @smatvienko-tb , I've been off working on other projects for the past little while. :(

It's the docs you cite above that originally had me concerned. Note the ambiguity of the "buildContext(...)" call in the sample code you cited:

public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> {

  @Override
  protected DriverContext buildContext( ... ) { ... }

  @Override
  protected GuavaSession wrap(CqlSession defaultSession) {
    return new DefaultGuavaSession(defaultSession);
  }

My concern was that this code wasn't clearly steering users towards the ProgrammaticArguments version introduced in JAVA-2315. The more I look at it there more I'm inclined to agree with you, though; I think the docs are probably fine. They point users at the buildContext() method generally, and from there it should be fairly clear which method should be overridden... especially if our sample code now uses the correct method (which it does with your changes in this PR).

I may still rethink this and change the docs later to show an explicit override of buildContext(ProgrammaticArguments) but it's probably okay for now.

@absurdfarce absurdfarce merged commit 03961c9 into apache:4.x Sep 7, 2022
@absurdfarce

Copy link
Copy Markdown
Contributor

Thanks for the contribution (and for the dialogue) @smatvienko-tb !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants