Giter VIP home page Giter VIP logo

composable's People

Contributors

gitter-badger avatar mlidbom avatar nilshjalmarson avatar rnilsark avatar staticage avatar twzhangyang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

composable's Issues

Implement high performance Single-Instance-Mode

A highly performant mode where most reads from the database can be eliminated thus greatly increasing performance and lessening the risk of locking issues in the storage providers.

An endpoint, via configuration, promises that it is the only instance that will use the EventStore, DocumentDb and ServiceBus. This will be enforced by some sort of persistence layer data/locks so that no other instance can start. Given this guarantee we can serve any read that has a cache hit directly from the cache since we can know that no update has happened in the storage. Concurrency will be managed in the style of the TransactionWideLock class used by the InMemory storage provider.

This mean that we eliminate the storage round-trip, storage load and serialization. Given sufficient memory to keep most hot data in the cache this mode should offer at least an order of magnitude increased performance. Given one or more hot-spares we also get availability.

EndpointConfiguration.IsPureClientEndpoint should be autodetected at runtime

What this property really tracks is whether or not the endpoint has any remotely accessible message handlers. This should simply be detected by whether or not any exists. The lack of this functionality causes some odd issues when setting up endpoints with no local handlers. Not a standard use case, but until/unless we make it easy to use individual components without the concept of an endpoint we need to take care of this.

Remove IUnitOfWorkParticipant and related interfaces.

The event store session should be refactored so that events are saved to the database and published on the bus when they are raised rather than when the transaction completes.

The implementation of the Save method on the DocumentDbSession should be likewise changed to perform its change immediately.

These changes should ensure that all participants are already enlisted in the transaction when commit is called. That way we should not run into the problems that we currently do with failures to escalate the transaction to a distributed transaction and a normal implementation of IEnlistmentNotification should work.

Track delivery per handler

  • Each handler has an immutable GUID id and Messages are routed on a per handler basis.
    • Use bitflag field to track which handlers of an event/command have been invoked. (v)char to enable arbitrary number of handlers
    • A separate table maps bitflags per concrete message type to handler guid and is loaded on startup
    • We get delivery tracking per handler
    • but still only one row updated on commit meaning no loss of performance or scalability
    • Handlers can now be moved to have their own queue without having to be repopulated

Support for refactoring and enriching events and history in the event store

Currently hhanging data in the event in any way will corrupt the insertion order of the events since we rely on SQL server rowversion. We need to fix this and will take the opportunity to make the refactoring story for events much more palatable.

