Skip to content

Provider Behavioral Gaps ​

This document catalogs known behavioral differences across Foundatio provider implementations. While all providers implement the same interfaces, underlying infrastructure limitations mean some operations behave differently than the interface contract might suggest. Use this as a compatibility reference when choosing or switching providers.

Summary ​

InterfaceInMemoryRedisAzureAWSRabbitMQKafkaAliyunSshNet
IFileStorageFullFullFullPartial——FullFull
IQueue<T>FullPartialPartialPartial————
IMessageBusFullFullFullPartialFullFull——
ICacheClientFullFull——————
ILockProviderFullFull——————

Legend: Full = all interface behaviors work as documented. Partial = some operations diverge (see below). — = not implemented by this provider.


IFileStorage ​

DeleteFileAsync for Non-Existent Files ​

ProviderBehaviorNotes
InMemoryReturns false✅ Expected
RedisReturns false✅ Expected
Azure BlobReturns false✅ Expected
S3Returns true⚠️ S3 DELETE is idempotent by design
AliyunReturns false✅ Expected
SshNetReturns false✅ Expected

Impact: Code that uses the return value to determine whether a file existed before deletion will get incorrect results on S3.

CopyFileAsync / RenameFileAsync with Non-Existent Source ​

ProviderBehaviorNotes
InMemoryReturns false✅ Expected
RedisReturns false✅ Expected
Azure BlobReturns false✅ Expected
S3Throws AmazonS3Exception⚠️ Exception instead of false
AliyunReturns false✅ Expected
SshNetReturns false✅ Expected

Impact: Callers must wrap S3 copy/rename in try-catch if the source file might not exist.

GetFileStreamAsync with Non-Existent File (ReadMode) ​

ProviderBehaviorNotes
InMemoryReturns null✅ Expected
RedisReturns null✅ Expected
Azure BlobReturns null✅ Expected
S3Throws AmazonS3Exception⚠️ Exception instead of null
AliyunReturns null✅ Expected
SshNetReturns null✅ Expected

GetFileStreamAsync with StreamMode.Write ​

ProviderSupportedNotes
InMemory✅Full support
Azure Blob✅Full support
Aliyun✅Full support
SshNet✅Full support
Redis❌Not supported
S3❌Not supported

IQueue<T> ​

GetDeadletterItemsAsync ​

ProviderSupportedNotes
InMemory✅Returns all deadlettered entries
Redis❌Not implemented
Azure Service Bus❌Deadletter is a separate sub-queue; requires different API
Azure Storage Queue❌No deadletter concept in Azure Storage Queues
SQS❌Deadletter is a separate queue; not retrievable via this API

QueueEntryOptions.UniqueId ​

ProviderSupportedNotes
InMemory✅Uses provided ID as the entry ID
Redis✅Uses provided ID as the entry ID
Azure Service Bus✅Maps to MessageId (deduplicated only when RequiresDuplicateDetection is enabled)
Azure Storage Queue❌Azure assigns its own MessageId
SQS⚠️SQS assigns its own MessageId. On FIFO queues the value is sent as MessageDeduplicationId; on standard queues it is not sent

QueueEntryOptions.GroupId ​

ProviderEffect on deliveryNotes
InMemory❌Stored and returned on entry.GroupId only
Redis❌Stored in the payload envelope and returned on entry.GroupId only
Azure Storage Queue❌Stored in the payload envelope (default compatibility mode); not stored in legacy mode
Azure Service Bus⚠️Sent as SessionId. On session-enabled queues (RequiresSession), ordered per group, one message at a time per group; ignored otherwise
SQS standard queue✅Sent as MessageGroupId to enable fair queues (noisy neighbor mitigation). No ordering
SQS FIFO queue✅Sent as MessageGroupId. Strict ordering within a group; required by SQS

Queues that do not use the value for delivery log a single debug message the first time a group id is enqueued. See Message Groups.

Queue Ordering Guarantees ​

ProviderOrderingNotes
InMemory✅FIFO within the queue
Redis✅FIFO list; retried entries are re-queued
Azure Storage Queue⚠️Best effort
Azure Service Bus⚠️Best effort; concurrent workers and retries can reorder processing. On session-enabled queues, ordered per GroupId; an abandoned message is retried before later messages in its group
SQS standard queue❌Best effort. GroupId does not change ordering
SQS FIFO queue✅Strict ordering within a GroupId

