public static async Task Main(string[] args) { // Configuration section const int NumberOfProducers = 200; const int NumberOfConsumers = 10; const ProducerAppendBehavior Behavior = ProducerAppendBehavior.Batched; const bool UseSnapshotIsolation = true; var container = new SqlServerContainer(); try { Console.WriteLine("Creating sql server container ..."); await container.InitializeAsync().ConfigureAwait(false); Console.WriteLine("Created."); var db = await container.CreateDatabaseAsync(UseSnapshotIsolation).ConfigureAwait(false); Console.WriteLine("ConnectionString={0}", db.ConnectionString); using (var store = new MsSqlStreamStoreV3(new MsSqlStreamStoreV3Settings(db.ConnectionString) { Schema = "dbo" })) { Console.WriteLine("Creating sql stream store schema ..."); await store.CreateSchemaIfNotExists().ConfigureAwait(false); Console.WriteLine("Created."); using (var scheduler = new Scheduler(SystemClock.Instance)) { var producers = Enumerable .Range(1, NumberOfProducers) .Select(id => new Producer(id, Behavior, store, scheduler)) .ToArray(); var consumers = Enumerable .Range(1, NumberOfConsumers) .Select(id => new Consumer(id, store, scheduler)) .ToArray(); Console.WriteLine("Starting {0} producers ...", NumberOfProducers); Array.ForEach(producers, producer => producer.Start()); Console.WriteLine("Started."); Console.WriteLine("Starting {0} consumers ...", NumberOfConsumers); Array.ForEach(consumers, consumer => consumer.Start()); Console.WriteLine("Started."); Console.WriteLine("Press enter to exit"); Console.ReadLine(); Console.WriteLine("Stopping {0} producers ...", NumberOfProducers); Array.ForEach(producers, producer => producer.Stop()); Console.WriteLine("Stopped."); Console.WriteLine("Stopping {0} consumers ...", NumberOfConsumers); Array.ForEach(consumers, consumer => consumer.Stop()); Console.WriteLine("Stopped."); } } } finally { Console.WriteLine("Removing sql server container ..."); await container.DisposeAsync().ConfigureAwait(false); Console.WriteLine("Removed."); } }
public Producer(int id, ProducerAppendBehavior behavior, IStreamStore store, IScheduler scheduler) { Id = id; Behavior = behavior; Store = store ?? throw new ArgumentNullException(nameof(store)); Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); MessagePumpCancellation = new CancellationTokenSource(); Mailbox = new BufferBlock <object>(new DataflowBlockOptions { BoundedCapacity = int.MaxValue, MaxMessagesPerTask = 1, CancellationToken = MessagePumpCancellation.Token }); MessagePump = Task.Run(async() => { try { var text = new Lorem(); var random = new Random(); while (!MessagePumpCancellation.Token.IsCancellationRequested) { var message = await Mailbox.ReceiveAsync(MessagePumpCancellation.Token).ConfigureAwait(false); switch (message) { case AppendToStream append: var messages = Enumerable .Range(0, random.Next(1, 100)) // produce between 1 and 99 messages per append .Select(index => new NewStreamMessage(Guid.NewGuid(), append.Stream, text.Sentences(random.Next(5, 10)))) //randomize the data a bit .ToArray(); if (behavior == ProducerAppendBehavior.Batched) { try { var result = await Store .AppendToStream( append.Stream, append.ExpectedVersion, messages, MessagePumpCancellation.Token) .ConfigureAwait(false); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } catch (Exception exception) { if (!MessagePumpCancellation.IsCancellationRequested) { Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = append.ExpectedVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } } } else { AppendResult result = new AppendResult(append.ExpectedVersion, -1); try { foreach (var msg in messages) { result = await Store .AppendToStream( append.Stream, append.ExpectedVersion, messages, MessagePumpCancellation.Token) .ConfigureAwait(false); } await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } catch (Exception exception) { if (!MessagePumpCancellation.IsCancellationRequested) { Console.WriteLine("[{0}]AppendToStream failed because {1}", Id, exception); await scheduler .ScheduleTellOnceAsync( () => Mailbox.Post(new AppendToStream { Stream = append.Stream, ExpectedVersion = result.CurrentVersion }), TimeSpan.FromMilliseconds(random.Next(100, 5000)), // produce another append on the same stream in about 100 to 5000ms MessagePumpCancellation.Token) .ConfigureAwait(false); } } } break; } } } catch (OperationCanceledException) { } }, MessagePumpCancellation.Token); }