internal async Task PersistenceStorage_WriteReadWriteRead100StatesInParallel(string prefix = nameof(this.PersistenceStorage_WriteReadWriteRead100StatesInParallel)) { //As data is written and read the ETags are as checked for correctness (they change). //Additionally the Store_WriteRead tests does its validation. var grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); int StartOfRange = 33900; const int CountOfRange = 100; string grainIdTemplate = $"{prefix}-" + "{0}"; var tasks = Enumerable.Range(StartOfRange, CountOfRange).Select(async i => { //Since the version is NULL, storage provider tries to insert this data //as new state. If there is already data with this class, the writing fails //and the storage provider throws. Essentially it means either this range //is ill chosen or the test failed due another problem. var grainId = string.Format(grainIdTemplate, i); var grainData = CommonStorageUtilities.GetTestReferenceAndState(grainId, null); var firstVersion = grainData.Item2.ETag; Assert.Equal(firstVersion, null); await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2); var secondVersion = grainData.Item2.ETag; Assert.NotEqual(firstVersion, secondVersion); await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2); var thirdVersion = grainData.Item2.ETag; Assert.NotEqual(firstVersion, secondVersion); Assert.NotEqual(secondVersion, thirdVersion); }); await Task.WhenAll(tasks); }
internal async Task PersistenceStorage_WriteReadWriteReadStatesInParallel(string prefix = nameof(this.PersistenceStorage_WriteReadWriteReadStatesInParallel), int countOfGrains = 100) { //As data is written and read the Version numbers (ETags) are as checked for correctness (they change). //Additionally the Store_WriteRead tests does its validation. var grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); int StartOfRange = 33900; int CountOfRange = countOfGrains; string grainIdTemplate = $"{prefix}-{{0}}"; //Since the version is NULL, storage provider tries to insert this data //as new state. If there is already data with this class, the writing fails //and the storage provider throws. Essentially it means either this range //is ill chosen or the test failed due to another problem. var grainStates = Enumerable.Range(StartOfRange, CountOfRange).Select(i => this.GetTestReferenceAndState(string.Format(grainIdTemplate, i), null)).ToList(); // Avoid parallelization of the first write to not stress out the system with deadlocks // on INSERT foreach (var grainData in grainStates) { //A sanity checker that the first version really has null as its state. Then it is stored //to the database and a new version is acquired. var firstVersion = grainData.Item2.ETag; Assert.Null(firstVersion); await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2).ConfigureAwait(false); var secondVersion = grainData.Item2.ETag; Assert.NotEqual(firstVersion, secondVersion); } ; int MaxNumberOfThreads = Environment.ProcessorCount * 3; // The purpose of Parallel.ForEach is to ensure the storage provider will be tested from // multiple threads concurrently, as would happen in running system also. Nevertheless // limit the degree of parallelization (concurrent threads) to avoid unnecessarily // starving and growing the thread pool (which is very slow) if a few threads coupled // with parallelization via tasks can force most concurrency scenarios. Parallel.ForEach(grainStates, new ParallelOptions { MaxDegreeOfParallelism = MaxNumberOfThreads }, async grainData => { // This loop writes the state consecutive times to the database to make sure its // version is updated appropriately. for (int k = 0; k < 10; ++k) { var versionBefore = grainData.Item2.ETag; await RetryHelper.RetryOnExceptionAsync(5, RetryOperation.Sigmoid, async() => { await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2); return(Task.CompletedTask); }); var versionAfter = grainData.Item2.ETag; Assert.NotEqual(versionBefore, versionAfter); } }); }
/// <summary> /// Writes a known inconsistent state to the storage and asserts an exception will be thrown. /// </summary> /// <returns>The <see cref="InconsistentStateException"/> thrown by the provider. This can be further inspected /// by the storage specific asserts.</returns> internal async Task <InconsistentStateException> PersistenceStorage_WriteInconsistentFailsWithInconsistentStateException() { //Some version not expected to be in the storage for this type and ID. var inconsistentStateVersion = RandomUtilities.GetRandom <int>().ToString(CultureInfo.InvariantCulture); var inconsistentState = CommonStorageUtilities.GetTestReferenceAndState(RandomUtilities.GetRandom <long>(), inconsistentStateVersion); string grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); var exception = await Record.ExceptionAsync(() => Store_WriteRead(grainTypeName, inconsistentState.Item1, inconsistentState.Item2)); Assert.NotNull(exception); Assert.IsType <InconsistentStateException>(exception); return((InconsistentStateException)exception); }
/// <summary> /// Writes a known inconsistent state to the storage and asserts an exception will be thrown. /// </summary> /// <returns></returns> internal async Task PersistenceStorage_Relational_WriteReadIdCyrillic() { var grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); var grainReference = CommonStorageUtilities.GetTestReferenceAndState(0, null); var grainState = grainReference.Item2; await Storage.WriteStateAsync(grainTypeName, grainReference.Item1, grainState); var storedGrainState = new GrainState <TestState1> { State = new TestState1() }; await Storage.ReadStateAsync(grainTypeName, grainReference.Item1, storedGrainState); Assert.Equal(grainState.ETag, storedGrainState.ETag); Assert.Equal(grainState.State, storedGrainState.State); }
/// <summary> /// Writes a known inconsistent state to the storage and asserts an exception will be thrown. /// </summary> /// <returns></returns> internal async Task PersistenceStorage_Relational_WriteReadIdCyrillic() { var grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); var grainReference = this.GetTestReferenceAndState(0, null); var grainState = grainReference.GrainState; await Storage.WriteStateAsync(grainTypeName, grainReference.GrainReference, grainState).ConfigureAwait(false); var storedGrainState = new GrainState <TestState1> { State = new TestState1() }; await Storage.ReadStateAsync(grainTypeName, grainReference.GrainReference, storedGrainState).ConfigureAwait(false); Assert.Equal(grainState.ETag, storedGrainState.ETag); Assert.Equal(grainState.State, storedGrainState.State); }
/// <summary> /// Writes to storage and tries to re-write the same state with NULL as ETag, as if the grain was just created. /// </summary> /// <returns>The <see cref="InconsistentStateException"/> thrown by the provider. This can be further inspected /// by the storage specific asserts.</returns> internal async Task <InconsistentStateException> PersistenceStorage_WriteDuplicateFailsWithInconsistentStateException() { //A grain with a random ID will be arranged to the database. Then its state is set to null to simulate the fact //it is like a second activation after a one that has succeeded to write. string grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); var inconsistentState = CommonStorageUtilities.GetTestReferenceAndState(RandomUtilities.GetRandom <long>(), null); var grainReference = inconsistentState.Item1; var grainState = inconsistentState.Item2; await Store_WriteRead(grainTypeName, inconsistentState.Item1, inconsistentState.Item2); grainState.ETag = null; var exception = await Record.ExceptionAsync(() => Store_WriteRead(grainTypeName, grainReference, grainState)); Assert.NotNull(exception); Assert.IsType <InconsistentStateException>(exception); return((InconsistentStateException)exception); }
internal async Task PersistenceStorage_WriteReadWriteReadStatesInParallel(string prefix = nameof(this.PersistenceStorage_WriteReadWriteReadStatesInParallel), int countOfGrains = 100) { //As data is written and read the Version numbers (ETags) are as checked for correctness (they change). //Additionally the Store_WriteRead tests does its validation. var grainTypeName = GrainTypeGenerator.GetGrainType <Guid>(); int StartOfRange = 33900; int CountOfRange = countOfGrains; string grainIdTemplate = $"{prefix}-" + "{0}"; //The purpose of this Task.Run is to ensure the storage provider will be tested from //multiple threads concurrently, as would happen in running system also. var tasks = Enumerable.Range(StartOfRange, CountOfRange).Select(i => Task.Run(async() => { //Since the version is NULL, storage provider tries to insert this data //as new state. If there is already data with this class, the writing fails //and the storage provider throws. Essentially it means either this range //is ill chosen or the test failed due another problem. var grainId = string.Format(grainIdTemplate, i); var grainData = CommonStorageUtilities.GetTestReferenceAndState(i, null); //A sanity checker that the first version really has null as its state. Then it is stored //to the database and a new version is acquired. var firstVersion = grainData.Item2.ETag; Assert.Equal(firstVersion, null); //This loop writes the state consecutive times to the database to make sure its //version is updated appropriately. await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2); for (int k = 0; k < 10; ++k) { var secondVersion = grainData.Item2.ETag; Assert.NotEqual(firstVersion, secondVersion); await Store_WriteRead(grainTypeName, grainData.Item1, grainData.Item2); var thirdVersion = grainData.Item2.ETag; Assert.NotEqual(firstVersion, secondVersion); Assert.NotEqual(secondVersion, thirdVersion); } })); await Task.WhenAll(tasks); }