Skip to content

robvdlv/JustGiving.EventStore.Http

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JG.EventStore.Http

A minimal EventStore .Net HTTP client, with some subscribing functionality too.

Nuget Nuget Join the chat at https://gitter.im/JustGiving/JustGiving.EventStore.Http

FAQs

Q: Are these questions frequently asked?
A: No, they have never been asked

Q: What platform does it run on?
A: This package makes extensive use of the C#5 async pattern, and is built against .Net 4.5. You may be able to run it against .Net 4 with the BCL compatibility packs, though we haven't tried.

Q: Why did you make this?
A: We wanted a simple, testable way to talk with our EventStore cluster over HTTP, as well as subscribe to events in a cacheable manner. We haven't found any other library, so we can give back to the ES community in our own little way

Q: Do you advocate HTTP over raw TCP?
A: Each to their own; check out the official line on it. We are using varnish which could be handy...

Q: Do you have any nuget packages?
A: Yes:
JustGiving.EventStore.Http.Client - The main client that actually talks to an EventStore instance
JustGiving.EventStore.Http.SubscriberHost - A library that will poll a stream, and invoke message handlers as events are found
JustGiving.EventStore.Http.SubscriberHost.Ninject - A support library for the SubscriberHost to find MessageHandlers for messages

Q: What is the minimum I need to do to get a client running?
A: Not much:

var connection = EventStoreHttpConnection.Create("http://localhost:2113");
await connection.AppendToStreamAsync("someStream", someEvent);

If you please, you can create a new ConnectionSettingsBuilder() to build a custom ConnectionSettings object.

Q: What exactly does the subscriber do?
A: At a high level, the subscriber will poll specified queues of an EventStore instance indefinitely, running event handlers the match the event type. The 'Event Type' is a string, found in the event's Summary field ('MessageType' in the web client). If you use the EventStore http or official client, this is taken care of automatically.
e.g. given an instance of the following event was stored

namespace SomeApp.Events
{
    public class SomethingHappened
    {
        public Guid Id { get; set; }
        public bool ItWasGood { get; set; 
    }
}

The following message handler would be fired

namespace SomeApp.Events
{
    public class DoSomethingUseful : IHandleEventsOf<SomethingHappened>
    {
        public Task Handle(SomethingHappened @event)
        {
            //Handle the event
        }
        
        public void OnError(Exception ex)
        {
            //Handle the exception
        }
    }
}

Alternatively, if you need some of the EventStore metadata as well as the event contents, you can create a handler deriving from IHandleEventsAndMetadataOf

namespace SomeApp.Events
{
    public class DoSomethingUsefulWithMetadata : IHandleEventsAndMetadataOf<SomethingHappened>
    {
        public Task Handle(SomethingHappened @event, BasicEventInfo metadata)
        {
            //Handle the event
        }
        
