All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.spark.connection.SubdocMutationAccessor.scala Maven / Gradle / Ivy

package com.couchbase.spark.connection

import java.util.concurrent.TimeUnit

import com.couchbase.client.core.BackpressureException
import com.couchbase.client.core.time.Delay
import com.couchbase.client.java.error.{CouchbaseOutOfMemoryException, TemporaryFailureException}
import com.couchbase.client.java.subdoc.SubdocOptionsBuilder
import com.couchbase.client.java.util.retry.RetryBuilder
import com.couchbase.spark.internal.LazyIterator
import rx.lang.scala.JavaConversions.toScalaObservable
import rx.lang.scala.JavaConversions.toJavaObservable
import rx.lang.scala.Observable

import scala.concurrent.duration.Duration


sealed trait SubdocMutationSpec { def id: String }
case class SubdocUpsert(override val id: String, path: String, value: Any,
                        createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocInsert(override val id: String, path: String, value: Any,
                        createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocReplace(override val id: String, path: String, value: Any)
  extends SubdocMutationSpec
case class SubdocRemove(override val id: String, path: String) extends SubdocMutationSpec
case class SubdocCounter(override val id: String, path: String, delta: Long,
                         createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayAppend(override val id: String, path: String, value: Any,
                        createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayPrepend(override val id: String, path: String, value: Any,
                             createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayInsert(override val id: String, path: String, value: Any,
                              createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayAddUnique(override val id: String, path: String, value: Any,
                             createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayAppendAll(override val id: String, path: String, values: Seq[Any],
                                createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocArrayPrependAll(override val id: String, path: String, values: Seq[Any],
                                createParents: Boolean = true) extends SubdocMutationSpec
case class SubdocMutationResult(cas: Seq[(String, Long)])

class SubdocMutationAccessor(cbConfig: CouchbaseConfig, specs: Seq[SubdocMutationSpec],
                             bucketName: String = null, timeout: Option[Duration]) {

  def compute(): Iterator[SubdocMutationResult] = {
    if (specs.isEmpty) {
      return Iterator[SubdocMutationResult]()
    }

    val bucket = CouchbaseConnection().bucket(cbConfig, bucketName).async()
    val maxDelay = cbConfig.retryOpts.maxDelay
    val minDelay = cbConfig.retryOpts.minDelay
    val maxRetries = cbConfig.retryOpts.maxTries

    val kvTimeout = timeout
      .map(_.toMillis)
      .orElse(cbConfig.timeouts.kv)
      .getOrElse(bucket.environment().kvTimeout())



    // 1: group specs by id
    val convertedSpecs = specs.groupBy(_.id).map(tuple => {
      val specs = tuple._2
      val builder = bucket.mutateIn(tuple._1)

      specs.foreach {
        case SubdocUpsert(_, path, fragment, createParents) => builder.upsert(path, fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocInsert(_, path, fragment, createParents) => builder.insert(path, fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocReplace(_, path, fragment) => builder.replace(path, fragment)
        case SubdocCounter(_, path, delta, createParents) => builder.counter(path, delta,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayAppend(_, path, fragment, createParents) => builder.arrayAppend(path,
          fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayPrepend(_, path, fragment, createParents) => builder.arrayPrepend(path,
          fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayInsert(_, path, fragment, createParents) => builder.arrayInsert(path,
          fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayAddUnique(_, path, fragment, createParents) => builder.arrayAddUnique(path,
          fragment,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayAppendAll(_, path, fragments, createParents) => builder.arrayAppendAll(path,
          fragments,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocArrayPrependAll(_, path, fragments, createParents) => builder.arrayPrependAll(
          path, fragments,
          SubdocOptionsBuilder.builder().createPath(createParents))
        case SubdocRemove(_, path) => builder.remove(path)
      }
      builder
    }).toList

    val retry = RetryBuilder
      .anyOf(classOf[TemporaryFailureException], classOf[BackpressureException],
        classOf[CouchbaseOutOfMemoryException])
      .delay(Delay.exponential(TimeUnit.MILLISECONDS, maxDelay, minDelay))
      .max(maxRetries)
      .build()

    LazyIterator {
      Observable
        .from(convertedSpecs)
        .flatMap(builder =>
          toScalaObservable(builder.execute().timeout(kvTimeout, TimeUnit.MILLISECONDS)
          ).retryWhen(input =>
            retry.call(toJavaObservable(input))
          ))
        .toList
        .toBlocking
        .toIterable
          .map(list => {
            SubdocMutationResult(list.map(df => (df.id(), df.cas())))
          })
        .iterator
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy