Getting Started with Distributed Transactions in the ScalarDB Cluster .NET Client SDK
The ScalarDB Cluster .NET Client SDK supports the distributed transaction functionality of ScalarDB Cluster. The SDK includes transaction and manager abstractions for easier communication within a cluster.
Although we recommend using asynchronous methods as in the following examples, you can use synchronous versions instead.
For details about distributed SQL transactions, see Getting Started with Distributed SQL Transactions in the ScalarDB Cluster .NET Client SDK.
Install the SDK
Install the same major and minor version of the SDK as ScalarDB Cluster into the .NET project. You can do this by using the built-in NuGet package manager, replacing <MAJOR>.<MINOR>
with the version that you're using:
dotnet add package ScalarDB.Client --version '<MAJOR>.<MINOR>.*'
Create a settings file
Create a scalardb-options.json
file and add the following, replacing <HOSTNAME_OR_IP_ADDRESS>
with the FQDN or the IP address, and <PORT>
with the port number (60053
by default) of your cluster:
{
"ScalarDbOptions": {
"Address": "http://<HOSTNAME_OR_IP_ADDRESS>:<PORT>",
"HopLimit": 10
}
}
For details about settings files and other ways to configure the client, see Client configuration.
Get a transaction manager
You need to get a transaction manager for distributed transactions. To get the transaction manager, you can use TransactionFactory
as follows:
// Pass the path to the settings file created in the previous step.
var factory = TransactionFactory.Create("scalardb-options.json");
using var manager = factory.GetTransactionManager();
Manage transactions
To execute CRUD operations, a transaction is needed. You can begin a transaction by using the transaction manager as follows:
var transaction = await manager.BeginAsync();
You can also resume a transaction that is already being executed as follows:
var transaction = manager.Resume(transactionIdString);
The Resume
method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible.
When a transaction is ready to be committed, you can call the CommitAsync
method of the transaction as follows:
await transaction.CommitAsync();
To roll back the transaction, you can use the RollbackAsync
method:
await transaction.RollbackAsync();
Execute CRUD operations
A transaction has GetAsync
, ScanAsync
, PutAsync
, DeleteAsync
, and MutateAsync
methods to execute CRUD commands against the cluster. As a parameter, these methods have a command object. A command object can be created by using the builders listed in this section.
To use these builders add the following namespace to the using
section:
using ScalarDB.Client.Builders;
The cluster does not support parallel execution of commands inside one transaction, so make sure to use await
for asynchronous methods.
GetAsync
method example
using GetTypeEnum = Scalar.Db.Cluster.Rpc.V1.Get.Types.GetType;
// ...
var get =
new GetBuilder()
.SetNamespaceName("ns")
.SetTableName("statements")
.SetGetType(GetTypeEnum.Get)
.AddPartitionKey("order_id", "1")
.AddClusteringKey("item_id", 2)
.SetProjections("item_id", "count")
.Build();
var getResult = await transaction.GetAsync(get);
Handle Result
objects
The GetAsync
and ScanAsync
methods return Result
objects. The SDK has GetValue
, TryGetValue
, and IsNull
extension methods to simplify using them:
using ScalarDB.Client.Extensions;
// ...
// Getting an integer value from the "item_id" column.
// If it fails, an exception will be thrown.
var itemId = result.GetValue<int>("item_id");
// Trying to get a string value from the "order_id" column.
// If it fails, no exception will be thrown.
if (result.TryGetValue<string>("order_id", out var orderId))
Console.WriteLine($"order_id: {orderId}");
// Checking if the "count" column is null.
if (result.IsNull("count"))
Console.WriteLine("'count' is null");
For details about which type should be used in GetValue<T>
and TryGetValue<T>
, see How ScalarDB Column Types Are Converted to and from .NET Types.
ScanAsync
method example
using static Scalar.Db.Cluster.Rpc.V1.Scan.Types;
// ..
var scan =
new ScanBuilder()
.SetNamespaceName("ns")
.SetTableName("statements")
.SetScanType(ScanType.Scan)
.AddPartitionKey("order_id", "1")
.AddStartClusteringKey("item_id", 2)
.SetStartInclusive(true)
.AddEndClusteringKey("item_id", 8)
.SetEndInclusive(true)
.SetProjections("item_id", "count")
.Build();
var scanResult = await transaction.ScanAsync(scan);
PutAsync
method example
var put =
new PutBuilder()
.SetNamespaceName("ns")
.SetTableName("statements")
.AddPartitionKey("order_id", "1")
.AddClusteringKey("item_id", 2)
.AddColumn("count", 11)
.Build();
await client.PutAsync(put);
DeleteAsync
method example
var delete =
new DeleteBuilder()
.SetNamespaceName("ns")
.SetTableName("statements")
.AddPartitionKey("order_id", "1")
.AddClusteringKey("item_id", 2)
.Build();
await client.DeleteAsync(delete);
MutateAsync
method example:
using Scalar.Db.Cluster.Rpc.V1;
// ...
var put =
new PutBuilder()
.SetNamespaceName("ns")
.SetTableName("statements")
.AddPartitionKey("order_id", "1")
.AddClusteringKey("item_id", 2)
.AddColumn("count", 11)
.Build();
var mutate = new Mutation { Put = put };
await client.MutateAsync(new[] { mutate });
To modify data by using the PutAsync
, DeleteAsync
, or MutateAsync
method, the data must be retrieved first by using the GetAsync
or ScanAsync
method.