LogoLogo
  • README
  • VERSION 9
    • Version Begins
  • Overview
    • Show me the code!
    • Basic Concepts
    • Why Brighter?
  • Brighter Configuration
    • Basic Configuration
    • How Configuring the Command Processor Works
    • How Configuring a Dispatcher for an External Bus Works
    • RabbitMQ Configuration
    • AWS SNS Configuration
    • Kafka Configuration
    • Azure Service Bus Configuration
    • Azure Archive Provider Configuration
  • Darker Configuration
    • Basic Configuration
  • Brighter Request Handlers and Middleware Pipelines
    • Building an Async Pipeline of Request Handlers
    • Basic Configuration
    • How to Implement an Async Request Handler
    • Requests, Commands and an Events
    • Dispatching Requests
    • Dispatching An Async Request
    • Returning results from a Handler
    • Using an External Bus
    • Message Mappers
    • Routing
    • Building a Pipeline of Request Handlers
    • Passing information between Handlers in the Pipeline
    • Failure and Dead Letter Queues
    • Supporting Retry and Circuit Breaker
    • Failure and Fallback
    • Feature Switches
  • Guaranteed At Least Once
    • Outbox Support
    • Inbox Support
    • EFCore Outbox
    • Dapper Outbox
    • Dynamo Outbox
    • MSSQL Inbox
    • MySQL Inbox
    • Postgres Inbox
    • Sqlite Inbox
    • Dynamo Inbox
  • Darker Query Handlers and Middleware Pipelines
    • How to Implement a Query Handler
  • Health Checks and Observability
    • Logging
    • Monitoring
    • Health Checks
    • Telemetry
  • Command, Processors and Dispatchers
    • Command, Processor and Dispatcher Patterns
  • Under the Hood
    • How The Command Processor Works
    • How Service Activator Works
  • Event Driven Architectures
    • Microservices
    • Event Driven Collaboration
    • Event Carried State Transfer
    • Outbox Pattern
  • Task Queues
    • Using a Task Queue
  • FAQ
    • FAQ
  • END OF VERSION
    • Version Ends
  • VERSION 10
    • Version Begins
  • Overview
    • Show me the code!
    • Basic Concepts
    • Why Brighter?
  • Brighter Configuration
    • Basic Configuration
    • How Configuring the Command Processor Works
    • How Configuring a Dispatcher for an External Bus Works
    • RabbitMQ Configuration
    • AWS SNS Configuration
    • Kafka Configuration
    • Azure Service Bus Configuration
    • Azure Archive Provider Configuration
  • Darker Configuration
    • Basic Configuration
  • Brighter Request Handlers and Middleware Pipelines
    • Building an Async Pipeline of Request Handlers
    • Basic Configuration
    • How to Implement an Async Request Handler
    • Requests, Commands and an Events
    • Dispatching Requests
    • Dispatching An Async Request
    • Returning results from a Handler
    • Using an External Bus
    • Message Mappers
    • Routing
    • Building a Pipeline of Request Handlers
    • Passing information between Handlers in the Pipeline
    • Failure and Dead Letter Queues
    • Supporting Retry and Circuit Breaker
    • Failure and Fallback
    • Feature Switches
  • Guaranteed At Least Once
    • Outbox Support
    • Inbox Support
    • EFCore Outbox
    • Dapper Outbox
    • Dynamo Outbox
    • MSSQL Inbox
    • MySQL Inbox
    • Postgres Inbox
    • Sqlite Inbox
    • Dynamo Inbox
  • Darker Query Handlers and Middleware Pipelines
    • How to Implement a Query Handler
  • Health Checks and Observability
    • Logging
    • Monitoring
    • Health Checks
    • Telemetry
  • Command, Processors and Dispatchers
    • Command, Processor and Dispatcher Patterns
  • Under the Hood
    • How The Command Processor Works
    • How Service Activator Works
  • Event Driven Architectures
    • Microservices
    • Event Driven Collaboration
    • Event Carried State Transfer
    • Outbox Pattern
  • Task Queues
    • Using a Task Queue
  • FAQ
    • FAQ
  • END OF VERSION
    • Version Ends
Powered by GitBook
On this page
  • Guaranteed, At Least Once
  • Guaranteed, Once Only
  • Inbox
  • Adding an Inbox to a Handler
  • Inbox Configuration
  • Inbox Builder
  • Clearing the Inbox
  • Non-Transactional Inbox
  • Implementing an Inbox

Was this helpful?

Edit on GitHub
  1. Guaranteed At Least Once

Inbox Support

PreviousOutbox SupportNextEFCore Outbox

Last updated 1 year ago

Was this helpful?

Guaranteed, At Least Once

