internal async Task <long> CommitToPersistence(StringBuilder dmlCollector, long?position) { if (position.HasValue == false) { throw new ArgumentException("Commit's must have a position", nameof(position)); } using (var readDb = SqlExecution.OpenWriteConnection(_commitConnectionstring)) { //Consider a write-lock around _dmlCollector, is it necessary? //Consider pre-pending a "BEGIN TRANSACTION" and have a all-or-nothing write dmlCollector.PrependLine("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; BEGIN TRANSACTION;"); dmlCollector.AppendLine( $"UPDATE Inf_ReadSubscriptions SET ReadPosition = {position.Value} WHERE SchemaName = '{_projection.SchemaIdentifier.Name}';"); dmlCollector.AppendLine("COMMIT TRANSACTION;"); var dml = new SqlCommand(dmlCollector.ToString(), readDb); var effect = await dml.ExecuteNonQueryAsync(); if (effect == 0) { throw new InvalidOperationException( "Something went wrong while updating the state of a readservice. SQL:\r\n" + dmlCollector); } //todo: Error handling? return(position.Value); } }
internal IEnumerable <SqlProjectionSubscription> WakeReadProjections(ISqlProjection[] projections) { // Subscriptions are scoped as: 1 instance per 'SchemaName' per database (connectionstring becomes the partition-key) using (var connection = SqlExecution.OpenWriteConnection(_connectionString)) { var existingSubscriptions = connection.Query( "SELECT SchemaName, SchemaRevision, ReadPosition FROM Inf_ReadSubscriptions") .Select(x => new { SchemaName = (string)x.SchemaName, SchemaRevision = (string)x.SchemaRevision, ReadPosition = (long?)x.ReadPosition }).ToDictionary(x => x.SchemaName); foreach (var projection in projections) { if (existingSubscriptions.TryGetValue(projection.SchemaIdentifier.Name, out var state) && projection.SchemaIdentifier.Revision.Equals(state.SchemaRevision)) { yield return(WakeReadProjection(projection, new SubscriptionState { AlreadyExists = true, ReadPosition = state.ReadPosition }, connection)); } else { //if not already existing (or existing in another revision), (re)register it yield return(WakeReadProjection(projection, new SubscriptionState { AlreadyExists = false, ReadPosition = null }, connection)); } } } }
private static void PrepareSubscriptionSchema(string sqlConnectionString) { using (SqlConnection connection = SqlExecution.OpenWriteConnection(sqlConnectionString)) SqlExecution.Run(_subscriberSchemaSetup, connection); }