
com.couchbase.transactions.AttemptContext Maven / Gradle / Ivy
Show all versions of couchbase-transactions Show documentation
/*
* Copyright 2021 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.transactions;
import com.couchbase.client.core.annotation.Stability;
import com.couchbase.client.core.error.CouchbaseException;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.Scope;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.transactions.log.TransactionLogger;
import com.couchbase.transactions.util.SchedulerUtil;
import java.util.Objects;
import java.util.Optional;
/**
* Provides methods to allow an application's transaction logic to read, mutate, insert and delete documents, as well
* as
* commit or rollback the transaction.
*
* These methods are blocking/synchronous. See {@link AttemptContextReactive} for the asynchronous version (which is
* the
* preferred option, and which this class largely simply wraps).
*/
public class AttemptContext {
private final AttemptContextReactive reactive;
AttemptContext(AttemptContextReactive reactive) {
Objects.requireNonNull(reactive);
this.reactive = reactive;
}
AttemptContextReactive ctx() {
return reactive;
}
/**
* Gives access to the attempt's logger, allowing application trace to be interleaved with transaction trace.
*/
public TransactionLogger logger() {
return ctx().LOGGER;
}
/**
* Gets a document from the specified Couchbase bucket
matching the specified id
. It's
* returned
* as Optional.empty() if not found.
*
* @param collection the Couchbase collection the document exists on
* @param id the document's ID
* @return an Optional
containing the document, or Optional.empty()
if not found
*/
public Optional getOptional(Collection collection, String id) {
Optional out = reactive.getOptional(collection.reactive(), id)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
// Semantics of .block() are to return null if the Mono completes with empty
if (out != null) {
return out;
} else {
return Optional.empty();
}
}
/**
* Gets a document from the specified Couchbase bucket
matching the specified id
. If
* the document
* is not
* found, a DocumentNotFoundException
is thrown. If not caught inside the transaction logic, this
* particular exception will cause the overall transaction to abort with a thrown TransactionFailed
.
*
* @param collection the Couchbase collection the document exists on
* @param id the document's ID
* @return a TransactionGetResult
containing the document
*/
public TransactionGetResult get(Collection collection, String id) {
return reactive.get(collection.reactive(), id)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Mutates the specified doc
with new content, using the
* document's last {@link TransactionGetResult#cas()}.
*
* The mutation is staged until the transaction is committed. That is, any read of the document by any Couchbase
* component will see the document's current value, rather than this staged or 'dirty' data. If the attempt is
* rolled back, the staged mutation will be removed.
*
* This staged data effectively locks the document from other transactional writes until the attempt completes
* (commits or rolls back).
*
* If the mutation fails with a CasMismatchException
, or any other exception, the transaction will
* automatically
* rollback this attempt, then retry.
*
* @param doc the doc to be updated
* @param content the content to replace the doc with. This will normally be a {@link JsonObject}.
* @param options options controlling how the document is replaced. See {@link TransactionReplaceOptions} for
* details
* @return the doc, updated with its new CAS value. For performance a copy is not created and the original doc
* object is modified.
*/
public TransactionGetResult replace(TransactionGetResult doc,
Object content,
TransactionReplaceOptions options) {
return reactive.replace(doc, content, options)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
public TransactionGetResult replace(TransactionGetResult doc, Object content) {
return reactive.replace(doc, content, TransactionReplaceOptions.DEFAULT)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Inserts a new document into the specified Couchbase collection
.
*
* As with {@link #replace}, the insert is staged until the transaction is committed. Due to technical limitations
* it is not as possible to completely hide the staged data from the rest of the Couchbase platform, as an empty
* document must be created.
*
* This staged data effectively locks the document from other transactional writes until the attempt completes
* (commits or rolls back).
*
* @param collection the Couchbase collection in which to insert the doc
* @param options options controlling how the document is inserted. See {@link TransactionInsertOptions} for
* details
* @param id the document's unique ID
* @param content the content to insert. Generally this will be a
* {@link com.couchbase.client.java.json.JsonObject}
* @return the doc, updated with its new CAS value and ID, and converted to a TransactionGetResult
*/
public TransactionGetResult insert(Collection collection, String id, Object content,
TransactionInsertOptions options) {
return reactive.insert(collection.reactive(), id, content, options)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
public TransactionGetResult insert(Collection collection, String id, Object content) {
return insert(collection, id, content, TransactionInsertOptions.DEFAULT);
}
/**
* Removes the specified doc
, using the document's last
* {@link TransactionGetResult#cas()}.
*
* As with {@link #replace}, the remove is staged until the transaction is committed. That is, the document will
* continue to exist, and the rest of the Couchbase platform will continue to see it.
*
* This staged data effectively locks the document from other transactional writes until the attempt completes
* (commits or rolls back).
*
* Note that a remove(String id)
method is not possible, as it's necessary to check a provided
* TransactionGetResult
to determine if the document is involved in another transaction.
*
* @param doc the doc to be removed
*/
public void remove(TransactionGetResult doc) {
reactive.remove(doc)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Commits the transaction. All staged replaces, inserts and removals will be written.
*
* After this, no further operations are permitted on this instance, and they will result in an
* IllegalStateException
that will, if not caught in the transaction logic, cause the transaction to
* fail
* with a TransactionFailed
exception.
*/
public void commit() {
reactive.commit()
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Defers committing this transaction.
*
* The resultant {@link TransactionResult#serialized()} will contain a serialized form of the context for this
* transaction, which can be resumed using {@link Transactions#commit} or {@link Transactions#rollback}.
*/
@Stability.Volatile
public void defer() {
reactive.defer()
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Rolls back the transaction. All staged replaces, inserts and removals will be removed. The transaction will not
* be retried, so this will be the final attempt.
*
* After this, no further operations are permitted on this instance, and they will result in an
* IllegalStateException
that will, if not caught in the transaction logic, cause the transaction to
* fail
* with a TransactionFailed
exception.
*/
public void rollback() {
reactive.rollback()
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Returns the globally unique ID of this attempt, which may be useful for debugging and logging purposes.
*/
public String attemptId() {
return reactive.attemptId();
}
/**
* Returns the globally unique ID of the overall transaction owning this attempt, which may be useful for debugging
* and logging purposes.
*/
public String transactionId() {
return reactive.transactionId();
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory. The reactive version of this API provides a streaming interface which will not/
*
* @throws CouchbaseException 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.
*/
@Stability.Uncommitted
public QueryResult query(String statement,
TransactionQueryOptions options) {
return reactive.queryBlocking(statement, options)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
QueryResult query(Scope scope,
String statement,
TransactionQueryOptions options,
boolean tximplicit) {
return reactive.queryBlocking(scope, statement, options, tximplicit)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Calls query() with default options.
*/
@Stability.Uncommitted
public QueryResult query(String statement) {
return query(statement, TransactionQueryOptions.queryOptions());
}
/**
* Runs a N1QL query and returns the result.
*
* All rows are buffered in-memory. The reactive version of this API provides a streaming interface which will not.
*
* 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.
*
* @throws CouchbaseException 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.
*/
@Stability.Uncommitted
public QueryResult query(Scope scope,
String statement,
TransactionQueryOptions options) {
return reactive.queryBlocking(scope, statement, options, false)
.publishOn(SchedulerUtil.schedulerBlocking)
.block();
}
/**
* Calls query() with default options.
*
* 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.
*/
@Stability.Uncommitted
public QueryResult query(Scope scope, String statement) {
return query(scope, statement, TransactionQueryOptions.queryOptions());
}
}