owned this note
owned this note
Published
Linked with GitHub
# Couchbase Transactions API
Copyright (c) 2020 Couchbase, Inc.
# Lambda
The application provides their transaction logic inside a lambda/callback. This a) gives us control over the error handling and retry strategy, e.g. we’re free to retry their logic as often as required, and can tweak this in the future. And b) frees them from the same.
Implementors should implement something close to this in their languages (full details are below, this is just to give an overview of how things look to the user - it is also now no longer relevant with ExtSDKIntegration):
```java
Transactions transactions = Transactions.create(cluster, TransactionConfigBuilder.create()
.durabilityLevel(TransactionDurabilityLevel.MAJORITY)
.build());
try {
transactions.run((ctx) -> {
// Inserting a doc:
String docId = "test-id";
ctx.insert(coll, docId, JsonObject.create());
// Getting documents:
Optional<TransactionGetResult> docOpt = ctx.getOptional(coll, docId);
TransactionGetResult doc = ctx.get(collection, docId);
// Replacing a doc:
TransactionGetResult anotherDoc = ctx.getOrError(coll, "anotherDoc");
JsonObject content = anotherDoc.contentAs(JsonObject.class);
content.put("transactions", "are awesome");
ctx.replace(anotherDoc, content);
// Removing a doc:
TransactionGetResult yetAnotherDoc = ctx.getOrError(coll, "yetAnotherDoc");
ctx.remove(yetAnotherDoc);
ctx.commit();
});
} catch (TransactionCommitAmbiguous e) {
for (LogDefer err : e.result().log().logs()) {
// Optionally, log the result
}
} catch (TransactionFailed e) {
for (LogDefer err : e.result().log().logs()) {
// Optionally, log the result
}
}
```
In ExtSDKIntegration this changes to:
```java
// This initial config section is Java-specific
ClusterEnvironment env =
ClusterEnvironment.builder()
.transactionsConfig(
TransactionsConfig.durabilityLevel(MAJORITY));
Cluster cluster = Cluster.connect(seedNodes(),
ClusterOptions
.clusterOptions(authenticator())
.environment(env));
try {
cluster.transactions().run((ctx) -> {
// The lambda internals are the same, except
// ctx.commit(), ctx.rollback() and ctx.getOptional() are removed.
});
} catch (TransactionCommitAmbiguousException e) {
// Optionally, log the result
e.logs().forEach(log -> logger.warn(log));
} catch (TransactionFailedException e) {
// Optionally, log the result
e.logs().forEach(log -> logger.warn(log));
}
```
ExtSDKIntegration integrates the transactions API into the SDKs.
This document specifies both ExtSDKIntegration and what existed before, rather than splitting them into separate documents, as the most common use-case currently is implementors needing to compare the changes.
The design document for ExtSDKIntegration is [here](https://docs.google.com/document/d/16Ldq3JxzwybRr2d3l1gUuwdMarmIjZRvHtmYRb6bl1w/edit#).
Some changes required for ExtSDKIntegration that are not spelt out elsewhere in this document are:
* Package names will likely change to reflect the underlying SDK.
*
# Platform Specifics
Java-esque syntax and naming conventions will be used to define the API. It is desirable that all implementations closely resemble each other, but the implementer should aim to strike a balance between adherence to the specification, and achieving a native-feeling API in the target language.
For example, where Java uses `TransactionConfig`, C++ may use `transaction::config`.
Languages are free to implement non-blocking variants also. The appearance of this will be highly platform-dependent: implementers should aim to achieve a non-blocking API as close to the specification as possible, with the constraints of the platform. See the Java version’s reactive API as an example.
Note, there are items in the public Java API that are not in the specification below. The specification should be used as the reference.
Used below will be `Optional`, representing a one-or-none monad, and `Duration`. The implementer should use the most appropriate platform-dependent tool for these.
# Transaction instantiation
Before ExtSDKIntegration: The `Transactions` object is a transactions factory, and is the starting point for the API. There should only be one per application, as it may be doing expensive tasks such as maintaining thread pools, or background threads doing cleanup. If more than one is created, the library should detect this and log a warning at Warn level. However, it will allow the operation to continue to support some advanced cases where the application is fully aware of what it’s doing (e.g. for testing).
### Transactions.create()
This only applies before ExtSDKIntegration.
```java
static Transactions create(Cluster cluster,
[TransactionConfig config])
```
Creates a Transactions object.
|Parameter|Required|Description|
|---------|--------|-----------|
|Cluster|Yes|Required for the distributed background cleanup process|
|Config|No|Configuration options|
### cluster.transactions()
From ExtSDKIntegration, this is how transactions are accessed.
```java
cluster.transactions().run((ctx) -> {
// Transaction logic
});
```
`cluster.transactions()` still returns a `Transactions` object with the same methods as specified in this document. But the `Transactions` object differs in key ways:
* It no longer needs to be a singleton. This is left implementation dependent: the implementation is free to create a `Transactions` object on each `cluster.transactions()` call if it wishes, though a singleton will likely be more performant.
* The key detail is that cleanup must have singleton behaviours. E.g. if a new `Transactions` object is created from the same `cluster`, it must not create a new set of cleanup threads. It is suggested, but left as an implementation detail, that there is a singleton `TransactionsCleanup` object per-`Cluster`.
* The life-cycle of that `Transactions` object is now bound to the `Cluster` it's created from. There is no need for an explicit `Transactions.close()` method anymore. It, and more importantly `TransactionsCleanup`, are destroyed when the `Cluster` is.
The main way we expect users to use the new API is this:
```java
cluster.transactions().run((ctx) -> {
// Transaction logic
});
```
They can also take the `Transactions` object and pass that around:
```java
Transactions transactions = cluster.transactions();
transactions.run((ctx) -> {
// Transaction logic
});
```
### Transactions.config()
```java
// ExtSDKIntegration
// Removed: no need to expose the TransactionsConfig.
// Non-ExtSDKIntegration
TransactionConfigBuilder config()
```
Return the config used to construct this.
### Transactions.run()
```java
// ExtSDKIntegration
TransactionResult run(Consumer<TransactionAttemptContext> transactionLogic,
[TransactionOptions options])
// Non-ExtSDKIntegration
TransactionResult run(Consumer<AttemptContext> transactionLogic,
[PerTransactionConfig perConfig])
```
Starts and runs a single transaction. Blocking (but implementations are free to implement a non-blocking version too, such as Java’s reactive API).
|Parameter|Required|Description|
|---------|--------|-----------|
|transactionLogic|Yes|A lambda, that takes an AttemptContext / TransactionAttemptContext, and uses this to perform transactional operations.|
|perConfig / options|No|Configuration options that override the global config|
### Transactions.commit()
Only needed if ExtDeferredCommit is implemented - which it should not be at this time.
```java
TransactionResult commit(TransactionSerializedContext serialized,
[PerTransactionConfig perConfig])
```
Commits a previously deferred transaction. Deferred transactions will be described in more detail later, but essentially, are a mechanism to pause (defer) a transaction just before the commit point, for future commit.
`@Volatile`: deferred transactions should be marked as a volatile feature. We cannot guarantee the API will not change at this point.
|Parameter|Required|Description|
|---------|--------|-----------|
|serialized|Yes|A serialized blob representing the deferred transaction.|
|perConfig|No|Configuration options that override the Transactions-level config|
### Transactions.rollback()
Only needed if ExtDeferredCommit is implemented - which it should not be at this time.
```java
TransactionResult rollback(TransactionSerializedContext serialized,
[PerTransactionConfig perConfig])
```
Rolls back a previously deferred transaction.
`@Volatile`: deferred transactions should be marked as a volatile feature. We cannot guarantee the API will not change at this point.
|Parameter|Required|Description|
|---------|--------|-----------|
|serialized|Yes|Serialized blob representing the deferred transaction.|
|perConfig|No|Configuration options that override the Transactions-level config|
### Transactions.close()
Do not add this in ExtSDKIntegration.
```java
void close()
```
Closes the Transactions object, along with any associated resources.
Resource management and ownership tend to be highly platform-specific, and the implementor should use the pattern and naming most appropriate to their platform. For example, C++ may want to use an RAII destructor, while Java implements @Autoclosable.
### query
Required for ExtSingleQuery, before the SDK integration.
If ExtSDKIntegration is also implemented, then these are not required. ExtSingleQuery is instead supported through an option on the standard SDK3 `QueryOptions` on `cluster.query()` and `scope.query()`.
```java
SingleQueryTransactionResult query(String statement)
SingleQueryTransactionResult query(String statement, SingleQueryTransactionConfig config)
SingleQueryTransactionResult query(Scope scope, String statement)
SingleQueryTransactionResult query(Scope scope, String statement, SingleQueryTransactionConfig config)
```
Performs a N1QL query inside the transaction.
Languages that do not support overloads can provide four methods with platform-suitable naming for review.
|Parameter|Description|
|---------|-----------|
|scope|Used for scope-level queries.|
|statement|The N1QL query.|
|config|Configuration options.|
# Configuration
In ExtSDKIntegration, configuration happens at the point of configuring the Cluster. In Java this looks like:
```java
ClusterEnvironment env =
ClusterEnvironment.builder()
.transactionsConfig(
TransactionsConfig.durabilityLevel(MAJORITY));
Cluster cluster = Cluster.connect(seedNodes(),
ClusterOptions
.clusterOptions(authenticator())
.environment(env));
```
Before ExtSDKIntegration, a `TransactionConfig` is passed to `Transactions.create()`.
## Builder pattern
Many SDKs use a fluent builder pattern for their options classes, and this should be followed for all such classes in this document if so.
For example:
```java
// ExtSDKIntegration
TransactionsConfig.Builder timeout(Duration timeout)
// Non-ExtSDKIntegration
TransactionConfigBuilder expirationTime(Duration expirationTime)
```
## TransactionConfig
Under ExtSDKIntegration it was renamed to `TransactionsConfig`.
The options are listed here in the approximate order of level of expected usage.
|Option|Default|Description|
|------|-------|-----------|
|expirationTime / timeout | 15 seconds|Sets the maximum time that transactions created by this Transactions object can run for, before expiring.<br><br>Note that expiration is not guaranteed to be followed precisely.<br>For example, if the application were to do a long blocking operation inside the lambda (which should be avoided), then expiration can only trigger after this finishes.<br><br>Similarly, if the transaction attempts a key-value operation close to the expiration time, and that key-value operation times out, then the expiration time may be exceeded.<br><br>Renamed to `timeout` in ExtSDKIntegration.|
|durabilityLevel|MAJORITY|The writes of all transactions created by this object will be performed with this durability setting.<br><br>TransactionDurabilityLevel is an enum with these values: NONE, MAJORITY, MAJORITY_AND_PERSIST_TO_ACTIVE, and PERSIST_TO_MAJORITY.<br><br>In ExtSDKIntegration, use the standard SDK3 `DurabilityLevel` enum instead.<br><br>NONE is there, but is not a supported setting. There are no atomicity guarantees if this is used: a node failover can cause vbucket rollback and an atomicity failure.|
|keyValueTimeout|unspecified (KV writes will default to the SDK’s defaults)|The default timeout used for all KV writes.<br><br>Do not add in ExtSDKIntegration. In Rage we will soon be completely reworking how KV timeouts work, which will remove the need for this parameter (which already has limited use due to transaction retries).|
|unstagingMode|platform-specific|This is only required if the implementation supports both ExtMemoryOptUnstaging and ExtTimeOptUnstaging and wants to allow the user to select the mode.<br><br>It should be marked the platform-specific version of @Stability.Volatile.|
|metadataCollection|Collection / TransactionKeyspace|Only required if implementation supports ExtCustomMetadataCollection.<br><br>It specifies the collection to use for any metadata (ATR and client-record) access.<br><br>ExtSDKIntegration: it is a `TransactionKeyspace` rather than a `Collection`. Specifying this also adds it to the cleanup set (and when anything is added to the cleanup set, cleanup is started.)|
|queryConfig|TransactionQueryConfig / TransactionsQueryConfig|Only required if implementation supports ExtQuery.|
## TransactionQueryConfig
This was renamed under ExtSDKIntegration to `TransactionsQueryConfig`.
It should be implemented as a nested object inside `TransactionsConfig` / `TransactionConfig`:
```java
// ExtSDKIntegration
TransactionsConfig queryConfig(TransactionsQueryConfig queryConfig)
// Non-ExtSDKIntegration
TransactionConfig queryConfig(TransactionQueryConfig queryConfig)
```
## Cleanup options
In ExtSDKIntegration these are moved to a separate object `TransactionsCleanupConfig`.
```java
ClusterEnvironment.Builder
.transactionsConfig(
TransactionsConfig.Builder
.cleanupConfig(TransactionsCleanupConfig.Builder
// Existing options
.cleanupWindow(Duration.ofSeconds(30))
.cleanupClientAttempts(false)
.cleanupLostAttempts(false)
// New options
.addCollection(TransactionKeyspace.create("aBucket",
DEFAULT_SCOPE,
"aCollection"))));
```
Before ExtSDKIntegration, these options exist in `TransactionConfig`.
|Option|Default|Description|
|------|-------|-----------|
|cleanupClientAttempts|true|Controls where any transaction attempts made by this client are automatically removed.<br><br>The usual implementation would be to start a background thread, that processes a queue of transactions created by this client that should be cleaned up after a certain time. E.g., removing the record for the attempt from the ATR.|
|cleanupLostAttempts|true|Controls whether a background process is created to cleanup any 'lost' transaction attempts.|
|cleanupWindow|60 seconds|Each client that has cleanupLostAttempts(true) enabled, will be participating in the distributed cleanup process. This involves checking all ATRs every cleanup window, and this parameter controls the length of that window.<br><br>This is a conservative default that will not put significant load on the cluster, while finding any lost transactions in a reasonable time. Decreasing the window will ensure that lost transactions are found more swiftly, at the cost of more frequent ATR polling.|
|addCollection|TransactionKeyspace|Added in ExtSDKIntegration.<br><br>Adds a collection to the cleanup set, which will also start cleanup immediately.|
# TransactionKeyspace
Added for ExtSDKIntegration, as we need to be able to specify collections without having the Collection created yet.
It should look very similar to the SDK's implementation of `EventingFunctionKeyspace`.
These constructor methods are possible:
```java
TransactionKeyspace.create("aBucket")
TransactionKeyspace.create("aBucket", "aScope")
TransactionKeyspace.create("aBucket", DEFAULT_SCOPE, "aCollection")
```
# TransactionQueryConfig
Required for ExtQuery.
Under ExtSDKIntegration it was renamed `TransactionsQueryConfig`.
|Option|Type|Description|
|------|-------|-----------|
|scanConsistency|QueryScanConsistency|Sets the default scan consistency to use for all query statements in the transaction. Default is unset (and query will default to REQUEST_PLUS).|
# PerTransactionConfig
This was renamed under ExtSDKIntegration to `TransactionOptions`.
Allows the global `TransactionConfig` settings to be overridden for this transaction. See [TransactionConfig](#TransactionConfig) for the definition of these settings.
These settings are currently supported:
* durabilityLevel
* unstagingMode (only if multiple unstaging modes are supported by the implementation)
ExtQuery adds the first three of these fields, ExtSDKIntegration the fourth:
|Field|Type|ExtSDKIntegration|
|------|-------|----|
|expirationTime|Duration|Rename to "timeout"|
|keyValueTimeout|Duration|Remove|
|queryConfig|PerTransactionQueryConfig|Remove|
|metadataCollection|Collection (not TransactionKeyspace)|Added. Was not present before ExtSDKIntegration.|
# PerTransactionQueryConfig
Required for ExtQuery.
Allows the global TransactionQueryConfigBuilder settings to be overridden for this transaction. Currently only scanConsistency is present.
Removed in ExtSDKIntegration.
# AttemptContext
Renamed in ExtSDKIntegration to `TransactionAttemptContext`.
Provides all operations for an individual transaction, to allow it to read and mutate documents.
Note, there are no throws specifications for any of these, as the application should not be doing any try-catch handling of any of these operations. Any operation could theorectically throw any exception (though in reality it can only throw a `ErrorWrapper`), the exceptions may change in the future, and the application is explicitly required to not make any assumptions on the current or future exception behaviour. This is required to allow the transactions library to transparently retry the transaction lambda when needed.
Some exceptions to those above rules exist as of ExtQuery and ExtSDKIntegration.
## JSON handling
A number of operations need to specify JSON. Transactions only support JSON.
JSON is described as `Object` in this doc as per the Java implementation, but the exact mechanism for supplying content will depend on the platform (e.g. an Object, a generic `T`, an `interface {}`, a dictionary...). The method for supplying JSON should resemble the underlying SDK.
## getOptional
```java
Optional<TransactionGetResult> getOptional(Collection collection, String id)
```
Gets a document. If the document does not exist, it will return `Optional.empty` rather than failing the transaction.
|Parameter|Required|Description|
|---------|--------|-----------|
|collection|Yes|The document's collection|
|id|Yes|The document’s key|
Removed in ExtSDKIntegration - replaced with ctx.get() raising a catchable `DocumentNotFoundException`.
## get
```java
TransactionGetResult get(Collection collection, String id, [TransactionGetOptions options])
```
Gets a document. The application can use this or [getOptional](#getOptional) depending on whether it is expecting the document to exist or not.
If the document does not exist, the transaction will auto-rollback then raise a `TransactionFailed` error to the application, with root cause `DocumentNotFoundException`.
|Parameter|Required|Description|
|---------|--------|-----------|
|collection|Yes|The document's collection|
|id|Yes|The document’s key|
|options|No|Added in ExtBinarySupport. See below for details|
Under ExtSDKIntegration, the implementation may raise a `DocumentNotFoundException`, which the user may catch.
`TransactionGetOptions` was added in ExtBinarySupport and contains one option: a `Transcoder transcoder` that is not-set/null by default, and works similarly to the same option in non-transactional `GetOptions`. Its exact handling is detailed below under [TransactionGetResult](#TransactionGetResult).
## replace
```java
TransactionGetResult replace(TransactionGetResult doc, Object content, [TransactionReplaceOptions options])
```
Mutates a document previously fetched inside the same transaction. This mutation will be staged in the document’s xattrs.
If the document does not exist, the transaction will auto-rollback then raise a `TransactionFailed` error to the application, with root cause `DocumentNotFoundException`.
Returns the inserted document in the form of a TransactionGetResult.
|Parameter|Required|Description|
|---------|--------|-----------|
|doc|Yes|The result of a previous call to get or getOptional, inside the same transaction attempt|
|content|Yes|The new content to stage|
|options|No|Added in ExtBinarySupport. See below for details|
### Transcoder
`TransactionReplaceOptions` was added with ExtBinarySupport and contains one option: a `Transcoder transcoder` that is not-set/null by default, and works similarly to the same option in non-transactional `ReplaceOptions`.
That is, if present, the `content` will be passed to the `Transcoder`, and the resulting encoded byte array and int32 flags, passed down to the transactions internals.
## insert
```java
TransactionGetResult insert(Collection collection, String id, Object content, [TransactionInsertOptions] options)
```
If the document already exists, the transaction will auto-rollback then raise a `TransactionFailed` error to the application, with root cause `DocumentExistsException`.
Returns the inserted document in the form of a TransactionGetResult.
|Parameter|Required|Description|
|---------|--------|-----------|
|collection|Yes|The collection to insert the document in|
|id|Yes|The document’s key|
|content|Yes|The new content to stage|
|options|No|Added in ExtBinarySupport. See below for details|
### Transcoder
ExtBinaryInsert: See the same section under [replace](#replace) above - transcoding for insert works the same way.
## remove
```java
void remove(TransactionGetResult doc)
```
Removes a document previously fetched inside the same transaction.
If the document does not exist, the transaction will auto-rollback then raise a TransactionFailed error to the application, with root cause DocumentNotFoundException.
|Parameter|Required|Description|
|---------|--------|-----------|
|doc|Yes|The result of a previous call to get or getOptional, inside the same transaction attempt|
## commit
```java
void commit()
```
Commits the transaction.
This is completely optional. If control flow reaches the end of the lambda with no thrown exceptions, commit will be performed automatically.
Removed in ExtSDKIntegration, as it is redundant.
## rollback
```java
void rollback()
```
Rolls back the transaction. No error will be returned to the application if the app-rollback succeeds.
It is rare to require this. In general, if the application discovers it needs to app-rollback the transaction, it is probably an error state, and better modelled by throwing a custom exception. This will result in the attempt being rolled back, and a `TransactionFailed` raised to the application with the custom exception as the root cause. This permits the application to confirm that the transaction in fact rolled back, and why, which this API call does not provide.
Removed in ExtSDKIntegration, as it is redundant.
## query
Required for ExtQuery.
```java
TransactionQueryResult query(String statement)
TransactionQueryResult query(String statement, TransactionQueryOptions options)
TransactionQueryResult query(Scope scope, String statement)
TransactionQueryResult query(Scope scope, String statement, TransactionQueryOptions options)
```
Performs a N1QL query inside the transaction.
Languages that do not support overloads can provide four methods with platform-suitable naming for review.
|Parameter|Description|
|---------|-----------|
|scope|Used for scope-level queries.|
|statement|The N1QL query.|
|options|Configuration options.|
Note that under the original ExtQuery implementation a QueryResult was returned. This was changed to a TransactionQueryResult under [CBD-4602](https://issues.couchbase.com/browse/CBD-4602), to allow implementations to have a streaming QueryResult and a non-streaming TransactionQueryResult.
TransactionQueryResult is identical to QueryResult except it presents a non-streaming interface. This is left implementation-dependent.
## defer
```java
void defer()
```
Defers committing the transaction. The returned TransactionResult contains a serialized() method that contains a serialized blob representing this transaction, which may be later committed.
@Volatile: as with all methods related to deferred commits, this must be marked volatile.
Only required for ExtDeferredCommit, which should not be implemented.
## logger
```java
TransactionLogger logger()
```
Returns the logger used by this. Optional, but helpful for debugging as it allows the application to write its own log into the transaction logs.
# TransactionGetResult
Represents a document fetched from Couchbase, along with additional transactional data (such as whether the transaction is currently involved in another transaction).
It’s intended to be similar to SDK3’s `GetResult`.
ExtBinarySupport: to be more explicit on that, internally the `TransactionGetResult` should hold an array of bytes for the content, and an int32 for the document's flags.
## id
```java
String id()
```
Returns the id of the document.
## contentAs
```java
T contentAs<T>()
```
Works exactly like SDK3: convert the document’s content into something else.
Note the content can have come from either the document’s body, or the staged mutation in the xattr.
Uses the `JsonSerializer` configured on the SDK, as per ExtSerialization.
#### ExtBinarySupport
If the user specified a `Transcoder` via `TransactionGetOptions`, then pass that transcoder the content byte array and the document's flags, and return the deserialized result to the user. This should work the same way as the rest of the SDK, so see the [SDK3 Transcoders and Serializers RFC](https://github.com/couchbaselabs/sdk-rfcs/blob/master/rfc/0055-serializers-transcoders.md) for more detail.
The one exception is that, that unlike other SDK operations, we do not use the default `Transcoder` specified at the `Cluster` level. This is to preserve compatibility with existing applications.
If a `Transcoder` has not been specified via `TransactionGetOptions`, then continue with the existing `contentAs` logic (e.g. use the `JsonSerializer` configured at the `Cluster` level).
## Internals
There are various transactional and document metadata required, including the document's CAS and any contents of the "txn" xattr. These should be stored in the `TransactionGetResult` but not accessible publically. The exact mechanism for this is platform dependent. The platform equivalent of @Stability.Internal can be used.
# TransactionResult
Represents the result of a transaction, which can contain multiple attempts. This is returned directly from `Transactions.run()` or `cluster.transactions().run()`, and is also contained in a `TransactionFailed` exception (the latter is no longer the case in ExtSDKIntegration).
## log
```java
TransactionLogger log()
```
Provides an in-memory log containing all log related to this transaction.
It is optional but strongly recommended. It allows:
* Easier debugging of intermittent test failures on CI, as FIT will automatically dump the log of the last transaction to stdout.
* We can have verbose-but-cheap logging. Transactions are complex and verbose logging has proved essential while debugging the Java implementation over 3+ years.
* The application can easily log this full verbose logging only on failed transactions.
## transactionId
```java
String transactionId()
```
The id of the transaction.
## ~~attempts~~
Deprecated/removed in CBD-3757. Do not add it to new implementations, and remove if present while implementing ExtSDKIntegration.
## ~~mutationState~~
Now removed under CBD-3802. This should not be present in any API that is not GA already (e.g. just Java). Remove if present while implementing ExtSDKIntegration.
## ~~mutationTokens~~
Now removed under CBD-3802. This should not be present in any API that is not GA already (e.g. just Java). Remove if present while implementing ExtSDKIntegration.
## unstagingComplete
```java
boolean unstagingComplete()
```
Returns true iff the transaction reached the COMPLETED state.
## serialized
```java
Optional<TransactionSerializedContext> serialized()
```
Only required for ExtDeferredCommit, which should not be implemented.
Returns a serialized representation of a deferred transaction, suitable for later commit or app-rollback.
This will only be present if the application called AttemptContext.defer() on the transaction.
@Volatile: as with everything to do with deferred commits, this must be marked volatile.
# ~~TransactionAttempt~~
Deprecated/removed in CBD-3757. Do not add it to new implementations, and remove if present while implementing ExtSDKIntegration
# Exceptions
For backwards-compatibility reasons, all exceptions have to derive from `TransactionFailed` / `TransactionFailedException`.
Only the exceptions in this section may be raised to the application.
ExtSDKIntegration: all exceptions are renamed to be suffixed with `Exception`, and derive from `CouchbaseException`, as per SDK3.
## TransactionFailed
The transaction failed, pre-commit. It is unambiguously not committed and none of its changes are visible.
It contains the underlying reason (exception) as a root cause, represented in the most platform-appropriate way (e.g. for Java it is `err.getCause()``).
It also has a method:
```java
TransactionResult result()
```
This returns the result object of the transaction, allowing (most usefully) access to the logs.
In ExtSDKIntegration this is replaced (except in Go, which GA-ed before this decision) with these methods:
```
List[TransactionLogMessage] logs()
String transactionId()
```
## TransactionExpired
The transaction expired pre-commit. It is unambiguously not commited, none of its changes are visible.
## TransactionCommitAmbiguous
It is ambiguous whether the transaction committed.
See the main design document for a detailed discussion of why this error is required.
# TransactionSerializedContext
Only required for ExtDeferredCommit, which should not be implemented.
## encodeAsString
```java
String encodeAsString()
```
Returns the serialized blob in String format.
## encodeAsBytes
```java
byte[] encodeAsBytes()
```
Returns the serialized blob in byte[] format.
# TransactionQueryOptions
This provides a subset of the standard SDK3 `QueryOptions`:
* parameters
* scanConsistency
* flexIndex
* serializer
* clientContextId
* scanWait
* scanCap
* pipelineBatch
* pipelineCap
* readonly
* adhoc
* raw
* profile
Anything not on that list is explicitly not included. This includes parentSpan (the transaction can configure this automatically itself), timeout (will be ignored by query), and retryStrategy (this is controlled by the transactions library).
# Single query transactions
This is the part of the API that changes most radically under ExtSDKIntegration, as it's integrated into the regular query API:
```java
// ExtSDKIntegration
QueryResult qr = cluster.query("INSERT...", queryOptions()
.asTransaction())
QueryResult qr = scope.query("INSERT...", queryOptions()
.asTransaction(SingleQueryTransactionOptions.
.durabilityLevel(DurabilityLevel.MAJORITY))
.timeout(Duration.ofSeconds(360)))
// Non-ExtSDKIntegration
SingleQueryTransactionResult qr = transactions.query("INSERT...")
SingleQueryTransactionResult qr = transactions.query(scope, "INSERT...",
SingleQueryTransactionConfigBuilder.create()
.expirationTime(Duration.ofSeconds(360))
.durabilityLevel(TransactionDurabilityLevel.MAJORITY)
.build())
```
# SingleQueryTransactionConfig
Required for ExtSingleQuery.
It is still required in ExtSDKIntegration, though its name has changed and most fields are removed.
Allows a single query transaction to be configured, with these options:
|Option|Type|Description|
|------|-----------|---|
|queryOptions|TransactionQueryOptions|Configures the single query.<br><br>Removed under ExtSDKIntegration, as we use the options from `QueryOptions`.|
|durabilityLevel|TransactionDurabilityLevel / DurabilityLevel|Overrides the default durability level.<br><br>Under ExtSDKIntegration, TransactionDurabilityLevel is replaced with the regular SDK DurabilityLevel.|
|expirationTime|Duration|Overrides the default expiration time.<br><br>Removed in ExtSDKIntegration, as we can use the `timeout` from `QueryOptions`.|
# SingleQueryTransactionResult
Required for ExtSingleQuery, before the SDK integration.
If ExtSDKIntegration is also implemented, then this is not required. The result, from the API perspective, is a standard SDK3 `QueryResult`.
|Option|Description|
|------|-----------|
|log: TransactionLogger|Buffered in-memory log from the transaction.|
|queryResult: QueryResult|Result of the query.|
|unstagingComplete: Boolean|Same as with `TransactionResult`.|