├── .gitattributes
├── .gitignore
├── AsyncOrigoSpike.Core
├── AkkaNet
│ ├── AkkaEngine.cs
│ ├── AkkaJournaler.cs
│ ├── CommandContext.cs
│ ├── Dispatcher.cs
│ ├── Executor.cs
│ └── JournalAcknowledgement.cs
├── AsyncOrigoSpike.Core.csproj
├── Common
│ ├── CommandBatch.cs
│ ├── EventStoreWriter.cs
│ ├── IEngine.cs
│ ├── IJournalWriter.cs
│ ├── Kernel.cs
│ ├── NullJournalWriter.cs
│ └── TransactionPrimitives.cs
├── DisruptorNet
│ ├── CommandDispatcher.cs
│ ├── DisruptorEngine.cs
│ ├── Journaler.cs
│ ├── Request.cs
│ └── SerializedTransactionHandler.cs
├── Properties
│ └── AssemblyInfo.cs
├── TplDataflow
│ ├── CommandRequest.cs
│ ├── ExecutionPipeline.cs
│ ├── QueryRequest.cs
│ ├── TplBatchingJournaler.cs
│ └── TplDataflowEngine.cs
├── TplNet
│ ├── TplNetEngine.cs
│ ├── TplNetExecutor.cs
│ └── TplNetJournaler.cs
└── packages.config
├── AsyncOrigoSpike.Test
├── AsyncOrigoSpike.Test.csproj
├── Properties
│ └── AssemblyInfo.cs
├── TestModel
│ ├── AddItemCommand.cs
│ ├── GetSumQuery.cs
│ ├── IntModel.cs
│ └── SumCommand.cs
├── TheTests.cs
└── packages.config
├── AsyncOrigoSpike.sln
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | [Bb]in/
16 | [Oo]bj/
17 |
18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
19 | !packages/*/build/
20 |
21 | # MSTest test Results
22 | [Tt]est[Rr]esult*/
23 | [Bb]uild[Ll]og.*
24 |
25 | *_i.c
26 | *_p.c
27 | *.ilk
28 | *.meta
29 | *.obj
30 | *.pch
31 | *.pdb
32 | *.pgc
33 | *.pgd
34 | *.rsp
35 | *.sbr
36 | *.tlb
37 | *.tli
38 | *.tlh
39 | *.tmp
40 | *.tmp_proj
41 | *.log
42 | *.vspscc
43 | *.vssscc
44 | .builds
45 | *.pidb
46 | *.log
47 | *.scc
48 |
49 | # Visual C++ cache files
50 | ipch/
51 | *.aps
52 | *.ncb
53 | *.opensdf
54 | *.sdf
55 | *.cachefile
56 |
57 | # Visual Studio profiler
58 | *.psess
59 | *.vsp
60 | *.vspx
61 |
62 | # Guidance Automation Toolkit
63 | *.gpState
64 |
65 | # ReSharper is a .NET coding add-in
66 | _ReSharper*/
67 | *.[Rr]e[Ss]harper
68 |
69 | # TeamCity is a build add-in
70 | _TeamCity*
71 |
72 | # DotCover is a Code Coverage Tool
73 | *.dotCover
74 |
75 | # NCrunch
76 | *.ncrunch*
77 | .*crunch*.local.xml
78 |
79 | # Installshield output folder
80 | [Ee]xpress/
81 |
82 | # DocProject is a documentation generator add-in
83 | DocProject/buildhelp/
84 | DocProject/Help/*.HxT
85 | DocProject/Help/*.HxC
86 | DocProject/Help/*.hhc
87 | DocProject/Help/*.hhk
88 | DocProject/Help/*.hhp
89 | DocProject/Help/Html2
90 | DocProject/Help/html
91 |
92 | # Click-Once directory
93 | publish/
94 |
95 | # Publish Web Output
96 | *.Publish.xml
97 |
98 | # NuGet Packages Directory
99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
100 | packages/
101 |
102 | # Windows Azure Build Output
103 | csx
104 | *.build.csdef
105 |
106 | # Windows Store app package directory
107 | AppPackages/
108 |
109 | # Others
110 | sql/
111 | *.Cache
112 | ClientBin/
113 | [Ss]tyle[Cc]op.*
114 | ~$*
115 | *~
116 | *.dbmdl
117 | *.[Pp]ublish.xml
118 | *.pfx
119 | *.publishsettings
120 |
121 | # RIA/Silverlight projects
122 | Generated_Code/
123 |
124 | # Backup & report files from converting an old project file to a newer
125 | # Visual Studio version. Backup files are not needed, because we have git ;-)
126 | _UpgradeReport_Files/
127 | Backup*/
128 | UpgradeLog*.XML
129 | UpgradeLog*.htm
130 |
131 | # SQL Server files
132 | App_Data/*.mdf
133 | App_Data/*.ldf
134 |
135 |
136 | #LightSwitch generated files
137 | GeneratedArtifacts/
138 | _Pvt_Extensions/
139 | ModelManifest.xml
140 |
141 | # =========================
142 | # Windows detritus
143 | # =========================
144 |
145 | # Windows image file caches
146 | Thumbs.db
147 | ehthumbs.db
148 |
149 | # Folder config file
150 | Desktop.ini
151 |
152 | # Recycle Bin used on file shares
153 | $RECYCLE.BIN/
154 |
155 | # Mac desktop service store files
156 | .DS_Store
157 |
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/AkkaEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Akka.Actor;
4 |
5 | namespace AsyncOrigoSpike
6 | {
7 | ///
8 | /// Prevalence engine
9 | ///
10 | public class AkkaEngine : IEngine
11 | {
12 | readonly ActorSystem _actorSystem;
13 | readonly ActorRef _dispatcher;
14 |
15 | public AkkaEngine(M model, int batchSize, IJournalWriter journalWriter)
16 | {
17 | // the kernel is an origodb component which
18 | // synchronizes reads and writes to the model
19 | // will be shared by
20 | var kernel = new Kernel(model);
21 |
22 |
23 | //var journalWriter = new NullJournalWriter();
24 | //build the chain of actors backwards
25 | _actorSystem = ActorSystem.Create("prevayler");
26 |
27 | //executor executes commands
28 | //it could also handle queries but would allow either a single query or command at time.
29 | //better to add a group of actors that can execute queries concurrently
30 | var executor = _actorSystem.ActorOf(Props.Create(() => new Executor(kernel)));
31 |
32 | //journaler writes commands to the journal in batches or at specific intervals
33 | //before passing to the executor
34 | var journaler = _actorSystem.ActorOf(Props.Create(() => new AkkaJournaler(executor, batchSize, journalWriter)));
35 |
36 | //dispatcher prepares initial message and passes to journaler
37 | _dispatcher = _actorSystem.ActorOf(Props.Create(() => new Dispatcher(journaler, executor)));
38 | }
39 |
40 | public Task ExecuteAsync(Command command)
41 | {
42 | return _dispatcher.Ask(command);
43 | }
44 |
45 | public Task ExecuteAsync(Command command)
46 | {
47 | return _dispatcher.Ask(command);
48 | }
49 |
50 | public R Execute(Command command)
51 | {
52 | return ExecuteAsync(command).Result;
53 | }
54 |
55 | public void Execute(Command command)
56 | {
57 | ExecuteAsync(command).Wait();
58 | }
59 |
60 | public Task ExecuteAsync(Query query)
61 | {
62 | return _dispatcher.Ask(query);
63 | }
64 |
65 | public R Execute(Query query)
66 | {
67 | return ExecuteAsync(query).Result;
68 | }
69 |
70 | public void Dispose()
71 | {
72 | _actorSystem.Shutdown();
73 | _actorSystem.WaitForShutdown();
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/AkkaJournaler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Akka.Actor;
5 |
6 | namespace AsyncOrigoSpike
7 | {
8 | ///
9 | /// Append multiple commands accumulated during a specific time period or up
10 | /// to a specific limit.
11 | ///
12 | public class AkkaJournaler : ReceiveActor
13 | {
14 |
15 | readonly private IJournalWriter _journalWriter;
16 |
17 | //number of commands at a time to journal
18 | public int BatchSize = 100;
19 |
20 | //or after a specific time elapsed, whichever comes first
21 | public TimeSpan Interval = TimeSpan.FromMilliseconds(10);
22 |
23 | //buffered commands waiting to be written to the journal
24 | private readonly List _commandBuffer = new List(200000);
25 |
26 | private readonly Queue _waitingForJournalAck = new Queue(200000);
27 |
28 | //pass on the journaled commands to this actor
29 | readonly ActorRef _executor;
30 |
31 |
32 | public AkkaJournaler(ActorRef executor, int batchSize, IJournalWriter journalWriter)
33 | {
34 | _journalWriter = journalWriter;
35 | BatchSize = batchSize;
36 | _executor = executor;
37 | Receive(t => Accept(t));
38 | Receive(_ => Go());
39 | Receive(_ => _executor.Tell(_waitingForJournalAck.Dequeue()));
40 |
41 | SetReceiveTimeout(Interval);
42 | }
43 |
44 | private void Go()
45 | {
46 | if (_commandBuffer.Count > 0)
47 | {
48 | var self = Self;
49 | var batch = _commandBuffer.ToArray();
50 | var task = _journalWriter.AppendAsync(batch.Select(ctx => (Command)ctx.Transaction));
51 | _commandBuffer.Clear();
52 | _waitingForJournalAck.Enqueue(batch);
53 | task.ContinueWith(t => self.Tell(JournalAcknowledgement.Instance));
54 | }
55 | }
56 |
57 | private void Accept(RequestContext command)
58 | {
59 | _commandBuffer.Add(command);
60 | if (_commandBuffer.Count == BatchSize) Go();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/CommandContext.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 |
3 | namespace AsyncOrigoSpike
4 | {
5 | public sealed class RequestContext
6 | {
7 | public readonly object Transaction;
8 | public readonly ActorRef Initiator;
9 |
10 | public RequestContext(object transaction, ActorRef actorRef)
11 | {
12 | Transaction = transaction;
13 | Initiator = actorRef;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/Dispatcher.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 |
3 | namespace AsyncOrigoSpike
4 | {
5 | public class Dispatcher : ReceiveActor
6 | {
7 | readonly ActorRef _journalWriter;
8 | readonly ActorRef _executor;
9 |
10 | public Dispatcher(ActorRef journalWriter, ActorRef executor)
11 | {
12 | _executor = executor;
13 | _journalWriter = journalWriter;
14 | Receive(command => _journalWriter.Tell(new RequestContext(command, Sender)));
15 | Receive(query => _executor.Tell(new RequestContext(query, Sender)));
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/Executor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Akka.Actor;
3 |
4 | namespace AsyncOrigoSpike
5 | {
6 | public class Executor : ReceiveActor
7 | {
8 | readonly Kernel _kernel;
9 | public Executor(Kernel kernel)
10 | {
11 | _kernel = kernel;
12 | Receive(commands => ExecuteCommands(commands));
13 | Receive(ctx => ExecuteQuery(ctx));
14 | }
15 |
16 | private void ExecuteQuery(RequestContext queryContext)
17 | {
18 | var result = _kernel.Execute((Query) queryContext.Transaction);
19 | queryContext.Initiator.Tell(result, Context.Parent);
20 | }
21 |
22 | private void ExecuteCommands(IEnumerable commandContexts)
23 | {
24 | foreach (var context in commandContexts)
25 | {
26 | var result = _kernel.Execute((Command)context.Transaction);
27 |
28 | //send a return message to the external caller
29 | // will correlate with the call to Ask<>() in AkkaEngine.ExecuteAsync()
30 | context.Initiator.Tell(result, Context.Parent);
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AkkaNet/JournalAcknowledgement.cs:
--------------------------------------------------------------------------------
1 | namespace AsyncOrigoSpike
2 | {
3 | sealed class JournalAcknowledgement
4 | {
5 | public static JournalAcknowledgement Instance = new JournalAcknowledgement();
6 | private JournalAcknowledgement()
7 | {
8 |
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/AsyncOrigoSpike.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {E0C71B98-17AF-4E95-ABC3-E8D31C631696}
8 | Library
9 | Properties
10 | AsyncOrigoSpike
11 | AsyncOrigoSpike.Core
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\Akka.0.6.3\lib\net45\Akka.dll
35 |
36 |
37 | ..\packages\Disruptor.Net.2.10.0\lib\Disruptor.dll
38 |
39 |
40 | ..\packages\EventStore.Client.3.0.0\lib\net40\EventStore.ClientAPI.dll
41 |
42 |
43 | ..\packages\Metrics.NET.0.2.8\lib\net45\Metrics.dll
44 |
45 |
46 | False
47 | ..\packages\Newtonsoft.Json.6.0.1\lib\net45\Newtonsoft.Json.dll
48 |
49 |
50 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
51 |
52 |
53 |
54 |
55 | False
56 | ..\packages\Microsoft.Tpl.Dataflow.4.5.23\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
105 |
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/CommandBatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace AsyncOrigoSpike
6 | {
7 |
8 | [Serializable]
9 | public class CommandBatch
10 | {
11 | public readonly Command[] Commands;
12 |
13 | public CommandBatch(IEnumerable commands)
14 | {
15 | Commands = commands.ToArray();
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/EventStoreWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Net;
5 | using System.Runtime.Serialization;
6 | using System.Runtime.Serialization.Formatters.Binary;
7 | using System.Threading.Tasks;
8 | using EventStore.ClientAPI;
9 |
10 | namespace AsyncOrigoSpike
11 | {
12 | ///
13 | /// IJournalWriter implementation that writes to an EventStore 3 instance
14 | ///
15 | public class EventStoreWriter : IJournalWriter
16 | {
17 | private readonly IEventStoreConnection _eventStore;
18 | private readonly IFormatter _formatter;
19 |
20 | public EventStoreWriter(IEventStoreConnection connection, IFormatter formatter = null)
21 | {
22 | _formatter = formatter ?? new BinaryFormatter();
23 | _eventStore = connection;
24 | }
25 |
26 | public Task AppendAsync(IEnumerable commands)
27 | {
28 | return _eventStore.AppendToStreamAsync("origo", ExpectedVersion.Any, ToEventData(commands));
29 | }
30 |
31 | private EventData ToEventData(IEnumerable commands)
32 | {
33 | var id = Guid.NewGuid();
34 | var stream = new MemoryStream();
35 | _formatter.Serialize(stream, new CommandBatch(commands));
36 | return new EventData(id, "origo-batch", false, stream.ToArray(), null);
37 | }
38 |
39 | public void Dispose()
40 | {
41 | _eventStore.Close();
42 | }
43 |
44 | ///
45 | /// create an instance with an open connection to an event store instance
46 | ///
47 | ///
48 | public static IJournalWriter Create()
49 | {
50 | var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1113);
51 | var connection = EventStoreConnection.Create(endPoint);
52 | connection.ConnectAsync().Wait();
53 | return new EventStoreWriter(connection);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/IEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace AsyncOrigoSpike
5 | {
6 | ///
7 | /// Core transaction handling capabilities of the OrigoDB engine
8 | ///
9 | ///
10 | public interface IEngine : IDisposable
11 | {
12 | Task ExecuteAsync(Command command);
13 | Task ExecuteAsync(Command command);
14 | Task ExecuteAsync(Query query);
15 |
16 | R Execute(Command command);
17 | void Execute(Command command);
18 | R Execute(Query query);
19 | }
20 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/IJournalWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace AsyncOrigoSpike
6 | {
7 | public interface IJournalWriter : IDisposable
8 | {
9 | Task AppendAsync(IEnumerable commands);
10 | }
11 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/Kernel.cs:
--------------------------------------------------------------------------------
1 | namespace AsyncOrigoSpike
2 | {
3 |
4 | ///
5 | /// Encapsulates the in-memory object graph,
6 | /// executes commands and queries
7 | ///
8 | public class Kernel
9 | {
10 | readonly object _model;
11 |
12 | public Kernel(object model)
13 | {
14 | _model = model;
15 | }
16 |
17 | public object Execute(Command command)
18 | {
19 | return command.ExecuteImpl(_model);
20 | }
21 |
22 | public object Execute(Query query)
23 | {
24 | return query.ExecuteImpl(_model);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/NullJournalWriter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace AsyncOrigoSpike
5 | {
6 |
7 | ///
8 | /// For baseline comparison of internal performance
9 | ///
10 | public class NullJournalWriter : IJournalWriter
11 | {
12 | public Task AppendAsync(IEnumerable commands)
13 | {
14 | return Task.FromResult(0);
15 | }
16 |
17 | public void Dispose()
18 | {
19 |
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/Common/TransactionPrimitives.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 |
4 | namespace AsyncOrigoSpike
5 | {
6 | [Serializable]
7 | public abstract class Command : Command
8 | {
9 | public abstract R Execute(M model);
10 |
11 | public override object ExecuteImpl(object model)
12 | {
13 | return Execute((M) model);
14 | }
15 | }
16 |
17 | [Serializable]
18 | public abstract class Command : Command
19 | {
20 | public abstract void Execute(M model);
21 | public override object ExecuteImpl(object model)
22 | {
23 | Execute((M) model);
24 | return null;
25 | }
26 | }
27 |
28 |
29 | [Serializable]
30 | public abstract class Query : Query
31 | {
32 | public abstract R Execute(M model);
33 | public override object ExecuteImpl(object model)
34 | {
35 | return Execute((M)model);
36 | }
37 | }
38 |
39 | [Serializable]
40 | public abstract class Query
41 | {
42 | public abstract object ExecuteImpl(object model);
43 | }
44 |
45 | [Serializable]
46 | public abstract class Command
47 | {
48 | public abstract object ExecuteImpl(object model);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/DisruptorNet/CommandDispatcher.cs:
--------------------------------------------------------------------------------
1 | using Disruptor;
2 | using Disruptor.Dsl;
3 |
4 | namespace AsyncOrigoSpike
5 | {
6 |
7 | ///
8 | /// pushes journaled commands to the execution ring
9 | ///
10 | public class CommandDispatcher : IEventHandler
11 | {
12 |
13 | private readonly Disruptor _executionBuffer;
14 |
15 | public CommandDispatcher(Disruptor executionBuffer)
16 | {
17 | _executionBuffer = executionBuffer;
18 | }
19 |
20 | public void OnNext(Request data, long sequence, bool endOfBatch)
21 | {
22 | _executionBuffer.PublishEvent((e,i) =>
23 | {
24 | e.Transaction = data.Transaction;
25 | e.Response = data.Response;
26 | return e;
27 | });
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/AsyncOrigoSpike.Core/DisruptorNet/DisruptorEngine.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Disruptor;
3 | using Disruptor.Dsl;
4 |
5 | namespace AsyncOrigoSpike
6 | {
7 | ///
8 | /// Engine implementation using disruptor.net
9 | /// Uses 2 ringbuffers, one for journaling/preprocessing and
10 | /// one for command/query execution
11 | ///
12 | public class DisruptorEngine : IEngine
13 | {
14 | //batches and writes to the journal
15 | readonly Disruptor _commandJournaler;
16 |
17 | //executes commands and queries
18 | readonly Disruptor _transactionHandler;
19 |
20 | readonly IJournalWriter _journalWriter;
21 |
22 | //wire up the disruptor ring buffers and handlers
23 | public DisruptorEngine(M model, int batchSize, IJournalWriter journalWriter)
24 | {
25 | _journalWriter = journalWriter;
26 | var kernel = new Kernel(model);
27 |
28 | _transactionHandler = new Disruptor(
29 | () => new Request(),
30 | new MultiThreadedClaimStrategy(1024 * 64),
31 | new YieldingWaitStrategy(),
32 | TaskScheduler.Default);
33 |
34 | _commandJournaler = new Disruptor(
35 | () => new Request(),
36 | new MultiThreadedClaimStrategy(1024 * 64),
37 | new YieldingWaitStrategy(),
38 | TaskScheduler.Default);
39 |
40 | _transactionHandler.HandleEventsWith(new SerializedTransactionHandler(kernel));
41 |
42 | _commandJournaler.HandleEventsWith(new Journaler(_journalWriter, batchSize))
43 | .Then(new CommandDispatcher(_transactionHandler));
44 |
45 | _transactionHandler.Start();
46 | _commandJournaler.Start();
47 |
48 | }
49 |
50 | public async Task ExecuteAsync(Command command)
51 | {
52 | var completion = new TaskCompletionSource