Skip to content

Commit 88bf7ee

Browse files
DOC-9740 - Use refactored shared partials (#248)
https://issues.couchbase.com/browse/DOC-9740 [Page Preview](https://maria-robobug.github.io/DOC-9740-dotnet/dotnet-sdk/DOC-9740/howtos/distributed-acid-transactions-from-the-sdk.html) * Use the changes made in [docs-sdk-common](couchbase/docs-sdk-common#45). * Included the `:page-toclevels: 2` attribute to improve UI (there are alot of sections in this doc, seems helpful for the user). * Added `acid-transactions-attributes.adoc` for SDK specific attributes (used by docs-sdk-common partials).
1 parent 4647db0 commit 88bf7ee

3 files changed

Lines changed: 91 additions & 113 deletions

File tree

modules/howtos/examples/TransactionsExample.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
// tag::imports[]
2+
using System;
23
using System.Linq;
34
using System.Threading.Tasks;
45
using Couchbase.KeyValue;
@@ -8,6 +9,7 @@
89
using Microsoft.Extensions.DependencyInjection;
910
using Microsoft.Extensions.Logging;
1011
using Newtonsoft.Json.Linq;
12+
// end::imports[]
1113

1214
namespace Couchbase.Transactions.Examples
1315
{

modules/howtos/pages/distributed-acid-transactions-from-the-sdk.adoc

Lines changed: 65 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
= Distributed Transactions from the .NET SDK
2-
:description: A practical guide to using Couchbases distributed ACID transactions, via the .NET API.
2+
:description: A practical guide to using Couchbase's distributed ACID transactions, via the .NET API.
33
:navtitle: ACID Transactions
44
:page-topic-type: howto
55
:page-aliases: acid-transactions.adoc
6+
:page-toclevels: 2
67

78
include::project-docs:partial$attributes.adoc[]
9+
include::partial$acid-transactions-attributes.adoc[]
810

911
[abstract]
1012
{description}
@@ -65,23 +67,8 @@ Here are all imports used by the following examples:
6567

6668
[source,csharp]
6769
----
68-
using System;
69-
using System.Linq;
70-
using System.Threading.Tasks;
71-
using Couchbase.KeyValue;
72-
using Couchbase.Query;
73-
using Couchbase.Transactions.Config;
74-
using Couchbase.Transactions.Deferred;
75-
using Couchbase.Transactions.Error;
76-
using Microsoft.Extensions.DependencyInjection;
77-
using Microsoft.Extensions.Logging;
78-
using Newtonsoft.Json.Linq;
79-
----
80-
////
81-
----
8270
include::example$TransactionsExample.cs[tag=imports,indent=0]
8371
----
84-
////
8572

8673
The starting point is the `Transactions` object.
8774
It is very important that the application ensures that only one of these is created per cluster, as it performs automated background clean-up processes that should not be duplicated.
@@ -93,6 +80,7 @@ include::example$TransactionsExample.cs[tag=init,indent=0]
9380
----
9481

9582
=== Multiple Transactions Objects
83+
9684
Generally an application will need just one `Transactions` object, and in fact the library will usually warn if more are created.
9785
Each `Transactions` object uses some resources, including a thread-pool.
9886

@@ -123,29 +111,21 @@ The asynchronous API allows you to use the thread pool, which can help you scale
123111
However, operations inside an individual transaction should be kept in-order and executed using `await` immediately.
124112
Do not use fire-and-forget tasks under any circumstances.
125113

114+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=lambda-ctx]
126115

127-
== Examples
128-
129-
A code example is worth a thousand words, so here is a quick summary of the main transaction operations.
130-
They are described in more detail below.
116+
// Examples
117+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=examples-intro]
131118

132119
[source,csharp]
133120
----
134121
include::example$TransactionsExample.cs[tag=examples,indent=0]
135122
----
136123

137-
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=mechanics]
124+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tags=mechanics;!integrated-sdk-cleanup-process]
138125

139126

140127
== Key-Value Mutations
141128

142-
=== Inserting
143-
144-
[source,csharp]
145-
----
146-
include::example$TransactionsExample.cs[tag=insert,indent=0]
147-
----
148-
149129
=== Replacing
150130

