Пара примеров работы с Couchbase на языке Scala

15 июля 2015

На момент написания этих строк существует как минимум два SDK для работы с Couchbase на языках под JVM — как бы старый SDK 1.4 и новый SDK 2.1. SDK версий 1.x основан на spymemcached, и потому практически такой же, включая всякие некрасивости вроде преобразований asInstanceOf. А вот SDK версий 2.x стильный, модный, молодежный, c RxJava внутри.

Раз со spymemcached мы уже работали, в рамках это заметки все внимание будет уделено SDK 2.x. Однако в полной версии исходников к посту на GitHub вы найдете версию кода как для SDK 1.4, так и для SDK 2.1. Кстати, обратите внимание на использование возможности SBT создавать multi-project. Если вдруг вы пропустили заметку про Couchbase, зачем он нужен, и как его установить, то вот эта заметка: Как я поднимал Couchbase-кластер в Амазоне.

Итак, указываем артефакт в зависимостях проекта:

libraryDependencies ++= Seq(
  "com.couchbase.client" % "java-client" % "2.1.3"
)

Нам понадобятся следующие импорты:

import rx._
import rx.functions._
import com.couchbase.client.java._
import com.couchbase.client.java.document._

import scala.collection.JavaConverters._
import scala.collection._
import scala.compat.Platform

Подключаемся к кластеру, указав IP нескольких узлов:

val cluster = CouchbaseCluster.create("10.110.0.10", "10.110.0.11")

Выбираем бакет:

val bucket = cluster.openBucket("default", "")

Пример добавления нового документа:

bucket.insert(StringDocument.create("key", "value"))

Обратите внимание, что метод insert создает новый документ только в случае, если его еще не существует. Чтобы создать документ или изменить, если он уже существует, используйте метод upsert. Также есть метод replace, который только изменяет документ. Если документа не существует, replace бросает исключение.

Считать документ по ключу:

val value = bucket.get(StringDocument.create(key)).content()

Обновление счетчика и получение нового его значения:

val counter = bucket.counter(counterName, +1L).content()

А примерно так многословно пишется multiget:

val readRes: Map[String, String] = {
  Observable
    .from(Seq("key-1", "key-2", "key-3").asJavaCollection)
    .flatMap(
      new Func1[String, Observable[StringDocument]]() {
        override def call(id: String): Observable[StringDocument] = {
          bucket.async().get(StringDocument.create(id))
        }
      })
    .toList.toBlocking.single.asScala
    .map(doc => doc.id -> doc.content).toMap
}

К счастью, можно один раз написать небольшой метод-обертку и забыть. Если по одному из ключей в базе нет документа, никакого исключения или вроде того брошено не будет. Просто в полученном Map’е тоже не будет соответствующего ключа.

Как видите, все довольно просто. В действительности, Couchbase SDK намного мощнее, чем описано в этой заметке. Однако другие его возможности, как всегда, я оставляю вам для самостоятельного изучения.

Примите во внимание, что приведенный выше код блокирующий. Если вдруг вы возьмете его за основу в своем проекте, не забудьте создать трэдпул. Также не могу не отметить, что по моему синтетическому и ничего не означающему бенчмарку SDK 2.x оказался заметно медленнее SDK 1.x. Поэтому в вашем проекте вам может захотеться все-таки использовать SDK 1.x. Но не верьте мне на слово — проверяйте сами на вашем окружении. Следует также отметить, что все Java-клиенты к Couchbase являются thread safe, и при этом не имеют внутри какой-то особо хитрой логики вроде пула коннектов, как, например, в клиенте для Java к Cassandra.

А какой SDK используете вы, 1.x или 2.x?

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.