Running in embedded mode

Is it possible to run GetEventStore in embedded mode?

This would be great for more “light weight” apps… that need little to zero maintenance

Yes, sure. Our console app is just a small wrapper around core lib.

See here:

https://github.com/EventStore/EventStore/blob/dev/src/EventStore/EventStore.SingleNode/Program.cs

https://github.com/EventStore/EventStore/blob/dev/src/EventStore/EventStore.Core/ProgramBase.cs

Pay attention to how logging is initialized. You should have NLog.config added to your executable project with Copy To Output option enabled. Without NLog.config and log initialization done in ProgramBase you wouldn’t see any logging output from EventStore, which will make it a pain to investigate problems, should they appear.

We could probably create some ready to use wrapper for using EventStore as embedded storage.

Thanks. i will definitely try that.

But I also think that I would be beneficiary to the project if you provided a ready to use wrapper to run the event store in embedded mode. I think this scenario is more common than one might think. A lot of projects start small and then grow over time. Thus it would decrease the friction when starting a new project. The deployment would be much simpler.

Thanks

I agree. It would be much easier. Have you seen yves work on self hosting for unit testing? It’s probably a copy/paste job from there.

Do you have any references… was not able to find Yves work.

Thanks

https://github.com/yreynhout/AggregateSource/blob/master/AggregateSource.Tests/GEventStore/EmbeddedEventStore.cs

Cool, thanks

You can look also at https://github.com/EventStore/EventStore/blob/dev/src/EventStore/EventStore.Core.Tests/ClientAPI/Helpers/MiniNode.cs for inspiration, we use MiniNode inside our tests.

The main thing with embedded node is to wait for BecomeMaster message, meaning that SingleNode is ready to accept requests.

In case anyone was looking, MiniNode has moved to:

https://github.com/EventStore/EventStore/blob/dev/src/EventStore/EventStore.Core.Tests/Helpers/MiniNode.cs

I also think it would be useful to have an embedded wrapper DLL. I have a pet project for which that would be awesome.

I found Yves wrapper again too:

https://github.com/yreynhout/AggregateSource/blob/master/src/EventStore/AggregateSource.EventStore.IntegratedTests/Framework/EmbeddedEventStore.cs

To use Yves wrapper, I downloaded v3 of the Event Store and used those DLLs. Through trial and error, these are the references required to make the wrapper work:

EventStore.Common

EventStore.Core

EventStore.Core.Tests (for mini node)

EventStore.Transport.Http

EventStore.Transport.Tcp

NLog

Oh, and the client API dll.

Seriously, please don’t do this. Apart from anything else, MiniNode often interacts very badly with other things running on the same box, and can cause chained failures if not initialised and terminated properly etc.

If you’re running integration tests, you’re better off running against an in-memory server, and if you’re running unit tests you should test at whatever layer of abstraction you have above the Event Store.

I’ll try to put up an example of writing a test using an in-memory server with Process.Start later today.

Cheers,

James

Oh, I’m using it for unit tests, not as embedded. Good to know tho.

Sorry was quick on the draw. I’ll look for your example. I was just going to write some infrastructure tests.

Hi,

This is how I am doing it using nunit:

Create a SetUpFicture that has assembly-level SetUp and TearDown hooks:

[SetUpFixture]

public class AssemblySetup

