From e857c8944ff04009c617e91d5d3433d99cd234f1 Mon Sep 17 00:00:00 2001 From: Reuben Bond Date: Mon, 16 Feb 2026 13:45:16 -0800 Subject: [PATCH 1/2] Make orphan-call transaction abort deterministic Replace the fire-and-forget grain call in OrphanCallTransaction with a direct ambient transaction fork, which always leaves an unjoined pending call and deterministically triggers OrleansOrphanCallException. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Grains/TransactionCoordinatorGrain.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs b/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs index 6d1c38b67fc..f51266f841c 100644 --- a/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs +++ b/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Orleans.Concurrency; +using Orleans.Transactions; using Orleans.Transactions.Abstractions; using Orleans.Transactions.TestKit.Correctnesss; @@ -28,7 +29,7 @@ public Task MultiGrainDouble(List grains) public Task OrphanCallTransaction(ITransactionTestGrain grain) { - _ = grain.Add(1000); + _ = TransactionContext.GetRequiredTransactionInfo().Fork(); return Task.CompletedTask; } From e6206cefee6787474671dc9e7c171cc0a5998792 Mon Sep 17 00:00:00 2001 From: Reuben Bond Date: Mon, 16 Feb 2026 14:11:57 -0800 Subject: [PATCH 2/2] Remove unused orphan-call parameter Address PR feedback by removing the now-unused grain parameter from OrphanCallTransaction in the coordinator interface/implementation and updating call sites and API baseline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Grains/ITransactionCoordinatorGrain.cs | 2 +- .../Grains/TransactionCoordinatorGrain.cs | 2 +- .../TestRunners/GrainFaultTransactionTestRunner.cs | 2 +- .../Orleans.Transactions.TestKit.Base.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Orleans.Transactions.TestKit.Base/Grains/ITransactionCoordinatorGrain.cs b/src/Orleans.Transactions.TestKit.Base/Grains/ITransactionCoordinatorGrain.cs index e7d0222e81a..b6adae108dc 100644 --- a/src/Orleans.Transactions.TestKit.Base/Grains/ITransactionCoordinatorGrain.cs +++ b/src/Orleans.Transactions.TestKit.Base/Grains/ITransactionCoordinatorGrain.cs @@ -24,7 +24,7 @@ public interface ITransactionCoordinatorGrain : IGrainWithGuidKey Task MultiGrainDoubleByWRWR(List grains, int numberToAdd); [Transaction(TransactionOption.Create)] - Task OrphanCallTransaction(ITransactionTestGrain grain); + Task OrphanCallTransaction(); [Transaction(TransactionOption.Create)] Task AddAndThrow(ITransactionTestGrain grain, int numberToAdd); diff --git a/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs b/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs index f51266f841c..0f55f533c08 100644 --- a/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs +++ b/src/Orleans.Transactions.TestKit.Base/Grains/TransactionCoordinatorGrain.cs @@ -27,7 +27,7 @@ public Task MultiGrainDouble(List grains) return Task.WhenAll(grains.Select(Double)); } - public Task OrphanCallTransaction(ITransactionTestGrain grain) + public Task OrphanCallTransaction() { _ = TransactionContext.GetRequiredTransactionInfo().Fork(); return Task.CompletedTask; diff --git a/src/Orleans.Transactions.TestKit.Base/TestRunners/GrainFaultTransactionTestRunner.cs b/src/Orleans.Transactions.TestKit.Base/TestRunners/GrainFaultTransactionTestRunner.cs index 6b349b6ff7d..16527a540da 100644 --- a/src/Orleans.Transactions.TestKit.Base/TestRunners/GrainFaultTransactionTestRunner.cs +++ b/src/Orleans.Transactions.TestKit.Base/TestRunners/GrainFaultTransactionTestRunner.cs @@ -145,7 +145,7 @@ public virtual async Task AbortTransactionOnOrphanCalls(string grainStates) ITransactionCoordinatorGrain coordinator = this.grainFactory.GetGrain(Guid.NewGuid()); await grain.Set(expected); - Func task = () => coordinator.OrphanCallTransaction(grain); + Func task = () => coordinator.OrphanCallTransaction(); await task.Should().ThrowAsync(); //await Task.Delay(20000); // give time for GC diff --git a/src/api/Orleans.Transactions.TestKit.Base/Orleans.Transactions.TestKit.Base.cs b/src/api/Orleans.Transactions.TestKit.Base/Orleans.Transactions.TestKit.Base.cs index c9562e915be..aa672cfca44 100644 --- a/src/api/Orleans.Transactions.TestKit.Base/Orleans.Transactions.TestKit.Base.cs +++ b/src/api/Orleans.Transactions.TestKit.Base/Orleans.Transactions.TestKit.Base.cs @@ -323,7 +323,7 @@ public partial interface ITransactionCoordinatorGrain : IGrainWithGuidKey, IGrai [Transaction(TransactionOption.Create)] System.Threading.Tasks.Task MultiGrainSetBit(System.Collections.Generic.List grains, int bitIndex); [Transaction(TransactionOption.Create)] - System.Threading.Tasks.Task OrphanCallTransaction(ITransactionTestGrain grain); + System.Threading.Tasks.Task OrphanCallTransaction(); [Transaction(TransactionOption.Create)] [Concurrency.ReadOnly] System.Threading.Tasks.Task UpdateViolated(ITransactionTestGrain grains, int numberToAdd); @@ -719,7 +719,7 @@ public partial class TransactionCoordinatorGrain : Grain, ITransactionCoordinato public System.Threading.Tasks.Task MultiGrainSetBit(System.Collections.Generic.List grains, int bitIndex) { throw null; } - public System.Threading.Tasks.Task OrphanCallTransaction(ITransactionTestGrain grain) { throw null; } + public System.Threading.Tasks.Task OrphanCallTransaction() { throw null; } public System.Threading.Tasks.Task UpdateViolated(ITransactionTestGrain grain, int numberToAdd) { throw null; } }