Delivery Delay (DelayUntilUtc) ​

ProviderSupportedGranularityNotes
InMemory✅MillisecondPrecise delay via background timer
Redis❌—Not supported
Azure Storage Queue✅SecondVisibilityTimeout parameter
Azure Service Bus✅SecondScheduledEnqueueTimeUtc
SQS⚠️SecondDelaySeconds, clamped to 900s. FIFO queues reject a positive delay

Retry Delay on Abandon ​

ProviderMechanismDeliveryNotes
InMemoryRe-added after a timerAt-least-oncePending retries are lost if the process exits
RedisWait listAt-least-onceMoved back by queue maintenance
Azure Storage QueueVisibility timeout on the same messageAt-least-onceMessage ID is unchanged
Azure Service BusScheduled copy of the message; immediate abandon on session-enabled queues or when duplicate detection is enabledAt-least-onceThe copy is sent before the original is completed, so the original can be redelivered alongside the retry if completion fails. It keeps MessageId, CorrelationId, Subject, ContentType and properties. Session-enabled queues ignore RetryDelay so the retry stays at the head of its group
SQSVisibility timeout on the same messageAt-least-onceMessage ID is unchanged. On FIFO queues, later messages in the group wait for the retry

Handlers should be idempotent with every provider; see message loss and duplicates for the Service Bus guidance.

Service Bus delayed retries reuse the original MessageId, so on a queue with duplicate detection enabled (RequiresDuplicateDetection) the copy would be discarded as a duplicate. The queue therefore abandons immediately instead and does not apply RetryDelay when duplicate detection is enabled.


IMessageBus ​

Delayed Message Delivery ​

ProviderSupportedGranularityNotes
InMemory✅MillisecondBackground timer, precise
Redis✅MillisecondLua script with delay
Azure Service Bus✅SecondScheduled enqueue
RabbitMQ✅MillisecondVia delayed-message-exchange plugin
Kafka✅MillisecondClient-side delay before produce
SQS/SNS⚠️SecondImpractical due to polling latency

Note: For distributed providers, delay precision is limited by network latency and polling intervals. Tests use 1-second minimum delay to accommodate provider-level granularity.

Message Ordering Guarantees ​

ProviderOrderedNotes
InMemory✅FIFO within a single subscriber
Redis✅Pub/sub delivers in publish order
Azure Service Bus✅FIFO with sessions enabled
RabbitMQ✅FIFO per queue
Kafka✅FIFO per partition
SQS/SNS⚠️Standard queues are best-effort ordering

ICacheClient ​

All tested providers (InMemory, Redis) exhibit fully consistent behavior. No behavioral gaps were found for:

  • AddAsync (returns false when key exists)
  • GetAsync (returns no-value for missing keys)
  • RemoveAsync (returns false for non-existent keys)
  • RemoveByPrefixAsync (returns 0 for no matches)
  • IncrementAsync (with zero amount returns current value)
  • SetExpirationAsync (no-op for non-existent keys)

ILockProvider ​

All tested providers (InMemory, Redis) exhibit fully consistent behavior. No behavioral gaps were found for:

  • AcquireAsync with releaseOnDispose: false
  • ReleaseAsync with force release
  • ILock metadata properties (LockId, Resource, AcquiredTimeUtc)
  • TryUsingAsync execution and automatic release

Recommendations ​

  1. Don't rely on DeleteFileAsync return value for existence checks on S3-compatible storage. Use GetFileInfoAsync first if you need to know whether a file existed.

  2. Wrap S3 copy/rename/read operations in try-catch if the source file might not exist. Other providers return false/null gracefully.

  3. Use at least 1-second granularity for delivery delays when targeting distributed providers. Sub-second delays are only reliable with InMemory.

  4. Don't rely on GetDeadletterItemsAsync — only InMemory supports it. Design deadletter processing around provider-specific mechanisms instead.

  5. Prefer QueueEntryOptions.UniqueId only with providers that support it (InMemory, Redis, Azure Service Bus). On SQS and Azure Storage Queues, the system assigns its own IDs.

  6. Treat QueueEntryOptions.GroupId as a hint, not a guarantee. It only changes delivery on SQS (fair queues on standard queues, ordering on FIFO) and session-enabled Service Bus queues (ordering per group); the other built-in queues store it as metadata.

Released under the Apache 2.0 License.