Статьи

Couchbase Java SDK 2.0.0 Developer Preview 2

Первоначально написано Майкл Nitschinger

От имени всей команды SDK я рад объявить о втором предварительном просмотре выпуска релиза Java / JVM SDK под названием Armstrong . В настоящее время он содержит как базовый пакет JVM «core-io» 0.2, так и предварительный просмотр Java SDK 2.0. Помимо значительного повышения зрелости по сравнению с первым предварительным просмотром для разработчиков, теперь он обеспечивает поддержку ребалансировки , добавляет API-интерфейсы как для View, так и для DSL N1QL , имеет реализована команда сброса и многое другое. Эта запись блога проведет вас через самые важные моменты.

Прежде чем мы углубимся в новые функции, вот как вы можете получить это:

<dependencies>
    <dependency>
        <groupId>com.couchbase.client</groupId>
        <artifactId>couchbase-client</artifactId>
        <version>2.0.0-dp2</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>couchbase</id>
        <name>couchbase repo</name>
        <url>http://files.couchbase.com/maven2</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>

логирование

Хотя новый SDK и не является новой функцией, он теперь использует исключительно SLF4J. Поэтому, если вы хотите включить ведение журнала, вы можете добавить зависимость для ведения журнала следующим образом:

<dependencies>
  <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.2</version>
  </dependency>
</dependencies>

Это даст вам запись уровня DEBUG по умолчанию, но вы можете использовать конфигурацию обратного входа, чтобы изменить ее на уровень INFO, если вам нужно (logback.xml):

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- set level to trace for packet-level output -->
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Простая настройка

Первый предварительный просмотр разработчика уже имел его, но мы не объявили об этом в то время. Для этого нового SDK мы используем библиотеку Typesafe Config , которая предоставляет универсальный способ настройки вашего приложения и библиотек. Мы, безусловно, считаем, что это также библиотека, которую вы можете повторно использовать в своих собственных приложениях, поскольку она предоставляет широкий спектр полезных методов и параметров конфигурации. Вот некоторые возможности в их порядке поиска:

  1. свойства системы
  2. application.conf (все ресурсы в classpath с этим именем)
  3. application.json (все ресурсы в classpath с этим именем)
  4. application.properties (все ресурсы в classpath с этим именем)
  5. reference.conf (все ресурсы в classpath с этим именем)

The SDK ships with a reference.conf as the default implementation, so all system properties or config files that you put in your classpath will override them. It’s as easy as this. As of dp2, the `reference.conf` has lots of options, here are the most common one to tune:

com.couchbase.client {

    queryEnabled = false

    bootstrap {
        sslEnabled = false
        sslKeystoreFile = ""
        sslKeystorePassword = ""

        http {
            enabled = true
        }

        carrier {
            enabled = true
        }
    }

    io.poolSize = 0
}

You can see that the couchbase settings are properly namespaced so they don’t interfer with the ones in your application. Let’s say you want to enable the experimental N1QL querying. You can do that either through an application.conf file (if you are using maven, just put it in the resources folder) like this:

com.couchbase.client.queryEnabled = true

or

com.couchbase.client {
        queryEnabled = true
}

Or if you want to use a system property (make sure to set it before you initialize the cluster object):

System.setProperty("com.couchbase.client.queryEnabled", "true");

All options will be properly documented in the future, but with these instructions it should be easy to override any configuration setting that you need. One note of warning though: override only those settings which you understand the impact of. There are also advanced settings to tune like ring buffer sizes which can have an impact on how your application behaves in certain situations and environments.

Rebalance support

The core package has been extended to support rebalance (adding, removing, failing over nodes) while it is running, so it is now possible to change your cluster configuration while you are running your workloads. This is something that we mark as «basic functionality» and should work with all different setups, but it is still undergoing testing. Please file any bugs that you find, especially around rebalance. We want to make sure that this is rock solid when we release the final version.

Note that there is a known issue that in some cases, operations are never completed. Make sure for this developer preview that you always chain a `timeout` in your observable to make sure your codepath moves forward even if the operation does not complete during rebalance.

Observable<JsonDocument> doc = bucket.get("key").timeout(5, TimeUnit.SECONDS);

Also, if you have N1QL enabled, make sure that the query server is running on every node that you add to the cluster, since it tries to pick it up from there.  As N1QL integrates more closely with Couchbase Server, this will require less manual setup.

N1QL DSL

One of the major new features is a DSL for N1QL queries, which is «intelligent» in that it guides you on the supported syntax. This is inspired by JooQ and implemented through interface hierachies. You can now specify N1QL queries like this (and if you type it in your IDE see how you get autocomplete support for the allowed next steps implicitly):

System.setProperty("com.couchbase.client.queryEnabled", "true");

Cluster cluster = new CouchbaseCluster();
Bucket bucket = cluster.openBucket().toBlocking().single();

Observable<QueryResult> results = bucket.query(
    select("firstname", "lastname").from("users").where(x("age").gt(x(18)))
);

results.subscribe(result ->
    System.out.println(result.value().getString("firstname") + result.value().getString("lastname"))
);

An example of how this looks in IntelliJ, my IDE while typing:

Скриншот N1QL DSL в Intellij

There are a few things going on here in the query method itself. First, the query method uses a few static imports to make the DSL more fluent. The first one is select (from com.couchbase.client.java.query.Select.select) which starts the DSL. The other one that you can see here is «x» (from com.couchbase.client.java.query.dsl.Expression.x), which acts as a shorthand for an expression. Everywhere N1QL supports an expression, it can be expressed as a expression in the DSL as well. Expressions allow you to nicely and type-safely create different types of statements (like the one shown above). In future releases we will add more overloads and functions to provide even better support for the full N1QL specification. You can also use the «s» static import. If used as a replacement for «x», it will automatically quote your expression.

View DSL

While we did put great emphasis on a fluent N1QL DSL, we did not forget our excellent Views of course (which, spoiler alert, will get even better with the next Couchbase Server release!). The new SDK also features a View DSL, very similar to the one you’ve read just a minute ago:

Cluster cluster = new CouchbaseCluster();
Bucket bucket = cluster.openBucket().toBlocking().single();

Observable<ViewResult> query = bucket.query(ViewQuery.from("designdoc", "view").limit(10).reduce());

query.subscribe(result -> System.out.println(result.value()));

This developer preview does not have «include docs» yet, but thanks to our observables this is very easy to fix (but for sure we will add support for it soon):

Observable<ViewResult> query = bucket.query(ViewQuery.from("brewery", "by_name").limit(10).reduce());

query
    .flatMap(result -> bucket.get(result.id()))
    .subscribe(doc -> System.out.println(doc.content()));

Flush

Especially during development, and if enabled on the server side, you can flush your bucket easily. This will remove all the documents from your bucket, which is especially handy during testing.  Do be aware though that flush can take a while (this is being tracked on Couchbase Server as an issue):

bucket.flush();

We are currently using something very similar in some integration tests:

@BeforeClass
public static void connect() {
    cluster = new CouchbaseCluster(seedNode);
    bucket = cluster.openBucket(bucketName, password).toBlocking().single();
    bucket.flush().toBlocking().single();
}

SSL Encryption

There will be a separate blog post about it later, but we can’t resist giving you some hints now. The next major server version will include support for client-side encryption, and this developer preview already has the support built in.

In order to enable it, you need to do two things:

 — First, get the server certificate and add it to your JVM keystore.  (Supported releases of the server have this in the UI)
 — Second, enable it on the client side.

The first step is not specific to the couchbase SDK, and most of you will be familiar with the «keytool» command that is used to import a certificate. Here is one way to do it:

keytool -import -file <certificate>

You can also specify a custom keystore, password and so on. Also, the place of the default keystore file depends on your platform. For now, please consult the keytool docs for more info in case you need it.

The second step is to enable it in the configuration:

com.couchbase.client.bootstrap {
    sslEnabled = true
    sslKeystoreFile = /path/to/keystorefile
    sslKeystorePassword = ""
}

That’s all you need to do! Enable it, give it the path to the keystore and the password of it (optionally if set). The client will automatically negotiate everything and run the data over encrypted connections.

Next Release

The next release is most probably going to be a beta release where we aim for feature completeness (replica read, durability requirements for persistence/replication, design document management, …) and focus on first-class documentation. Please file any issues you find here and provide feedback through comments here or the developer mailing list. We also have advanced features in the pipeline like legacy and custom converters, thanks to some source code contributions already.