151131
Replacing a document requires awaiting a `TransactionGetResult` returned from `ctx.GetAsync()`, `ctx.InsertAsync()`, or another `ctx.ReplaceAsync()` call first.
@@ -166,9 +146,14 @@ As with replaces, removing a document requires awaiting a `TransactionGetResult`
166146
include::example$TransactionsExample.cs[tag=remove,indent=0]
167147
----
168148

169-
== Key-Value Reads
149+
=== Inserting
150+
151+
[source,csharp]
152+
----
153+
include::example$TransactionsExample.cs[tag=insert,indent=0]
154+
----
170155

171-
=== Getting
156+
== Key-Value Reads
172157

173158
There are two ways to get a document, `GetAsync` and `GetOptionalAsync`:
174159

@@ -188,15 +173,16 @@ include::example$TransactionsExample.cs[tag=getReadOwnWrites,indent=0]
188173
----
189174

190175

191-
include::7.0@sdk:shared:partial$acid-transactions.adoc[tag=query-intro]
176+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tags=query-intro;!integrated-sdk-begin-transaction]
192177

193178
=== Using N1QL
179+
194180
If you already use N1QL from the .NET SDK, then its use in transactions is very similar.
195-
it returns the same `IQueryResult<T>` you are used to, and takes most of the same options.
181+
It returns the same `IQueryResult<T>` you are used to, and takes most of the same options.
196182

197183
You must take care to write `ctx.QueryAsync()` inside the lambda however, rather than `cluster.QueryAsync()` or `scope.QueryAsync()`.
198184

199-
An example of SELECTing some rows from the `travel-sample` bucket:
185+
An example of selecting some rows from the `travel-sample` bucket:
200186

201187
[source,csharp]
202188
----
@@ -227,19 +213,22 @@ include::example$TransactionsExample.cs[tag=queryExamplesComplex,indent=0]
227213
----
228214

229215
=== Read Your Own Writes
216+
230217
As with Key-Value operations, N1QL queries support Read Your Own Writes.
231218

232-
This example shows INSERTing a document and then SELECTing it again.
219+
This example shows inserting a document and then selecting it again.
233220

234221
[source,csharp]
235222
----
236223
include::example$TransactionsExample.cs[tag=queryInsert,indent=0]
237224
----
225+
238226
<1> The inserted document is only staged at this point. as the transaction has not yet committed.
239227
Other transactions, and other non-transactional actors, will not be able to see this staged insert yet.
240228
<2> But the SELECT can, as we are reading a mutation staged inside the same transaction.
241229

242230
=== Mixing Key-Value and N1QL
231+
243232
Key-Value operations and queries can be freely intermixed, and will interact with each other as you would expect.
244233

245234
In this example we insert a document with Key-Value, and read it with a SELECT.
@@ -251,20 +240,36 @@ include::example$TransactionsExample.cs[tag=queryRyow,indent=0]
251240
<2> But the SELECT can view it, as the insert was in the same transaction.
252241

253242
=== Query Options
254-
Query options can be provided via `TransactionsQueryOptions`, which provides a subset of the options in the .NET SDK's `QueryOptions`.
243+
244+
Query options can be provided via `TransactionQueryOptions`, which provides a subset of the options in the .NET SDK's `QueryOptions`.
255245

256246
[source,csharp]
257247
----
258248
include::example$TransactionsExample.cs[tag=queryOptions,indent=0]
259249
----
260250

261-
include::7.0@sdk:shared:partial$acid-transactions.adoc[tag=query-options]
251+
The supported options are:
252+
253+
* Parameter
254+
* ScanConsistency
255+
* FlexIndex
256+
* Serializer
257+
* ClientContextId
258+
* ScanWait
259+
* ScanCap
260+
* PipelineBatch
261+
* PipelineCap
262+
* Readonly
263+
* AdHoc
264+
* Raw
262265

263266
See the xref:howtos:n1ql-queries-with-sdk.adoc#query-options[QueryOptions documentation] for details on these.
264267

265-
include::7.0@sdk:shared:partial$acid-transactions.adoc[tag=query-perf]
268+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=query-concurrency]
266269

267-
include::7.0@sdk:shared:partial$acid-transactions.adoc[tag=query-single]
270+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=query-perf]
271+
272+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=query-single]
268273