Goals:

  • [b ] Schema(
    • Enable renaming events on the DB level quickly and easily (separate table).
    • Enable enriching or refactoring of existing events (Implemented by replacing them so that no history is lost.)
    • Enable inserting new events at specific points in the stream
      • Achieving this means that we cannot rely on an identity column to track insertion order of the events. We thought that we were pretty much done with the schema until we realized this...
  • Framework support
    • Support for refactoring without immediately changing the store data
      • #4 Renaming events through Attributes retaining compatibility with the store.
      • Renaming Paths (Namespaces and/or parent classes) while maintaining compatibility with the store.
      • Interfaces that given the history of an aggregate are able to mutate the history of the aggregate.
        • Enrich events (Implemented by replacing them so that no history is lost.)
        • Refactor events (Implemented by replacing them so that no history is lost.)
        • Insert events
      • [LATER] Interfaces that given any event of a specific type can refactor/enrich them without knowing the context of the aggregate.
    • Pushing the changes into the store automatically and then ignoring the refactorings and
    • [LATER] warning that they should be removed now that the store is updated..

Enforce event immutability.

We achieve excellent performance in the event store by caching the event histories of an aggregate.
This relies on the assumption that events are never modified. Currently we have no way of guaranteeing that this is the case. We should at least do some basic validation of the design of event interfaces and classes and throw exceptions if they are mutable.

  • Event interface inspection
    • Forbid setters.
  • Event class inspection
    • Forbid public setters
    • Forbid public fields

Then the question is whether we should recursively walk into the types exposed by the properties.
If so what should be the rules for those types? [For now we do not recurse into the exposed property types.]

It can't register account at first when application start with an empty database for AccountManagement sample

I found a problem for AccountManagement sample, If I running it with empty database, when application start, it will only create InboxMessages, MessageDispatching, OutboxMessages. If I running register at once it will throw "Invalid object name 'ValueType'." which caused by bus.Execute(AccountApi.Queries.TryGetByEmail(email)) is Some. By if I login first with any account, it will create store and ValueType tables, then everything works.

Detect and warn about using an EventStoreSession from within multiple transactions.

Using an EventStoreSession instance within multiple transactions leaves you without the guarantee that access to an aggregate for write purposes will be serialized. If contention is high for an aggregate failures due to optimistic concurrency becomes quite likely.

If this occurs the cause of the problem is also quite hard to understand for the framework user since the exception you get is a primary key violation on an insert. This is not acceptable user experience. What we want the user to see in the log instead is something like this:

  • WARN: EventStoreSession used within a second transaction. This is likely to cause optimistic concurrency failures [Full stack trace]
  • Error: OptimisticConcurrencyException: You reused an EventStoreSession within multiple transactions and between these transactions another transaction modified the aggregate: [AggregateId] Therefore your changes cannot be saved since it would corrupt the aggregate history.
    [Full stack trace]

This enhancement is about adding the warning. I will add another enhancement for catching and wrapping the primary key violation in an OptimisticConcurrencyException.

Enable the use of AggregateRoot, AggregateRootEvent etc with other event stores.

In principle there is no reason why these classes should be usable only by Composable. They supply a lot of value that would be beneficial even if you use another event store implementation.

As part of this, investigate if it is possible and/or beneficial to remove the requirements that the events inherit AggregateRootEvent

MILESTONE: Hypermedia Service Bus (HyprBus)

Here we link together the work to be done for the milestone as a whole.

  • #37 Underlying low latency communication based on NetMQ
  • Traceability by always tracking:
    • CascadeId
    • TriggeredById
  • Persistence
    • Topology
      • Command Handler registrations
      • Event handler registrations
      • Endpoint registrations
    • Delivery
      • Outbox
      • Inbox
  • Exactly once guarantee
    • Run handlers in transaction
    • Automated retries
    • Future: Manual retries
  • Low latency querying
  • Ordering / Consistency guarantees
    • In order delivery of events and command (1)
    • Cascade tracking
      • Waiting for cascade completion before executing queries
        • Local endpoint
        • Future: Across endpoints
  1. Assuming no permanently failed messages and that you don't enable parallelism for commands/events

When persisting migrations renamed event information should also be persisted

  • After persisting migrations you should be able to remove all the migration code in the project.

Questions

  • Should we ensure that the event store does not loose the information about what the original name of the events were?
    • Consider if it is reasonable to add a new event type to the store when adding an EventRenamedAttribute so that you can see in the persisted data which events were added with the old event name and which with the new. If so we should somehow persist the information that the new name replaces the old so that no warnings are printed when loading event types and the replaced type cannot be mapped in memory.

If the first transaction that adds a new event type fails then all future inserts of that event type fails until process is restarted.

When we first raise an event of a type that has not been raised before:

  • The event type is inserted in the EventType table.
  • The event type is added to an in memory Dictionary
  • The actual event is inserted in the Event Table.

When more events of the new type are raised their Event Type Id is fetched from the Dictionary.
Now If for some reason the first transaction fails the database change is rolled back, but the Event Type Id will still exist in the Dictionary in memory.
From this point on all events raised of this type will cause an exception with the error:

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Event_EventType". The conflict occurred in database "SOME_DATABASE", table "dbo.EventType", column 'Id'.

Remove IHandleRemoteMessage

IHandleRemoteMessage should be removed. It is a fatally flawed routing mechanism. The idea was to get rid of duplicate handlers when the same handler was referenced in process (where the event was originally raised) and in a separate process (some other endpoint). Implementing this interface made sure that the handler was not triggered in memory so it was only executed once. But of course this fails again the second two or more endpoints references the assembly containing the handler so it is simply a broken idea.

The correct approach is to always implement bus message handlers in the assembly defining the endpoint where they should execute. Period.

Async support

The event store, document database, message handler interfaces should all have async versions of the appropriate methods

Add the ability to delete an event to the refactoring support.

Sometimes an event is essentially a poison event(Created through a bug). Breaking an aggregate root.
When that happens it should be possible to write a migrator that "deletes" those events.
It should also be simple to write a sql script that does the same.
Of course nothing should be removed from the actual storage. Rather something should be added :)

AggregateRoot should support recursive data structures (Composite Component pattern)

The support for nesting Components and Entities within an Aggregate is currently more limited than it should be. It locks the structure down to a specific depth of nesting where the exact type is known at each depth.
This is unacceptably limiting. We should support more dynamic nesting strategies.

  • Recursive data structure. (Composite Component)
  • Unknown subtype at each nesting level. (BranchType1, BranchType2, LeafType1)

Implement example of this Aggregate in the Samples project: Thread/Post Composite Aggregate where the Aggregate is a thread with a name and a root Post that is a recursive structure where each post contains a list of Answers that are also Posts.

