public void SchemaCreationIsSerializedAndIdempotent() { using TestDatabase testDb = this.CreateTestDb(); IOrchestrationService service = this.CreateServiceWithTestDb(testDb); // Simulate 4 workers starting up concurrently and trying to initialize // the same database schema. It should just work with predictable output. Parallel.For(0, 4, i => { service.CreateIfNotExistsAsync().GetAwaiter().GetResult(); }); ValidateDatabaseSchema(testDb); // Operations are expected to be serialized, making the log output deterministic. LogAssert.Sequence( this.logProvider, // 1st LogAssert.AcquiredAppLock(statusCode: 0), LogAssert.SprocCompleted("dt._GetVersions"), LogAssert.ExecutedSqlScript("schema-0.2.0.sql"), LogAssert.ExecutedSqlScript("logic.sql"), LogAssert.ExecutedSqlScript("permissions.sql"), LogAssert.SprocCompleted("dt._UpdateVersion"), // 2nd LogAssert.AcquiredAppLock(statusCode: 1), LogAssert.SprocCompleted("dt._GetVersions"), // 3rd LogAssert.AcquiredAppLock(statusCode: 1), LogAssert.SprocCompleted("dt._GetVersions"), // 4th LogAssert.AcquiredAppLock(statusCode: 1), LogAssert.SprocCompleted("dt._GetVersions")); }
public async Task SingleTimer() { string input = $"Hello {DateTime.UtcNow:o}"; string orchestrationName = "OrchestrationWithTimer"; TimeSpan delay = TimeSpan.FromSeconds(3); // Performs a delay and then returns the input TestInstance <string> instance = await this.testService.RunOrchestration( input, orchestrationName, implementation : async(ctx, input) => { var result = await ctx.CreateTimer(ctx.CurrentUtcDateTime.Add(delay), input); return(result); }); TimeSpan timeout = delay + TimeSpan.FromSeconds(10); OrchestrationState state = await instance.WaitForCompletion( timeout, expectedOutput : input); // Verify that the delay actually happened Assert.True(state.CreatedTime.Add(delay) <= state.CompletedTime); // Validate logs LogAssert.NoWarningsOrErrors(this.testService.LogProvider); LogAssert.Sequence( this.testService.LogProvider, LogAssert.AcquiredAppLock(), LogAssert.CheckpointStarting(orchestrationName), LogAssert.CheckpointCompleted(orchestrationName), LogAssert.CheckpointStarting(orchestrationName), LogAssert.CheckpointCompleted(orchestrationName)); }
public async Task CanCreateIfNotExists(bool isDatabaseMissing) { using TestDatabase testDb = this.CreateTestDb(!isDatabaseMissing); IOrchestrationService service = this.CreateServiceWithTestDb(testDb); await service.CreateIfNotExistsAsync(); LogAssert.NoWarningsOrErrors(this.logProvider); LogAssert .For(this.logProvider) .Expect( LogAssert.CheckedDatabase()) .ExpectIf( isDatabaseMissing, LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]"), LogAssert.CreatedDatabase(testDb.Name)) .Expect( LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions"), LogAssert.ExecutedSqlScript("schema-0.2.0.sql"), LogAssert.ExecutedSqlScript("logic.sql"), LogAssert.ExecutedSqlScript("permissions.sql"), LogAssert.SprocCompleted("dt._UpdateVersion")) .EndOfLog(); ValidateDatabaseSchema(testDb); }
public async Task CanCreateAndDropSchema(bool isDatabaseMissing) { using TestDatabase testDb = this.CreateTestDb(!isDatabaseMissing); IOrchestrationService service = this.CreateServiceWithTestDb(testDb); // Create the DB schema for the first time await service.CreateAsync(recreateInstanceStore : true); LogAssert.NoWarningsOrErrors(this.logProvider); LogAssert .For(this.logProvider) .Expect( LogAssert.CheckedDatabase()) .ExpectIf( isDatabaseMissing, LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]"), LogAssert.CreatedDatabase(testDb.Name)) .Expect( LogAssert.AcquiredAppLock(), LogAssert.ExecutedSqlScript("drop-schema.sql"), LogAssert.ExecutedSqlScript("schema-0.2.0.sql"), LogAssert.ExecutedSqlScript("logic.sql"), LogAssert.ExecutedSqlScript("permissions.sql"), LogAssert.SprocCompleted("dt._UpdateVersion")) .EndOfLog(); ValidateDatabaseSchema(testDb); // Create the DB schema again - should be a no-op since it already exists this.logProvider.Clear(); await service.CreateIfNotExistsAsync(); ValidateDatabaseSchema(testDb); // The subsequent execution should run exactly one sproc and no scripts. // It's important to verify this to ensure the overhead of CreateIfNotExistsAsync is very small. LogAssert.NoWarningsOrErrors(this.logProvider); LogAssert.Sequence( this.logProvider, LogAssert.CheckedDatabase(), LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions")); // Delete the database and validate this.logProvider.Clear(); await service.DeleteAsync(); LogAssert.NoWarningsOrErrors(this.logProvider); LogAssert.Sequence( this.logProvider, LogAssert.AcquiredAppLock(), LogAssert.ExecutedSqlScript("drop-schema.sql")); // The previous schema validation ensures all objects are in the "dt" schema. // We know that all objects were successfully removed if the "dt" no longer exists. Assert.DoesNotContain("dt", testDb.GetSchemas()); }
public void SchemaCreationIsSerializedAndIdempotent(bool isDatabaseMissing) { using TestDatabase testDb = this.CreateTestDb(!isDatabaseMissing); IOrchestrationService service = this.CreateServiceWithTestDb(testDb); // Simulate 4 workers starting up concurrently and trying to initialize // the same database schema. It should just work with predictable output. Parallel.For(0, 4, i => { service.CreateIfNotExistsAsync().GetAwaiter().GetResult(); }); ValidateDatabaseSchema(testDb); // Operations are expected to be serialized, making the log output deterministic. LogAssert .For(this.logProvider) .Expect( // At least 1 worker will check the database first LogAssert.CheckedDatabase()) .Contains( // The other 3 workers will check in some non-deterministic order LogAssert.CheckedDatabase(), LogAssert.CheckedDatabase(), LogAssert.CheckedDatabase()) .ContainsIf( // One worker may obtain the lock after another worker created the database. isDatabaseMissing, LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]"), LogAssert.CreatedDatabase(testDb.Name)) .OptionallyContainsIf( // Anywhere from 0 to 3 of the workers may end up attempting to create the database. isDatabaseMissing, LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]"), LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]"), LogAssert.CommandCompleted($"CREATE DATABASE [{testDb.Name}]")) .Expect( // 1st LogAssert.AcquiredAppLock(statusCode: 0), LogAssert.SprocCompleted("dt._GetVersions"), LogAssert.ExecutedSqlScript("schema-0.2.0.sql"), LogAssert.ExecutedSqlScript("logic.sql"), LogAssert.ExecutedSqlScript("permissions.sql"), LogAssert.SprocCompleted("dt._UpdateVersion"), // 2nd LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions"), // 3rd LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions"), // 4th LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions")) .EndOfLog(); }
public async Task CanCreateIfNotExists() { using TestDatabase testDb = this.CreateTestDb(); IOrchestrationService service = this.CreateServiceWithTestDb(testDb); await service.CreateIfNotExistsAsync(); LogAssert.NoWarningsOrErrors(this.logProvider); LogAssert.Sequence( this.logProvider, LogAssert.AcquiredAppLock(), LogAssert.SprocCompleted("dt._GetVersions"), LogAssert.ExecutedSqlScript("schema-0.2.0.sql"), LogAssert.ExecutedSqlScript("logic.sql"), LogAssert.ExecutedSqlScript("permissions.sql"), LogAssert.SprocCompleted("dt._UpdateVersion")); ValidateDatabaseSchema(testDb); }
public async Task EmptyOrchestration() { string input = $"Hello {DateTime.UtcNow:o}"; string orchestrationName = "EmptyOrchestration"; // Does nothing except return the original input TestInstance <string> instance = await this.testService.RunOrchestration( input, orchestrationName, implementation : (ctx, input) => Task.FromResult(input)); await instance.WaitForCompletion( expectedOutput : input); // Validate logs LogAssert.NoWarningsOrErrors(this.testService.LogProvider); LogAssert.Sequence( this.testService.LogProvider, LogAssert.AcquiredAppLock(), LogAssert.CheckpointStarting(orchestrationName), LogAssert.CheckpointCompleted(orchestrationName)); }