269274
[source,csharp]
270275
----
@@ -342,30 +347,11 @@ After a transaction is rolled back, it cannot be committed, no further operation
342347

343348

344349
// Error Handling
345-
// tag::error[]
346-
== Error Handling
347-
348-
As discussed previously, the transactions library will attempt to resolve many errors itself, through a combination of retrying individual operations and the application's lambda.
349-
This includes some transient server errors, and conflicts with other transactions.
350-
351-
But there are situations that cannot be resolved, and total failure is indicated to the application via exceptions.
352-
These errors include:
353-
354-
* Any exception thrown by your transaction lambda, either deliberately or through an application logic bug.
355-
* Attempting to insert a document that already exists.
356-
* Attempting to remove or replace a document that does not exist.
357-
* Calling `ctx.GetAsync()` on a document key that does not exist.
358-
359-
IMPORTANT: Once one of these errors occurs, the current attempt is irrevocably failed (though the transaction may retry the lambda).
360-
It is not possible for the application to catch the failure and continue.
361-
Once a failure has occurred, all other operations tried in this attempt (including commit) will instantly fail.
350+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=error]
362351

363-
Transactions, as they are multi-stage and multi-document, also have a concept of partial success/failure.
364-
This is signalled to the application through the `TransactionResult.UnstagingComplete` property, described later.
365-
366-
There are three exceptions that the transactions library can raise to the application: `TransactionFailed`, `TransactionExpired`, and `TransactionCommitAmbiguous`.
367-
All exceptions derive from `TransactionFailed` for backwards-compatibility purposes.
368-
// end::error[]
352+
There are three exceptions that Couchbase transactions can raise to the application:
353+
`TransactionFailedException`, `TransactionExpiredException` and `TransactionCommitAmbiguousException`.
354+
All exceptions derive from `TransactionFailedException` for backwards-compatibility purposes.
369355

370356
// txnfailed
371357
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=txnfailed]
@@ -375,57 +361,11 @@ include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=txnfaile
375361
include::example$TransactionsExample.cs[tag=config-expiration,indent=0]
376362
----
377363

378-
// tag::txnfailed1[]
379-
This will allow the protocol more time to get past any transient failures (for example, those caused by a cluster rebalance).
380-
The tradeoff to consider with longer expiration times, is that documents that have been staged by a transaction are effectively locked from modification from other transactions, until the expiration time has exceeded.
381-
382-
Note that expiration is not guaranteed to be followed precisely.
383-
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.
384-
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.
385-
386-
=== TransactionCommitAmbiguous
387-
388-
As discussed <<mechanics,previously>>, each transaction has a 'single point of truth' that is updated atomically to reflect whether it is committed.
389-
390-
However, it is not always possible for the protocol to become 100% certain that the operation was successful, before the transaction expires.
391-
That is, the operation may have successfully completed on the cluster, or may succeed soon, but the protocol is unable to determine this (whether due to transient network failure or other reason).
392-
This is important as the transaction may or may not have reached the commit point, e.g. succeeded or failed.
393-
394-
The library raises `TransactionCommitAmbiguous` to indicate this state.
395-
It should be rare to receive this exception.
364+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=txnfailed1]
396365

397-
If the transaction had in fact successfully reached the commit point, then the transaction will be fully completed ("unstaged") by the asynchronous cleanup process at some point in the future.
398-
With default settings this will usually be within a minute, but whatever underlying fault has caused the TransactionCommitAmbiguous may lead to it taking longer.
366+
Similar to `TransactionResult`, `SingleQueryTransactionResult` also has an `UnstagingComplete` property.
399367

