In-memory queues
Queues in Nexus are backed by a database but in some cases you want to squeeze the last bit of performance out of your queues and then the overhead of a database can become too much for certain use cases, or if you need to relieve your database of the work needed to handle some very busy queues.
For those scenarios Nexus lets you have in-memory queues without the overhead of reading and writing to a database first. Note however that this only works if you only deploy Nexus to a single server. See the section below about single-server use. You can however allow other servers to enqueue but only if you use IApiEnqueuer to enqueue through the API to the single server hosting the API and the memory queue(s).
Creating a memory queue
You create a memory queue just like any other queue by creating a class that implements IQueueMessage or IQueueMessageWithId:
[Queue("example_memory", StorageMode = QueueStorageMode.InMemory)]
public class ExampleMemoryQueueMessage : IQueueMessage
{
// Properties here
}
When you set StorageMode = QueueStorageMode.InMemory in the attribute it gets registered as a memory queue. Note that you can switch back and forth between having a queue as an in-memory queue or not because an in-memory queue will use its database queue as fallback storage to ensure that messages aren't lost because of deploy or application restarts.
Messages and message changes are scheduled to be persisted to the database as soon as possible, but it's done on a background thread. The in-memory queue registers a listener for application shutdown to prevent the application from shutting down before all changes are persisted.
Durability
Nexus persists in-memory changes to fallback storage in the background so persisted changes survive restarts. An abrupt process failure can lose changes that have not been flushed.
Fallback persistence is asynchronous and is not a durable commit before EnqueueAsync() returns. Nexus flushes pending changes during graceful shutdown, but abrupt termination, persistence failures, or an insufficient shutdown timeout can lose unflushed data. Use database queues when the producer needs a durable database write before returning.
Fallback storage
The in-memory queue will fallback to it's own regular database table by default which makes the transition from a non-memory queue to an in-memory queue smooth. But if you want to use an in-memory queue to lessen the load on your database it doesn't have the intended effect. In that case you can explicitly use the Sqlite driver and use Sqlite tables as fallback storage by installing the CommerceMind.Nexus.Sqlite NuGet package and adding this in your Program.cs:
builder.Services
.AddNexus()
.AddSqliteMemoryQueueFallbackStorage();
If using disk as fallback storage isn't an option for you it's possible to create your own implementation of the interface IMemoryQueueFallbackStorage and register it in the service collection.
Single server use only
In-memory queues can generally only be used if you only have a single server/instance running Nexus. Even if changes to an in-memory queue is persisted to storage Nexus won't read from the storage except during initialization.
You can use in-memory queues in a multi-instance application if the queues are local to that instance and messages in the same queue have no effect on messages in other instances in terms of sort order or processing side effects. In this case however you must implement your own IMemoryQueueFallbackStorage as the built in implementation uses a memory counter to increment assign queue item ids.
If you're using a hosting service such as Azure App Service you probably can't use in-memory queues unless you make sure to stop the application before starting a deploy as the default in Azure App Service is to bring up a parallel deployment slot and swap with the live slot. Which means that during deploy you will have multiple instances running. If this is an issue for your application will depend on how you use the queues.
Application shutdown
In-memory queues registers a callback on the applications stopping token (CancellationToken) and performs a synchronous flush of all pending changes to the fallback storage. Nexus will also attempt to stop all jobs when shutdown starts and won't start any jobs after that.
But it's up to you not to enqueue to memory queues after shutdown has started. If you do you will risk enqueueing a message that doesn't have time to be persisted before the process exits.
In-memory queues and transactions
In-memory queues does not offer a way to integrate with the transaction support in DbConnectionScope. Outside the Entity Framework integration, in-memory changes are visible immediately to the whole application, and changes are sent to the fallback storage explicitly without transaction support to not accidentally enlist those queries in unrelated transactions.
With the Entity Framework integration, enqueue operations wait for the scoped context's asynchronous commit. Committed messages are added to memory before readiness is requested; rolled-back or unsaved operations are discarded. This does not make fallback persistence synchronous. Other in-memory updates still do not roll back with a database transaction.
For explicit transactions outside the EF integration, publish to memory only after the transaction commits, or use a database outbox queue if the handoff must be durable.
Processing and activation
In-memory queues support readiness-driven or scheduled queue jobs, including the same leased-message replacement rules as database queues. They do not support distributed queue processors. In-memory queue storage and in-memory activation storage are separate choices; selecting one does not select the other.