Early Solution draft

  • RecursiveEntityCollectionManager generic class that adds the ability to handle recursive structures.
    • Type parameter TParentEntityIdGetter
    • Optional Type parameter TInstanceFactory that specifies a factory class to use for creating the correct subtype of the nested entity.

Questions

  • How do you handle the case where the root is a single object instead of a collection? Such as the Post case that would be used in the sample? Currently entity and nested entity require a collection to work. Would it be OK to simple keep this restriction and leave it up to the implementation of the Aggregate/Entity to ensure that no more root level entities are added?.

Review IHandleReplayedEvents and related abstractions

Is this really a good way to handle it?

  • It is easy to implement the wrong interface making the handler fail to be called when replaying or be called even though it should not be.
  • Maybe we should assert that no events can be raised while replaying events?
  • Maybe make the handlers more explicit?
    • IOnPublishedEventHandler
    • IOnReplayedEventHandler
    • IOnPublishedOrReplayedEventHandler (Or maybe the above interfaces have differently named methods and this interface does not exist. If you wish to handle both published and replayed events you will have to implement both methods.)
  • Document that message handlers should never be created except in the assembly that is the root for an endpoint process.

When using the RenamedEvent attribute it is not clear that it will not work unless you also change wiring.

When using the RenamedEvent attribute to rename an event it is not clear that it will not work unless you also change wiring. Worse: you will only notice that it does not work if you load an old aggregate that contains the event. This might not occur until in production. And when you get the exception the real cause (missing wiring) is anything but obvious.

Possible short term improvement:
Detect if any event class used by the event store has such an attribute, but not the wiring for it. If so, throw an exception. The exception should contain a message that clearly explains what the developer needs to change. If the event type is used at all in automated or manual testing the bug will be detected.

Optimal long term solution:
Find a way of implementing this without requiring manual wiring, and without causing significant startup slowdown by having to scan every type in every assembly.

Performance Suggestion

Since I can see you have been working on making the event sourcing in Composable perform, and I have a suggestion.

From what I can see, the domain events are created in the domain model/aggreagte constructor, every time a domain model is instantiated.

Example:

        public Foo()
        {
            RegisterEventAppliers()
                .For<IRenamedEvent>(Apply)
                .For<IFooNamePropertyUpdated>(e => this.Name = e.Name);
        }

2 Action<> objects, 2 event registrations and a closure for ´this´ are instantiated. In a situation where a lot of domain models are instantiated, we get a lot of unnecessary allocations and GC's, especially using a domain model with a lot of events, which they tend to get over time.

Instead, i suggest event registration in a static constructor, and using the domain model as the second argument to the event handler Action<TEvent, TDomainModel>.

Example (Domain model/aggregate constructor):

        static Foo()
        {
            RegisterEventAppliers()
                .For<IRenamedEvent>(Apply)
                .For<IFooNamePropertyUpdated>((theEvent, theDomainModel) => theDomainModel.Name = theEvent.Name);
        }

2 event registrations and 2 ´Action<,>´ objects are created, but only once. No closure is needed for the domain model, since it's a parameter.

What do you think?

Refactor Composable.CQRS dependencies to reduce "assembly spam"

We want a consumer project to get as much of the functionality from Composable.CQRS as possible with as few transitive dependencies pulled in as possible. If the functionality used only requires Composable to reference .Net Standard, then no other dependencies should be pulled in.

In practice this probably means that many/all of the base interfaces and in-memory implementations should move into Composable.Core. Alternatively all code that needs additional dependencies should be pulled out of Composable.CQRS.

Make sure to consider #19 (Migrate to .Net Standard) and #8 (Simple Service Bus) when making this change.

Remove usage of SqlDecimal in event store

The type SqlDecimal is SqlServer specific. Having that in the EventStore class makes implementing other storage layers cumbersome and does not feel like a clean design. It is also rather complicated to use. The same goes for the current structure of the table with computed column which is not supported by all relational databases.

We could consider using two 64 bit integer columns for event ordering instead. The first would get the value of the identity column when inserting normal events. The second would be used to order events with the same value in the first column.

As far as I can see this should work and make the code and table structure both simpler and more portable.

Better encapsulation of ConfigureContainerForTesting

Test code should not need to know about how to wire framework level classes for testing.:

Things like wiring these should all be encapsulated:

  • IMessageSpy
  • IServiceBus
  • DummyTimeSource

Also you should not need to know when and how to call the pair of current methods:
...CallBeforeAllOtherWiring, ....CallAfterAllOtherWiring

API should be something like like:

Container.ConfigureForTesting( container =>
    {
        //The actual container setup code.
    }
 )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.