Simplifying Message Driven Architectures with Brighter

Ian Cooper demoed Brighter at the .NET user group in London last night and I must say I'm impressed. In essence, it is a command processing framework that allows you to hand off processing commands (or discreet units of work) to in process and out of process (RabbitMQ, SQS etc) work queues.

With message driven architectures, tasks that could take a long time to process or are dependent on third party services (such as sending an email) are usually pushed off to an external message broker. Because sending a message is an atomic operation, the user gets instant feedback that what they've done has completed while the task (or command) is actually defered to a background process.

Message driven architectures are great in theory but as they say, the devil is in the detail. In this case, it's to do with processing these queues in parallel and how failures are handled.

In a sufficiently large system, messages will be rapidly queued from users of the system and conversely need to be rapidly de-queued by a background process. This means you need to have one or more worker processes with multiple threads, dequeuing messages and processing them one by one. Getting threading logic right is non trivial and luckily Brighter abstracts this away for us. That leads us onto the second point, error handling.

External dependencies can become unavailable for a number of reasons (e.g. temporary network issues, failures under high load or full service outages). If a failure is temporarily, one should ideally retry processing the message before giving up. But, if a service is experiencing issues because of load, our retry strategy could lead to an avalanche of requests and most probably take the service completely offline.

We therefore need logic that retries a couple of times in the event of transitory failures but backs off completely if a sufficient number of errors happen concurrently (e.g. 10). Care needs to be taken that this error count is monitored across threads.

To solve this, Brighter makes use of another excellent library, Polly. Polly allows you to specify these rules (specifically retry logic and circuit breakers) using an intuitive DSL. Brighter allows you to chain these policies as ordered attributes above the code to handle the command. Something along the lines of the following:

    [RequestLogging(step: 1, timing: HandlerTiming.Before)]
    [UsePolicy(CommandProcessor.CIRCUITBREAKER, step: 2)]
    [UsePolicy(CommandProcessor.RETRYPOLICY, step: 3)]
    public override MyCommand Handle(MyCommand command)
    {
        // Code to process command

        return base.Handle(command);
    }

I'm only just scratching the surface of what Brighter has to offer but suffice to say I wish this framework was around 5 years ago when I was working on solving these exact issues... just nowhere near as elegantly.

Show Comments