private TransactionConfigBuilder DefaultConfigBuilder(ITestOutputHelper outputHelper) { return(TransactionConfigBuilder.Create() .CleanupClientAttempts(false) .CleanupLostAttempts(false) .LoggerFactory(new ClusterFixture.TestOutputLoggerFactory(outputHelper))); }
public async Task Rollback_Insert_Should_Result_In_No_Document() { var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(Rollback_Insert_Should_Result_In_No_Document), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); var durability = DurabilityLevel.None; var txn = Transactions.Create(_fixture.Cluster); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); var runTask = txn.RunAsync(async ctx => { var insertResult = await ctx.InsertAsync(defaultCollection, docId, sampleDoc); throw ErrorClass.FailHard.Throwable(); }); var transactionFailedException = await Assert.ThrowsAsync <TransactionFailedException>(() => runTask); var result = transactionFailedException.Result; Assert.False(result.UnstagingComplete); var postTxnGetTask = defaultCollection.GetAsync(docId); _ = await Assert.ThrowsAsync <DocumentNotFoundException>(() => postTxnGetTask); }
public async Task <IActionResult> Transfer([FromBody] ProfileTransferCredit request) { try { var bucket = await _bucketProvider.GetBucketAsync(_couchbaseConfig.BucketName); var collection = await bucket.CollectionAsync(_couchbaseConfig.CollectionName); // only use DurabilityLevel.None for single node (e.g. a local single-node install, not Capella) var tx = Transactions.Create(bucket.Cluster, TransactionConfigBuilder.Create().DurabilityLevel(DurabilityLevel.None)); await tx.RunAsync(async (ctx) => { var fromProfileDoc = await ctx.GetAsync(collection, request.Pfrom.ToString()); var fromProfile = fromProfileDoc.ContentAs <Profile>(); var toProfileDoc = await ctx.GetAsync(collection, request.Pto.ToString()); var toProfile = toProfileDoc.ContentAs <Profile>(); fromProfile.TransferTo(toProfile, request.Amount); await ctx.ReplaceAsync(fromProfileDoc, fromProfile); await ctx.ReplaceAsync(toProfileDoc, toProfile); await ctx.CommitAsync(); }); return(Ok()); } catch (Exception ex) { _logger.LogError(ex.Message); return(StatusCode(StatusCodes.Status500InternalServerError, $"Error: {ex.Message} {ex.StackTrace} {Request.GetDisplayUrl()}")); } }
public async Task Basic_Insert_Should_Succeed_CustomMetadataCollection() { // Use a feature from an unreleased CouchbaseNetClient to guarantee we're using latest from master instead. var ex = new Core.Exceptions.CasMismatchException(); var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var customCollection = await _fixture.OpenCustomCollection(_outputHelper); var sampleDoc = new { type = nameof(Basic_Insert_Should_Succeed), foo = "bar", revision = 100 }; var docId = nameof(Basic_Insert_Should_Succeed) + Guid.NewGuid().ToString(); try { var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId + "_testDurability", sampleDoc); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); configBuilder.MetadataCollection(customCollection); var txn = Transactions.Create(_fixture.Cluster, configBuilder.Build()); var result = await txn.RunAsync(async ctx => { var insertResult = await ctx.InsertAsync(defaultCollection, docId, sampleDoc).ConfigureAwait(false); Assert.Equal(ClusterFixture.CustomCollectionName, insertResult?.TransactionXattrs?.AtrRef?.CollectionName); var getResult = await ctx.GetAsync(defaultCollection, docId); Assert.NotNull(getResult); var asJobj = getResult !.ContentAs <JObject>(); Assert.Equal("bar", asJobj["foo"].Value <string>()); Assert.Equal(ClusterFixture.CustomCollectionName, getResult?.TransactionXattrs?.AtrRef?.CollectionName); }); var postTxnGetResult = await defaultCollection.GetAsync(docId); var postTxnDoc = postTxnGetResult.ContentAs <dynamic>(); Assert.Equal("100", postTxnDoc.revision.ToString()); var postTxnLookupInResult = await defaultCollection.LookupInAsync(docId, spec => spec.Get("txn", isXattr: true)); Assert.False(postTxnLookupInResult.Exists(0)); } catch { throw; } finally { try { await defaultCollection.RemoveAsync(docId); } catch (Exception e) { _outputHelper.WriteLine($"Error during cleanup: {e.ToString()}"); } } }
void ConfigExpired() { // #tag::config-expiration[] Transactions transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create() .ExpirationTime(TimeSpan.FromSeconds(120)) .Build()); // #end::config-expiration[] }
void Config() { // #tag::config[] var transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create() .DurabilityLevel(DurabilityLevel.PersistToMajority) .Build()); // #end::config[] }
void ConfigCleanup(byte[] encoded) { // #tag::config-cleanup[] Transactions transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create() .CleanupClientAttempts(false) .CleanupLostAttempts(false) .CleanupWindow(TimeSpan.FromSeconds(120)) .Build()); // #end::config-cleanup[] }
public async Task DocumentLookup_Should_Include_Metadata() { var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(DocumentLookup_Should_Include_Metadata), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId, sampleDoc); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); if (Debugger.IsAttached) { configBuilder.ExpirationTime(TimeSpan.FromMinutes(10)); } var txn = Transactions.Create(_fixture.Cluster, configBuilder); txn.TestHooks = new DelegateTestHooks() { BeforeDocCommittedImpl = async(ctx, id) => { var documentLookupResult = await DocumentRepository.LookupDocumentAsync(defaultCollection, id, null, true); return(0); }, AfterStagedReplaceCompleteImpl = async(ctx, id) => { var documentLookupResult = await DocumentRepository.LookupDocumentAsync(defaultCollection, id, null, true); return(0); } }; var result = await txn.RunAsync(async ctx => { var getResult = await ctx.GetAsync(defaultCollection, docId); var docGet = getResult !.ContentAs <dynamic>(); docGet.revision = docGet.revision + 1; var replaceResult = await ctx.ReplaceAsync(getResult, docGet); var documentLookupResult = await DocumentRepository.LookupDocumentAsync(defaultCollection, docId, null, true); Assert.NotNull(documentLookupResult?.TransactionXattrs); Assert.NotNull(documentLookupResult?.StagedContent?.ContentAs <object>()); _outputHelper.WriteLine(JObject.FromObject(documentLookupResult !.TransactionXattrs).ToString()); }); }
void Config() { // #tag::config[] var transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create() .DurabilityLevel(DurabilityLevel.PersistToMajority) /* // #tag::config_warn[] * .LogOnFailure(true, Event.Severity.WARN) * // #end::config_warn[]*/ .Build()); // #end::config[] }
public async Task Exception_Rollback_Should_Result_In_No_Changes() { var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(Exception_Rollback_Should_Result_In_No_Changes), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); try { var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId, sampleDoc); var txn = Transactions.Create(_fixture.Cluster); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); int attemptCount = 0; var runTask = txn.RunAsync(async ctx => { attemptCount++; var getResult = await ctx.GetAsync(defaultCollection, docId); var docGet = getResult.ContentAs <dynamic>(); docGet.revision = docGet.revision + 1; var replaceResult = await ctx.ReplaceAsync(getResult, docGet); throw new InvalidOperationException("Forcing rollback."); }); await Assert.ThrowsAsync <TransactionFailedException>(() => runTask); Assert.Equal(1, attemptCount); var postTxnGetResult = await defaultCollection.GetAsync(docId); var postTxnDoc = postTxnGetResult.ContentAs <dynamic>(); Assert.Equal("100", postTxnDoc.revision.ToString()); } finally { try { await defaultCollection.RemoveAsync(docId); } catch (Exception e) { _outputHelper.WriteLine($"Error during cleanup: {e.ToString()}"); throw; } } }
public async Task DataAccess_Is_Abstracted() { // After the data access was refactored into repository classes, the ICouchbaseCollection instances passed in shouldn't actually be accessed. // We verify this by using Mock with strict behavior and NotImplemented members. // The test should run to the end without hitting any of the ICouchbaseCollection other than the names and fetching the default collection on the bucket. using var cluster = CreateTestCluster(Enumerable.Empty <TransactionGetResult>()); var mockCollection = new MockCollectionWithNames(nameof(DataAccess_Is_Abstracted) + "col", nameof(DataAccess_Is_Abstracted) + "scp", nameof(DataAccess_Is_Abstracted) + "bkt"); var atr = new MockAtrRepository(); var doc = new MockDocumentRepository(); string docId = nameof(DataAccess_Is_Abstracted) + ".id"; var mockLookupInResult = new Mock <ILookupInResult>(MockBehavior.Strict); mockLookupInResult.SetupGet(l => l.IsDeleted).Returns(false); mockLookupInResult.SetupGet(l => l.Cas).Returns(5); doc.Add(mockCollection, docId, new DataModel.DocumentLookupResult(docId, new JObjectContentWrapper(new { foo = "original" }), null, mockLookupInResult.Object, new Components.DocumentMetadata(), mockCollection)); var configBuilder = TransactionConfigBuilder.Create().DurabilityLevel(DurabilityLevel.Majority); try { await using var transactions = Transactions.Create(cluster); transactions.DocumentRepository = doc; transactions.AtrRepository = atr; var tr = await transactions.RunAsync(async ctx => { var fetched = await ctx.GetAsync(mockCollection, docId); var replaced = await ctx.ReplaceAsync(fetched, new { foo = "bar" }); await ctx.RemoveAsync(replaced); var inserted = await ctx.InsertAsync(mockCollection, docId + "inserted", new { foo = "inserted in transaction" }); }); try { var aborted = await transactions.RunAsync(async ctx => { var inserted = await ctx.InsertAsync(mockCollection, docId + "inserted_to_rollback", new { foo = "to be thrown" }); throw new InvalidOperationException("force fail"); }); } catch (TransactionFailedException) { } } catch (Exception ex) { _outputHelper.WriteLine(ex.ToString()); throw; } }
public static Transactions CreateTransaction(ICluster cluster, DurabilityLevel durability, ITestOutputHelper outputHelper) { var configBuilder = TransactionConfigBuilder.Create() .DurabilityLevel(durability) .LoggerFactory(new ClusterFixture.TestOutputLoggerFactory(outputHelper)); if (Debugger.IsAttached) { // don't expire when watching the debugger. configBuilder.ExpirationTime(TimeSpan.FromMinutes(1000)); } var txn = Transactions.Create(cluster, configBuilder.Build()); return(txn); }
static async Task Main(string[] args) { var options = new ClusterOptions().WithCredentials("Administrator", "password"); var cluster = await Cluster.ConnectAsync("couchbase://localhost", options).ConfigureAwait(false); var bucket = await cluster.BucketAsync("default").ConfigureAwait(false); var collection = bucket.DefaultCollection(); var transactions = Transactions.Create(cluster, TransactionConfigBuilder.Create()); using var program = new Program(cluster, bucket, collection, transactions); Console.WriteLine("Hello World!"); }
public async Task Basic_Replace_Should_Succeed() { var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(Basic_Replace_Should_Succeed), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); try { var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId, sampleDoc); var txn = Transactions.Create(_fixture.Cluster); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); var result = await txn.RunAsync(async ctx => { var getResult = await ctx.GetAsync(defaultCollection, docId); var docGet = getResult.ContentAs <dynamic>(); docGet.revision = docGet.revision + 1; var replaceResult = await ctx.ReplaceAsync(getResult, docGet); }); Assert.True(result.UnstagingComplete); var postTxnGetResult = await defaultCollection.GetAsync(docId); var postTxnDoc = postTxnGetResult.ContentAs <dynamic>(); Assert.Equal("101", postTxnDoc.revision.ToString()); await txn.DisposeAsync(); } finally { try { await defaultCollection.RemoveAsync(docId); } catch (Exception e) { _outputHelper.WriteLine($"Error during cleanup: {e.ToString()}"); throw; } } }
public async Task Basic_Remove_Should_Succeed() { bool removed = false; var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(Basic_Remove_Should_Succeed), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); try { var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId, sampleDoc); var txn = Transactions.Create(_fixture.Cluster); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); var result = await txn.RunAsync(async ctx => { var getResult = await ctx.GetAsync(defaultCollection, docId); var docGet = getResult.ContentAs <dynamic>(); docGet.revision = docGet.revision + 1; await ctx.RemoveAsync(getResult); }); await Assert.ThrowsAsync <DocumentNotFoundException>(() => defaultCollection.GetAsync(docId)); removed = true; } finally { try { if (!removed) { await defaultCollection.RemoveAsync(docId); } } catch (Exception e) { _outputHelper.WriteLine($"Error during cleanup: {e.ToString()}"); throw; } } }
public async Task Singles(string statement) { (var defaultCollection, var docId, var sampleDoc) = await TestUtil.PrepSampleDoc(_fixture, _outputHelper); _outputHelper.WriteLine(sampleDoc.ToString()); var loggerFactory = new ClusterFixture.TestOutputLoggerFactory(_outputHelper); await defaultCollection.InsertAsync(docId, sampleDoc); var txnCfg = TransactionConfigBuilder.Create().LoggerFactory(loggerFactory); var txn = TestUtil.CreateTransaction(_fixture.Cluster, KeyValue.DurabilityLevel.None, _outputHelper); var config = new SingleQueryTransactionConfigBuilder(); config.QueryOptionsValue.Parameter("docId", docId); var results = await txn.QueryAsync <object>(statement, config); await foreach (var r in results.QueryResult.Rows) { _outputHelper.WriteLine($"result = {r}"); } }
async Task CompleteLogging() { // #tag::full-logging[] //Logging dependencies var services = new ServiceCollection(); services.AddLogging(builder => { builder.AddFile(AppContext.BaseDirectory); builder.AddConsole(); }); await using var provider = services.BuildServiceProvider(); var loggerFactory = provider.GetService <ILoggerFactory>(); var logger = loggerFactory.CreateLogger <Program>(); //create the transactions object and add the ILoggerFactory var transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create().LoggerFactory(loggerFactory)); try { var result = await transactions.RunAsync(async ctx => { // ... transactional code here ... }); } catch (TransactionCommitAmbiguousException err) { // The transaction may or may not have reached commit point logger.LogInformation("Transaction returned TransactionCommitAmbiguous and" + " may have succeeded, logs:"); Console.Error.WriteLine(err); } catch (TransactionFailedException err) { // The transaction definitely did not reach commit point logger.LogInformation("Transaction failed with TransactionFailed, logs:"); Console.Error.WriteLine(err); } // #end::full-logging[] }
public async Task Retry_On_Certain_Failures() { var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new { type = nameof(Retry_On_Certain_Failures), foo = "bar", revision = 100 }; var docId = Guid.NewGuid().ToString(); try { var durability = DurabilityLevel.Majority; try { _ = await defaultCollection.InsertAsync(docId, sampleDoc, opts => opts.Durability(durability)); } catch (DurabilityImpossibleException) { // when running on single-node cluster, such as localhost. durability = DurabilityLevel.None; _ = await defaultCollection.InsertAsync(docId, sampleDoc, opts => opts.Durability(durability)); } var txn = Transactions.Create(_fixture.Cluster); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); int attemptCount = 0; var result = await txn.RunAsync(async ctx => { attemptCount++; var getResult = await ctx.GetAsync(defaultCollection, docId); var docGet = getResult.ContentAs <dynamic>(); docGet.revision = docGet.revision + 1; var replaceResult = await ctx.ReplaceAsync(getResult, docGet); if (attemptCount < 3) { throw new TestRetryException("force retry", new InvalidOperationException()); } }); Assert.True(result.UnstagingComplete); var postTxnGetResult = await defaultCollection.GetAsync(docId); var postTxnDoc = postTxnGetResult.ContentAs <dynamic>(); Assert.Equal("101", postTxnDoc.revision.ToString()); } catch (Exception ex) { _outputHelper.WriteLine($"Error during main try: {ex.ToString()}"); throw; } finally { try { await defaultCollection.RemoveAsync(docId); } catch (Exception e) { _outputHelper.WriteLine($"Error during cleanup: {e.ToString()}"); throw; } } }
static async Task Main(string[] args) { try { var clusterOptions = new ClusterOptions() { RedactionLevel = Couchbase.Core.Logging.RedactionLevel.Partial, UserName = "******", Password = "******" }; var connectionString = "couchbase://localhost"; await using var cluster = await Cluster.ConnectAsync(connectionString, clusterOptions); await using var bucket = await cluster.BucketAsync("default"); var collection = await bucket.DefaultCollectionAsync(); var sampleDoc = new ExampleTransactionDocument(); var insertResult = await collection.InsertAsync(sampleDoc.Id, sampleDoc) .ConfigureAwait(false); var getResultRoundTrip = await collection.GetAsync(sampleDoc.Id).ConfigureAwait(false); var roundTripSampleDoc = getResultRoundTrip.ContentAs <ExampleTransactionDocument>(); Serilog.Log.Logger = new Serilog.LoggerConfiguration() .Enrich.FromLogContext() .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss.fff} {Level:u3}] |{Properties:j}| [{SourceContext:l}] {Message:lj}{NewLine}{Exception}").CreateLogger(); var configBuilder = TransactionConfigBuilder.Create() .LoggerFactory(new SerilogLoggerFactory()) .DurabilityLevel(DurabilityLevel.None); if (Debugger.IsAttached) { configBuilder.ExpirationTime(TimeSpan.FromMinutes(10)); } var txn = Transactions.Create(cluster, configBuilder.Build()); for (int i = 0; i < 1; i++) { var sw = Stopwatch.StartNew(); var txnResult = await txn.RunAsync(async ctx => { var getResult = await ctx.GetAsync(collection, sampleDoc.Id).ConfigureAwait(false); var docGet = getResult.ContentAs <JObject>(); var insertResult = await ctx.InsertAsync(collection, Guid.NewGuid().ToString(), docGet); docGet["revision"] = docGet["revision"].Value <int>() + 1; var replaceResult1 = await ctx.ReplaceAsync(insertResult, docGet).ConfigureAwait(false); var replaceResult2 = await ctx.ReplaceAsync(getResult, docGet).ConfigureAwait(false); // Commit happens automatically at this point. You don't need to call it explicitly. }).ConfigureAwait(false); sw.Stop(); Console.Out.WriteLine(txnResult.ToString()); Console.Out.WriteLine($"Elapsed = {sw.Elapsed.TotalMilliseconds}ms"); } } catch (Exception e) { Console.Error.WriteLine(e.ToString()); throw; } }
private async Task <bool> CanonicalExample() { using var cluster = CreateTestCluster(Enumerable.Empty <TransactionGetResult>()); var configBuilder = TransactionConfigBuilder.Create() .DurabilityLevel(DurabilityLevel.Majority); if (Debugger.IsAttached) { configBuilder.ExpirationTime(TimeSpan.FromMinutes(10)); } using var transactions = Transactions.Create(cluster, configBuilder.Build()); bool reachedPostCommit = false; TransactionResult tr = null; try { tr = await transactions.RunAsync(async ctx => { // Inserting a doc: var docId = "test-id"; var bucket = await cluster.BucketAsync("test-bucket").ConfigureAwait(false); var collection = await bucket.DefaultCollectionAsync(); var insertResult = await ctx.InsertAsync(collection, docId, new JObject()).ConfigureAwait(false); // Getting documents: var docOpt = await ctx.GetOptionalAsync(collection, docId).ConfigureAwait(false); var doc = await ctx.GetAsync(collection, docId).ConfigureAwait(false); // Replacing a document: var anotherDoc = await ctx.GetAsync(collection, "anotherDoc").ConfigureAwait(false); var content = anotherDoc.ContentAs <JObject>(); content["transactions"] = "are awesome"; await ctx.ReplaceAsync(anotherDoc, content); // Removing a document: var yetAnotherDoc = await ctx.GetAsync(collection, "yetAnotherDoc)").ConfigureAwait(false); await ctx.RemoveAsync(yetAnotherDoc).ConfigureAwait(false); await ctx.CommitAsync().ConfigureAwait(false); reachedPostCommit = true; }); } catch (TransactionCommitAmbiguousException e) { // TODO: log individual errors _outputHelper.WriteLine(e.ToString()); throw; } catch (TransactionFailedException e) { // TODO: log errors from exception _outputHelper.WriteLine(e.ToString()); throw; } Assert.NotNull(tr); return(reachedPostCommit); }
static async Task Main(string[] args) { // SETUP: connect to Couchbase _cluster = await Cluster.ConnectAsync( "couchbase://localhost", "Administrator", "password"); _bucket = await _cluster.BucketAsync("matt"); _scope = await _bucket.ScopeAsync("myScope"); _coll = await _scope.CollectionAsync("myCollection"); // SETUP: create a 'conference' document and a 'conference activities' document await SetupInitialDocuments(); // STEP 1: create transactions object var transactions = Transactions.Create(_cluster, TransactionConfigBuilder.Create() .DurabilityLevel(DurabilityLevel.MajorityAndPersistToActive) // since I have 1 node, replication must be 0, or this will throw exception .Build()); Console.WriteLine("Press ENTER to continue"); Console.ReadLine(); // STEP 2: transaction operations await transactions.RunAsync(async (ctx) => { var now = DateTime.Now; // FIRST: get the two document I want to change var confDoc = await ctx.GetAsync(_coll, "dataLove2021"); var actsDoc = await ctx.GetAsync(_coll, "dataLove2021::activities"); var conf = confDoc.ContentAs <Conference>(); var acts = actsDoc.ContentAs <ConferenceActivities>(); // SECOND: add an event to the "activities" document acts.Events.Add(new ConferenceEvent { Type = "CFP", DtActivity = now, Desc = "Submitted to the CFP" }); // acts.Events.Add(new ConferenceEvent // { // Type = "PRESENTATION", // DtActivity = now, // Desc = "Delivered ACID presentation" // }); // acts.Events.Add(new ConferenceEvent // { // Type = "SPATIAL", // DtActivity = now, // Desc = "Answered questions in Spatial Chat" // }); // THIRD: change the "conference" document conf.Followups = (conf.Followups ?? 0) + 1; conf.LastActivity = now; // FOURTH: write the changes await ctx.ReplaceAsync(confDoc, conf); // OPTIONAL STEP: fail right in the middle of the transaction making two writes // var fail = true; // if(fail) throw new Exception("Something went wrong!"); await ctx.ReplaceAsync(actsDoc, acts); // FIFTH: commit (implied) }); await _cluster.DisposeAsync(); }
public async Task Parallel_Increments_All_Succeed(int parallelCount) { var barrier = new Barrier(parallelCount); var defaultCollection = await _fixture.OpenDefaultCollection(_outputHelper); var sampleDoc = new ParallelDoc { Type = nameof(Parallel_Increments_All_Succeed), Name = "ContentiousDoc", Participants = new List <int>(), Count = 0 }; var docId = nameof(Parallel_Increments_All_Succeed) + Guid.NewGuid().ToString(); var durability = await TestUtil.InsertAndVerifyDurability(defaultCollection, docId, sampleDoc); var sw = Stopwatch.StartNew(); var tasks = Enumerable.Range(0, parallelCount).Select(i => Task.Run( async() => { try { _outputHelper.WriteLine($"{sw.Elapsed}:{i}: BEGIN_INIT"); var configBuilder = TransactionConfigBuilder.Create(); configBuilder.DurabilityLevel(durability); configBuilder.LoggerFactory(new TestOutputLoggerFactory(_outputHelper)); if (parallelCount > 50) { configBuilder.ExpirationTime(TimeSpan.FromMinutes(5)); } await using var txn = Transactions.Create(_fixture.Cluster, configBuilder.Build()); barrier.SignalAndWait(); _outputHelper.WriteLine($"{sw.Elapsed}:{i}: BEFORE_RUN"); var swRunTime = Stopwatch.StartNew(); try { long retryCount = 0; var tr = await txn.RunAsync(async ctx => { _outputHelper.WriteLine($"{sw.Elapsed}:{i}: BEGIN_RUN, retryCount={retryCount}"); Interlocked.Increment(ref retryCount); var getResult = await ctx.GetAsync(defaultCollection, docId); var doc = getResult.ContentAs <ParallelDoc>(); doc.Count++; doc.Participants.Add(i); doc.LastParticipant = i; _ = await ctx.ReplaceAsync(getResult, doc); _outputHelper.WriteLine($"{sw.Elapsed}:{i}: END_RUN"); }); } finally { _outputHelper.WriteLine($"{sw.Elapsed}:{i}: AFTER_RUN {swRunTime.Elapsed}"); } } catch (Exception ex) { _outputHelper.WriteLine($"{sw.Elapsed}:{i}:Exception! {ex.ToString()}"); throw; } })); try { await Task.WhenAll(tasks); } finally { var getResult = await defaultCollection.GetAsync(docId); var finalDoc = getResult.ContentAs <ParallelDoc>(); _outputHelper.WriteLine(JObject.FromObject(finalDoc).ToString()); Assert.Equal(parallelCount, finalDoc.Count); } }
async Task QueryExamples() { // this isn't meant to run, merely to compile correctly. ICluster cluster = null; var transactions = Transactions.Create(cluster); { // tag::queryExamplesSelect[] var st = "SELECT * FROM `travel-sample`.inventory.hotel WHERE country = $1"; var transactionResult = await transactions.RunAsync(async ctx => { IQueryResult <object> qr = await ctx.QueryAsync <object>(st, new TransactionQueryOptions().Parameter("United Kingdom")); await foreach (var result in qr.Rows) { Console.Out.WriteLine($"result = {result}", result); } }); // end::queryExamplesSelect[] } { // tag::queryExamplesSelectScope[] IBucket travelSample = await cluster.BucketAsync("travel-sample"); IScope inventory = travelSample.Scope("inventory"); var transactionResult = await transactions.RunAsync(async ctx => { var st = "SELECT * FROM `travel-sample`.inventory.hotel WHERE country = $1"; IQueryResult <object> qr = await ctx.QueryAsync <object>(st, options: new TransactionQueryOptions().Parameter("United Kingdom"), scope: inventory); }); // end::queryExamplesSelectScope[] } { IBucket travelSample = await cluster.BucketAsync("travel-sample"); IScope inventory = travelSample.Scope("inventory"); // tag::queryExamplesUpdate[] var hotelChain = "http://marriot%"; var country = "United States"; await transactions.RunAsync(async ctx => { var qr = await ctx.QueryAsync <object>( statement: "UPDATE hotel SET price = $price WHERE url LIKE $url AND country = $country", configure: options => options.Parameter("price", 99.99m) .Parameter("url", hotelChain) .Parameter("country", country), scope: inventory); Console.Out.WriteLine($"Records Updated = {qr?.MetaData.Metrics.MutationCount}"); }); // end::queryExamplesUpdate[] } { IBucket travelSample = await cluster.BucketAsync("travel-sample"); IScope inventory = travelSample.Scope("inventory"); var hotelChain = "http://marriot%"; var country = "United States"; //private class Review { }; // tag::queryExamplesComplex[] await transactions.RunAsync(async ctx => { // Find all hotels of the chain IQueryResult <Review> qr = await ctx.QueryAsync <Review>( statement: "SELECT reviews FROM hotel WHERE url LIKE $1 AND country = $2", configure: options => options.Parameter(hotelChain).Parameter(country), scope: inventory); // This function (not provided here) will use a trained machine learning model to provide a // suitable price based on recent customer reviews. var updatedPrice = PriceFromRecentReviews(qr); // Set the price of all hotels in the chain await ctx.QueryAsync <object>( statement: "UPDATE hotel SET price = $1 WHERE url LIKE $2 AND country = $3", configure: options => options.Parameter(hotelChain, country, updatedPrice), scope: inventory); }); // end::queryExamplesComplex[] } { // tag::queryInsert[] await transactions.RunAsync(async ctx => { await ctx.QueryAsync <object>("INSERT INTO `default` VALUES ('doc', {'hello':'world'})", TransactionQueryConfigBuilder.Create()); // <1> // Performing a 'Read Your Own Write' var st = "SELECT `default`.* FROM `default` WHERE META().id = 'doc'"; // <2> IQueryResult <object> qr = await ctx.QueryAsync <object>(st, TransactionQueryConfigBuilder.Create()); Console.Out.WriteLine($"ResultCount = {qr?.MetaData.Metrics.ResultCount}"); }); // end::queryInsert[] } { // tag::querySingle[] var bulkLoadStatement = "<a bulk-loading N1QL statement>"; try { SingleQueryTransactionResult <object> result = await transactions.QueryAsync <object>(bulkLoadStatement); IQueryResult <object> queryResult = result.QueryResult; } catch (TransactionCommitAmbiguousException e) { Console.Error.WriteLine("Transaction possibly committed"); foreach (var log in e.Result.Logs) { Console.Error.WriteLine(log); } } catch (TransactionFailedException e) { Console.Error.WriteLine("Transaction did not reach commit point"); foreach (var log in e.Result.Logs) { Console.Error.WriteLine(log); } } // end::querySingle[] } { string bulkLoadStatement = null /* your statement here */; // tag::querySingleScoped[] IBucket travelSample = await cluster.BucketAsync("travel-sample"); IScope inventory = travelSample.Scope("inventory"); await transactions.QueryAsync <object>(bulkLoadStatement, scope : inventory); // end::querySingleScoped[] } { string bulkLoadStatement = null; /* your statement here */ // tag::querySingleConfigured[] // with the Builder pattern. await transactions.QueryAsync <object>(bulkLoadStatement, SingleQueryTransactionConfigBuilder.Create() // Single query transactions will often want to increase the default timeout .ExpirationTime(TimeSpan.FromSeconds(360))); // using the lambda style await transactions.QueryAsync <object>(bulkLoadStatement, config => config.ExpirationTime(TimeSpan.FromSeconds(360))); // end::querySingleConfigured[] } { ICouchbaseCollection collection = null; // tag::queryRyow[] await transactions.RunAsync(async ctx => { _ = await ctx.InsertAsync(collection, "doc", new { Hello = "world" }); // <1> // Performing a 'Read Your Own Write' var st = "SELECT `default`.* FROM `default` WHERE META().id = 'doc'"; // <2> var qr = await ctx.QueryAsync <object>(st); Console.Out.WriteLine($"ResultCount = {qr?.MetaData.Metrics.ResultCount}"); }); // end::queryRyow[] } { // tag::queryOptions[] await transactions.RunAsync(async ctx => { await ctx.QueryAsync <object>("INSERT INTO `default` VALUES ('doc', {'hello':'world'})", new TransactionQueryOptions().FlexIndex(true)); }); // end::queryOptions[] } { // tag::custom-metadata[] ICouchbaseCollection metadataCollection = null; // this is a Collection opened by your code earlier Transactions transactionsWithCustomMetadataCollection = Transactions.Create(cluster, TransactionConfigBuilder.Create().MetadataCollection(metadataCollection)); // end::custom-metadata[] } }
static async Task Main(string[] args) { Parser.Default.ParseArguments <Options>(args).WithParsed(RunOptions); var config = TransactionConfigBuilder.Create(); config.DurabilityLevel(ParseDurability(_options.Durability)); // Initialize the Couchbase cluster var cluster = await Cluster.ConnectAsync(_options.Cluster, _options.UserName, _options.Password).ConfigureAwait(false); var bucket = await cluster.BucketAsync(_options.Bucket).ConfigureAwait(false); var collection = bucket.DefaultCollection(); // Initialize transactions. Must only be one Transactions object per app as it creates background resources. var transactions = Transactions.Create(cluster, config); //Logging dependencies var services = new ServiceCollection(); services.AddLogging(builder => { builder.AddFile(AppContext.BaseDirectory); builder.AddConsole(); }); await using var provider = services.BuildServiceProvider(); var loggerFactory = provider.GetService <ILoggerFactory>(); var logger = loggerFactory.CreateLogger <Program>(); var gameServer = new GameServer(transactions, collection, logger); // Initialise some sample data - a player and a monster. This is based on the Game Simulation sample bucket // provided with Couchbase, though that does not have to be installed. var playerId = "player_jane"; var player = new { experiance = 14248, hitpoints = 23832, jsonType = "player", level = 141, loggedIn = true, name = "Jane", uuid = Guid.NewGuid() }; var monsterId = "a_grue"; var monster = new { experienceWhenKilled = 91, hitpoints = 4000, itemProbability = 0.19239324085462631, name = "grue", uuid = Guid.NewGuid() }; await collection.UpsertAsync(playerId, player).ConfigureAwait(false); logger.LogInformation($"Upserted sample player document {playerId}"); await collection.UpsertAsync(monsterId, monster).ConfigureAwait(false); logger.LogInformation($"Upserted sample monster document {monsterId}"); // Now perform the transaction // The player is hitting the monster for a certain amount of damage await gameServer.PlayerHitsMonster( // This UUID identifies this action from the player's client Guid.NewGuid().ToString(), // This has a 50% chance of killing the monster, which has 4000 hitpoints new Random().Next(0, 8000), playerId, monsterId).ConfigureAwait(false); // Shutdown resources cleanly transactions.Dispose(); await cluster.DisposeAsync().ConfigureAwait(false); Console.Read(); }