Messaging makes the guaranteed, at least once promise.

  • Guaranteed: A broker writes a copy of your message to disk, so that it is not lost. An writes the message to the application's database to ensure it is not lost.

  • At Least Once: In a distributed system you cannot guarantee that a writer will receive a response that it has successfully persisted a message, it must choose to retry the persistence if it does not receive an acknowledgement. This means that duplicates will occur, hence at least once.

Guaranteed, Once Only

There are two possible reactions to the at least once problem.

  • Idempotency: Ensure that when receiving a message, handling it multiple times will not have side-effects

  • De-duplication: Ensure that when receiving a message, you check whether you have handled it before, and discard if you have already processed it.

Events are often idempotent, whilst commands often require de-duplication.

Inbox

An inbox records messages that you have received and processed. Brighter provides inbox implementations built over a range of databases. If your preferred database is not included, see . Brighter also makes available an in-memory inbox which is intended for development only as it will not work across multiple consumers, or survive process restarts.

You can configure the usage of an inbox for de-duplication on a per-handler, or per command processor basis.

Adding an Inbox to a Handler

The inbox is middleware and forms a part of the internal bus pipeline. When added to the pipeline, it will run before your handler, and after subsequent handlers (see the ).

  • Before: Check to see if we have already seen this request

  • After: Add this request to those that we have already seen.

You also need to configure what action the inbox takes when it has seen a request:

  • OnceOnly: Does the inbox reject duplicates, or does it simply record requests. WARNING: Defaults to false - so it won't reject duplicates unless you set this parameter to true, just log requests that pass through this handler.

  • OnceOnlyAction: What does the inbox do, when a duplicate is encountered. Defaults to Throw.

    • Warn: Just log that a duplicate was received

    • Throw: Throws a OnceOnlyException

If you wish to terminate processing on a duplicate, you should set OnceOnly to true, which is what you need to terminate processing; the OnceOnlyAction will default to Throw

In the context of the Service Activator (listening to messages over middleware) throwing a OnceOnlyException will result in the message being acked (because it has already been processed).

The inbox is global to your application and uses the request id; you will want to distinguish requests in the inbox if you need to store the same request id for different pipelines. For example, if you deliver an event to multiple handlers, each handler has a request with the same request id.

Use the contextKey parameter to the attribute to disambiguate the request id. We recommend using the type of the handler, as the id usually needs to be unique via pipeline.

       [UseInboxAsync(step:0, contextKey: typeof(GreetingMadeHandlerAsync), onceOnly: true )]
        public override async Task<GreetingMade> HandleAsync(GreetingMade @event, CancellationToken cancellationToken = default(CancellationToken))
        {    
            Console.WriteLine($"Greeting Received: {@event.Greeting}");
            
            return await base.HandleAsync(@event, cancellationToken);
        }

Inbox Configuration

Inbox Builder

Brighter contains DDL to configure your Inbox. For each supported database we include an InboxBuilder. The Inbox Builder GetDDL which allows you to obtain the DDL statements required to create an Inbox. You can use this as part of your application start up to configure the Inbox if it does not already exist.

The following example shows creation of a MySql inbox.

We assume that INBOX_TABLE_NAME is a constant, shared with the code that configures your inbox.


private static void CreateInbox(IConfiguration config, IHostEnvironment env)
{
    try
    {
        var connectionString = config.GetConnectionString("Salutations")

        using var sqlConnection = new MySqlConnection(connectionString);
        sqlConnection.Open();

        using var existsQuery = sqlConnection.CreateCommand();
        existsQuery.CommandText = MySqlInboxBuilder.GetExistsQuery(INBOX_TABLE_NAME);
        bool exists = existsQuery.ExecuteScalar() != null;

        if (exists) return;

        using var command = sqlConnection.CreateCommand();
        command.CommandText = MySqlInboxBuilder.GetDDL(INBOX_TABLE_NAME);
        command.ExecuteScalar();
    }
    catch (System.Exception e)
    {
        Console.WriteLine($"Issue with creating Inbox table, {e.Message}");
        throw;
    }
}

Clearing the Inbox

As of V9, clearing the inbox is deferred to the implementer i.e. Brighter will not do this for you. Typically this involves creating a cron job, or agent, that clears inbox entries that are outside of the window during which they may be resent.

Later versions of Brighter may include data retention policy options that let you configure clearing an inbox.

Non-Transactional Inbox

As of V9 Brighter's inbox is not transactional, that is it does not participate in the transaction that may write to disk as a result of processing a message. This means that the inbox could fail if your changes to state as a result of processing a request are made, but the inbox is not updated.

Later versions of Brighter may address including the inbox within a transaction, as outbox does today.

Implementing an Inbox

You can refer to existing inbox implementations if you need to implement an inbox that Brighter does not support.

There are two versions of the attribute: sync and async. Ensure that you choose the correct version, which should .

Your inbox is configured as part of the Brighter extensions to ServiceCollection. See for more.

match your handler
Inbox Configuration
Outbox
Russian Doll Model
implementing an inbox