        public void OnError(Exception ex)
        {
            //Handle the exception
        }
    }
}

Q: What can I do if my events don't match my .Net Event Types?
A: The Subscriber (as of 12/Mar/2015) allows you to bind .Net classes to arbitrary events using one of two custom-binding attributes, show below. The first requires a reference to System.ComponentModel.DataAnnotations only, and allows your Type to be bound to one ES event. The second allows for multiple binding, but requires your DTO reference the SubscriberHost assembly.

[System.ComponentModel.DefaultEvent("MyDomain:PasswordUpdated-V3")]
public class UserChangedPassword
{
    public int UserId{ get; set; }
    public DateTime DateChanged{ get; set; }
}
[BindsTo("MyDomain:PasswordUpdated-V1")]
[BindsTo("MyDomain:PasswordUpdated-V2")]
public class UserChangedPassword
{
    public Guid UserId{ get; set; }
    public DateTime DateChanged{ get; set; }
}

Q: What do I need to do to get the subscriber running?
A: This is a little more tricky than the plain client because you will need to implement one or two interfaces:

IEventHandlerResolver - Get all EventHandlers for an event type (or use the Ninject one above)
IStreamPositionRepository - Save and load the last-read event for a given stream
Apart from that:

var subscriber = EventStreamSubscriber.Create(someConnection, someEventHanderResolver, someStreamPositionRepository);

Again, you may use a builder to customise the subscriber:

var builder = new EventStreamSubscriberSettingsBuilder(someConnection, someEventHanderResolver, someStreamPositionRepository);
var subscriber = EventStreamSubscriber.Create(builder);

Finally, subscribe to streams that you are interestesd in

subscriber.SubscribeTo("InterestingStreamName");

Q: Do you collect have any performance metrics?
A: By jingo, yes! Each IEventStreamSubscriber has two properties - AllEventsStats & ProcessedEventsStats. These collect counts of processed events and all events from your subscribed queues respectively. Each one yields a PerformanceStats object, which is a time-series enumerable of stream/message-count pairs.
The number and duration of snapshots may be configured per-subscriber when building it:

var builder = new EventStreamSubscriberSettingsBuilder(someConnection, someEventHanderResolver, someStreamPositionRepository);
                .WithMessageProcessingStatsWindowPeriodOf(someTimespan)
                .WithMessageProcessingStatsWindowCountOf(someInt);
.

By default, a maximum of 120 windows will be kept, each representing 30 seconds of activity.

Q: Can I have multiple handlers for a stream or message?
A: Yes; They will be fired in broadly in parallel, depending on you hardware and the number of handlers

Q: Can I subscribe different handlers to different positions in the stream? What does this even mean
A: Yes: Sometimes you may want want to create a handler that starts from the beginning of a stream whilst allowing other handlers to progress from where they left off (e.g. to migrate data to a new table whilst keeping the system online)

To support this, you need to create an event handler with a NonDefaultSubscriber attribute, supplying an alternate 'Subscriber Id'. Then kick off the stream in your subscriber host...

public class OriginalEventHandler : IHandleEventsOf<SomeEvent>
{...}

[NonDefaultSubscriber("BackfillingSomeEventV1")]
public class BackfillingEventHandler : IHandleEventsOf<SomeEvent>
{...}

//subscriber setup code...

_subscriber.SubscribeTo("SomeStream"); //Resume the original event processing to keep your current oltb db up to date
_subscriber.SubscribeTo("SomeStream", "BackfillingSomeEventV1"); //whilst kicking off a new subscription against the same stream, backfilling your new db

//subscriber setup code...

Q: Can I replay events, or process events without a subscription?
A: Yes. You will need to have initialised an EventStreamSubscriber (although you do not need any subscriptions), and then you can just call AdHocInvokeAsync, passing in the stream name, and event id. A third parameter of subscriberId is available, in case you have any handlers with a [NonDefaultSubscriber]
Once the invocation has completed, you will receive a status object, with the result of the execution, and any exceptions that may have been raised by handlers.

var result = await _subscriber.AdHocInvokeAsync("SomeStream", 123);

Q: Any known bugs?
A: None as of 22/May/2015, but the subscriber does not support competing consumers right now.

Otherwise, see https://github.com/JustGiving/JustGiving.EventStore.Http/issues - all issues welcome

Q: What is the DI/IoC story here?
A: Not ideal, as the interfaces were designed in homage to the GetEventStore TCP client. The builders are designed to be injectable directly, but the stream / subscribers will need a custom builder (hey, the stream endpoint is mandatory anyway...)

Q: Do you accept pull requests?
A: Maybe! This library was created to fulfil a small set of internal needs, and we have no real policy on them; it depends on the usefulness vs how breaky they are. Feel free to discuss with @jghackers

Q: If you could drink any cola, what would it be?
A: Faygo

Building

This API is built from the ground up on C#5, so you will need VS2013 to build, but no other prerequisites exist outside of some nuget packages We created a build.ps1 which builds and runs tests etc if you are into that kind of thing.

About

An ES .Net HTTP client, with some subscriber stuff too

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C# 75.6%
  • PowerShell 24.3%
  • Batchfile 0.1%