400-
If the transaction had not in fact reached the commit point, then the asynchronous cleanup process will instead attempt to roll it back at some point in the future.
401-
If unable to, any staged metadata from the transaction will not be visible, and will not cause problems (e.g. there are safety mechanisms to ensure it will not block writes to these documents for long).
402-
403-
==== Handling
404-
This error can be challenging for an application to handle.
405-
As with `TransactionFailed` it is recommended that it at least writes any logs from the transaction, for future debugging.
406-
It may wish to retry the transaction at a later point, or globally extend transactional expiration times to give the protocol additional time to resolve the ambiguity.
407-
408-
=== TransactionResult.UnstagingComplete
409-
410-
As above, there is a 'single point of truth' for a transaction.
411-
After this atomic commit point is reached, the documents themselves will still be individually committed (we also call this "unstaging").
412-
However, transactionally-aware actors will now be returning the post-transaction versions of the documents, and the transaction is effectively fully committed to those actors.
413-
414-
So if the application is solely working with transaction-aware actors, then the unstaging process is optional.
415-
And failures during the unstaging process are not particularly important, in this case.
416-
(Note the asynchronous cleanup process will still complete the unstaging process at a later point.)
417-
418-
Hence, many errors during unstaging will cause the transaction to immediately return success.
419-
That is, successful return simply means that the commit point was reached.
420-
421-
The property `TransactionResult.UnstagingComplete` indicates whether the unstaging process completed successfully or not.
422-
This should be used any time that the application needs all results of the transaction to be immediately available to non-transactional actors (which currently includes N1QL and non-transactional Key-Value reads).
423-
424-
Error handling differs depending on whether a transaction is before or after the point of commit (or rollback).
425-
// end::txnfailed1[]
426-
427-
428-
== Full Error Handling Example
368+
=== Full Error Handling Example
429369

430370
Pulling all of the above together, this is the suggested best practice for error handling:
431371

@@ -436,7 +376,20 @@ include::example$TransactionsExample.cs[tag=full-error-handling,indent=0]
436376

437377

438378
// == Asynchronous Cleanup
439-
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=cleanup]
379+
include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tags=cleanup;!integrated-sdk-cleanup-collections]
380+
381+
[#tuning-cleanup]
382+
=== Configuring Cleanup
383+
384+
The cleanup settings can be configured as so:
385+
386+
[options="header"]
387+
|===
388+
|Setting|Default|Description
389+
|`CleanupWindow`|60 seconds|This determines how long a cleanup 'run' is; that is, how frequently this client will check its subset of ATR documents. It is perfectly valid for the application to change this setting, which is at a conservative default. Decreasing this will cause expiration transactions to be found more swiftly (generally, within this cleanup window), with the tradeoff of increasing the number of reads per second used for the scanning process.
390+
|`CleanupLostAttempts`|true|This is the thread that takes part in the distributed cleanup process described above, that cleans up expired transactions created by any client. It is strongly recommended that it is left enabled.
391+
|`CleanupClientAttempts`|true|This thread is for cleaning up transactions created just by this client. The client will preferentially aim to send any transactions it creates to this thread, leaving transactions for the distributed cleanup process only when it is forced to (for example, on an application crash). It is strongly recommended that it is left enabled.
392+
|===
440393

441394
////
442395
=== Monitoring Cleanup
@@ -499,5 +452,5 @@ include::{version-server}@sdk:shared:partial$acid-transactions.adoc[tag=custom-m
499452

500453
== Further Reading
501454

502-
* Theres plenty of explanation about how Transactions work in Couchbase in our xref:{version-server}@server:learn:data/transactions.adoc[Transactions documentation].
455+
* There's plenty of explanation about how Transactions work in Couchbase in our xref:{version-server}@server:learn:data/transactions.adoc[Transactions documentation].
503456
* You can find further code examples on our https://github.com/couchbaselabs/couchbase-transactions-dotnet-examples[transactions examples repository].
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// These attributes are used in sdk::shared:partial$acid-transactions.adoc
2+
3+
// intro
4+
:durability-exception: pass:q[`DurabilityImpossibleException`]
5+
6+
7+
// creating
8+
:lambda-attempt-ctx: pass:q[an `AttemptContext`]
9+
:collection-insert: pass:q[`collection.InsertAsync()`]
10+
:ctx-insert: pass:q[`ctx.InsertAsync()`]
11+
12+
13+
// error
14+
:ctx-get: pass:q[`ctx.GetAsync()`]
15+
:error-unstaging-complete: pass:q[`TransactionResult.UnstagingComplete` property]
16+
17+
18+
// txnfailed
19+
:transaction-failed: TransactionFailedException
20+
:transaction-expired: TransactionExpiredException
21+
:transaction-config: TransactionConfigBuilder
22+
:transaction-commit-ambiguous: TransactionCommitAmbiguousException
23+
:txnfailed-unstaging-complete: TransactionResult.UnstagingComplete

0 commit comments

Comments
 (0)