com.couchbase.client.scala.transactions.AsyncTransactionAttemptContext.scala Maven / Gradle / Ivy
Show all versions of scala-client_2.12 Show documentation
/*
* Copyright 2024 Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.couchbase.client.scala.transactions
import com.couchbase.client.core.api.query.{CoreQueryContext, CoreQueryOptions}
import com.couchbase.client.core.cnc.TracingIdentifiers.{
TRANSACTION_OP_INSERT,
TRANSACTION_OP_REMOVE,
TRANSACTION_OP_REPLACE
}
import com.couchbase.client.core.cnc.{CbTracing, RequestSpan, TracingIdentifiers}
import com.couchbase.client.core.msg.kv.CodecFlags
import com.couchbase.client.core.transaction.CoreTransactionAttemptContext
import com.couchbase.client.core.transaction.support.SpanWrapper
import com.couchbase.client.scala.codec.JsonSerializer
import com.couchbase.client.scala.env.ClusterEnvironment
import com.couchbase.client.scala.transactions.internal.EncodingUtil.encode
import com.couchbase.client.scala.util.FutureConversions
import com.couchbase.client.scala.{AsyncCollection, AsyncScope}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success};
/**
* Provides methods to allow an application's transaction logic to read, mutate, insert and delete documents.
*
* @define SupportedTypes this can be of any type for which an implicit
* `com.couchbase.client.scala.codec.Conversions.JsonSerializer` can be found: a list
* of types that are supported 'out of the box' is available at
* [[https://docs.couchbase.com/scala-sdk/current/howtos/json.html these JSON docs]]
*/
class AsyncTransactionAttemptContext private[scala] (
private val internal: CoreTransactionAttemptContext,
private val environment: ClusterEnvironment
) {
implicit val executionContext: ExecutionContext = environment.ec
/**
* Gets a document with the specified id
and from the specified Couchbase collection
.
*
* If the document does not exist it will raise a [[com.couchbase.client.core.error.DocumentNotFoundException]].
*
* @param collection the Couchbase collection the document exists on
* @param id the document's ID
* @return a TransactionGetResult
containing the document
*/
def get(collection: AsyncCollection, id: String): Future[TransactionGetResult] = {
FutureConversions
.javaMonoToScalaFuture(internal.get(collection.collectionIdentifier, id))
.map(TransactionGetResult)
}
/**
* Inserts a new document into the specified Couchbase collection
.
*
* @param collection the Couchbase collection in which to insert the doc
* @param id the document's unique ID
* @param content $SupportedTypes
* @return the doc, updated with its new CAS value and ID, and converted to a TransactionGetResult
*/
def insert[T](collection: AsyncCollection, id: String, content: T)(
implicit serializer: JsonSerializer[T]
): Future[TransactionGetResult] = {
val span = CbTracing.newSpan(internal.core().context(), TRANSACTION_OP_INSERT, internal.span())
span.lowCardinalityAttribute(TracingIdentifiers.ATTR_OPERATION, TRANSACTION_OP_INSERT)
encode(content, span, serializer, internal.core.context) match {
case Failure(exception) => Future.failed(exception)
case Success(encoded) =>
closeSpan(
span,
FutureConversions
.javaMonoToScalaFuture(
internal.insert(
collection.collectionIdentifier,
id,
encoded,
CodecFlags.JSON_COMPAT_FLAGS,
new SpanWrapper(span)
)
)
.map(TransactionGetResult)
)
}
}
/**
* Mutates the specified doc
with new content.
*
* @param doc the doc to be mutated
* @param content $SupportedTypes
* @return the doc, updated with its new CAS value. For performance a copy is not created and the original doc
* object is modified.
*/
def replace[T](doc: TransactionGetResult, content: T)(
implicit serializer: JsonSerializer[T]
): Future[TransactionGetResult] = {
val span = CbTracing.newSpan(internal.core().context(), TRANSACTION_OP_REPLACE, internal.span())
span.lowCardinalityAttribute(TracingIdentifiers.ATTR_OPERATION, TRANSACTION_OP_REPLACE)
encode(content, span, serializer, internal.core.context) match {
case Failure(exception) => Future.failed(exception)
case Success(encoded) =>
closeSpan(
span,
FutureConversions
.javaMonoToScalaFuture(
internal
.replace(doc.internal, encoded, CodecFlags.JSON_COMPAT_FLAGS, new SpanWrapper(span))
)
.map(TransactionGetResult)
)
}
}
private def closeSpan[T](
span: RequestSpan,
future: Future[TransactionGetResult]
): Future[TransactionGetResult] = {
future.onComplete {
case Failure(_) =>
span.status(RequestSpan.StatusCode.ERROR)
span.end()
case Success(_) =>
span.end()
}
future
}
/**
* Removes the specified doc
.
*
*
* @param doc - the doc to be removed
*/
def remove(doc: TransactionGetResult): Future[Unit] = {
val span = CbTracing.newSpan(internal.core().context(), TRANSACTION_OP_REMOVE, internal.span())
span.lowCardinalityAttribute(TracingIdentifiers.ATTR_OPERATION, TRANSACTION_OP_REMOVE)
val out = FutureConversions
.javaMonoToScalaFuture(internal.remove(doc.internal, new SpanWrapper(span)))
.map(_ => ())
out.onComplete {
case Failure(_) =>
span.status(RequestSpan.StatusCode.ERROR)
span.end()
case Success(_) =>
span.end()
}
out
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory.
*
* Raises [[com.couchbase.client.core.error.CouchbaseException]] or an error derived from it on failure.
* The application can choose to catch and ignore this error, and the
* transaction attempt is allowed to continue. This differs from Key-Value operations, whose failure will
* cause the attempt to fail.
*/
def query(
statement: String
): Future[TransactionQueryResult] = {
query(null, statement, null)
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory.
*
* Raises [[com.couchbase.client.core.error.CouchbaseException]] or an error derived from it on failure.
* The application can choose to catch and ignore this error, and the
* transaction attempt is allowed to continue. This differs from Key-Value operations, whose failure will
* cause the attempt to fail.
*/
def query(
statement: String,
options: TransactionQueryOptions
): Future[TransactionQueryResult] = {
query(null, statement, options)
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory.
*
* This overload performs a 'scope-level query': that is, one in which a collection may be referenced by name in the
* query statement, without needing to specify the full bucket.scope.collection syntax.
*
* Raises [[com.couchbase.client.core.error.CouchbaseException]] or an error derived from it on failure.
* The application can choose to catch and ignore this error, and the
* transaction attempt is allowed to continue. This differs from Key-Value operations, whose failure will
* cause the attempt to fail.
*/
def query(
scope: AsyncScope,
statement: String
): Future[TransactionQueryResult] = {
query(scope, statement, null)
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory.
*
* This overload performs a 'scope-level query': that is, one in which a collection may be referenced by name in the
* query statement, without needing to specify the full bucket.scope.collection syntax.
*
* Raises [[com.couchbase.client.core.error.CouchbaseException]] or an error derived from it on failure.
* The application can choose to catch and ignore this error, and the
* transaction attempt is allowed to continue. This differs from Key-Value operations, whose failure will
* cause the attempt to fail.
*/
def query(
scope: AsyncScope,
statement: String,
options: TransactionQueryOptions
): Future[TransactionQueryResult] = {
val opts: CoreQueryOptions = Option(options).map(v => v.toCore).orNull
FutureConversions
.javaMonoToScalaFuture(
internal.queryBlocking(
statement,
if (scope == null) null else CoreQueryContext.of(scope.bucketName, scope.name),
opts,
false
)
)
.map(TransactionQueryResult)
}
}