{

public static int HttpPortNumber = 2120;

public static int TcpPortNumber = 1120;

public static string IpAddress = “127.0.0.1”;

private bool _connected;

private const string EventStorePath = @“C:\EventStore\EventStore.SingleNode.exe”;

private Process _process;

[SetUp]

public void RunBeforeAnyTests()

{

_process = Process.Start(EventStorePath, string.Format("–mem-db --tcp-port={0} --http-port={1} --run-projections=ALL", TcpPortNumber, HttpPortNumber));

var ipAddress = IPAddress.Parse(AssemblySetup.IpAddress);

var ipEndPoint = new IPEndPoint(ipAddress, AssemblySetup.TcpPortNumber);

var testConnection = EventStoreConnection.Create(ipEndPoint);

testConnection.Connected += EventStoreConnected;

testConnection.Connect();

//wait for the EventStore to initialize itself

byte attempt = 1;

while (!_connected)

{

if (attempt == 10) // give up after 10 seconds

break;

Thread.Sleep(1000);

attempt++;

}

testConnection.Close();

}

private void EventStoreConnected(object sender, ClientConnectionEventArgs e)

{

_connected = true;

}

[TearDown]

public void RunAfterAnyTests()

{

_process.Kill();

}

}

The SetUp will launch the process, then attempt to create a connection by a hacky sleep-until-connected loop (i.e. wait for ES to initialize itself). This should be “live” for all tests in the assembly.

I then have a base class that other tests should inherit from:

public class EventStoreTests

{

protected IEventStoreConnection InMemoryEventStore;

[TestFixtureSetUp]

public void FixtureSetup()

{

var ipAddress = IPAddress.Parse(AssemblySetup.IpAddress);

var ipEndPoint = new IPEndPoint(ipAddress, AssemblySetup.TcpPortNumber);

InMemoryEventStore = EventStoreConnection.Create(ipEndPoint);

InMemoryEventStore.Connect();

}

[TestFixtureTearDown]

public void FixtureTearDown()

{

InMemoryEventStore.Close();

}

}

Your tests should then have access to the InMemoryEventStore

This is generally working OK for me, although we have had some issues with trying to test projections when running on our build server (but not locally), but haven’t had chance to investigate yet

Tom

Hi Tom,

Why not use something like a ManualResetEvent instead of the polling? Also, you’ll discover that in the new version the synchronous overloads of the methods on IEventStoreConnection have gone, so you’ll need to switch this over to ConnectAsync - all it was
doing internally was .Wait(), so one potential method to do this is to just append .Wait() to the call.

Cheers,

James

Hi James,

I am using 3.0.0-rc2 version from the nuget feed. I actually wrote this class a few months ago so am struggling to remember why I even had all the polling stuff in there… I think it was an issue with trying to test code using the ProjectionsManager, but I’ve since given up on those tests.

Anyway, removing everything except the Process.Start call works fine, as well as using ConnectAsync().Wait() :slight_smile:

Thanks

Tom

Here’s where I ended up. This works with my continuous integration build on TFS as well. I do sometimes get a build warning for AppDomainUnloadedException, which is irritating. My tests reference InMemoryEventStore.Connection.

[TestClass]

public static class InMemoryEventStore

{

    private const string EventStorePath = @"C:\EventStore\EventStore.SingleNode.exe";

    private const string EventStoreArgsFmt = "--mem-db --tcp-port={0} --http-port={1} --run-projections=ALL";

    private const int EventStoreTcpPort = 2111; // 1113 was taken on build server by TfsJobAgent.exe

    private const int EventStoreHttpPort = 2113;

    private const string EventStoreIp = "127.0.0.1";

    private static Process _process;

    public static IEventStoreConnection Connection { get; private set; }

    [AssemblyInitialize]

    public static void SetupInMemoryEventStore(TestContext context)

    {

        _process = new Process

        {

            StartInfo =

            {

                // no window

                UseShellExecute = false,

                CreateNoWindow = true,

                FileName = EventStorePath,

                Arguments = string.Format(EventStoreArgsFmt, EventStoreTcpPort, EventStoreHttpPort)

            }

        };

        _process.Start();

        var endpoint = new IPEndPoint(IPAddress.Parse(EventStoreIp), EventStoreTcpPort);

        Connection = EventStoreConnection.Create(endpoint);

        Connection.ConnectAsync().Wait();

    }

    [AssemblyCleanup]

    public static void TeardownInMemoryEventStore()

    {

        Connection.Close();

        _process.Kill();

        _process.WaitForExit();

    }

}