Guidelines and recommendations for Reliable Collections in Azure Service Fabric
This section provides guidelines for using Reliable State Manager and Reliable Collections. The goal is to help users avoid common pitfalls.
Reliable Collection guildelines
The guidelines are organized as simple recommendations prefixed with the terms Do, Consider, Avoid and Do not.
- Do not modify an object of custom type returned by read operations (for example,
TryGetValueAsync). Reliable Collections, just like Concurrent Collections, return a reference to the objects and not a copy.
- Do deep copy the returned object of a custom type before modifying it. Since structs and built-in types are pass-by-value, you do not need to do a deep copy on them unless they contain reference-typed fields or properties that you intend to modify.
- Do not use
TimeSpan.MaxValuefor timeouts. Timeouts should be used to detect deadlocks.
- Do not use a transaction after it has been committed, aborted, or disposed.
- Do not use an enumeration outside of the transaction scope it was created in.
- Do not create a transaction within another transaction's
usingstatement because it can cause deadlocks.
- Do not create reliable state with
IReliableStateManager.GetOrAddAsyncand use the reliable state in the same transaction. This results in an InvalidOperationException.
- Do ensure that your
IComparable<TKey>implementation is correct. The system takes dependency on
IComparable<TKey>for merging checkpoints and rows.
- Do use Update lock when reading an item with an intention to update it to prevent a certain class of deadlocks.
- Consider keeping number of Reliable Collections per partition to be less than 1000. Prefer Reliable Collections with more items over more Reliable Collections with fewer items.
- Consider keeping your items (for example, TKey + TValue for Reliable Dictionary) below 80 KBytes: smaller the better. This reduces the amount of Large Object Heap usage as well as disk and network IO requirements. Often, it reduces replicating duplicate data when only one small part of the value is being updated. Common way to achieve this in Reliable Dictionary, is to break your rows in to multiple rows.
- Consider using backup and restore functionality to have disaster recovery.
- Avoid mixing single entity operations and multi-entity operations (e.g
CreateEnumerableAsync) in the same transaction due to the different isolation levels.
- Do handle InvalidOperationException. User transactions can be aborted by the system for variety of reasons. For example, when the Reliable State Manager is changing its role out of Primary or when a long-running transaction is blocking truncation of the transactional log. In such cases, user may receive InvalidOperationException indicating that their transaction has already been terminated. Assuming, the termination of the transaction was not requested by the user, best way to handle this exception is to dispose the transaction, check if the cancellation token has been signaled (or the role of the replica has been changed), and if not create a new transaction and retry.
- Do not apply parallel or concurrent operations within a transaction. Only one user thread operation is supported within a transaction. Otherwise, it will cause memory leak and lock issues.
- Consider dispose transaction as soon as possible after commit completes (especially if using ConcurrentQueue).
- Do not perform any blocking code inside a transaction.
Here are some things to keep in mind:
- The default timeout is 4 seconds for all the Reliable Collection APIs. Most users should use the default timeout.
- The default cancellation token is
CancellationToken.Nonein all Reliable Collections APIs.
- The key type parameter (TKey) for a Reliable Dictionary must correctly implement
Equals(). Keys must be immutable.
- To achieve high availability for the Reliable Collections, each service should have at least a target and minimum replica set size of 3.
- Read operations on the secondary may read versions that are not quorum committed. This means that a version of data that is read from a single secondary might be false progressed. Reads from Primary are always stable: can never be false progressed.
- Security/Privacy of the data persisted by your application in a reliable collection is your decision and subject to the protections provided by your storage management; I.E. Operating System disk encryption could be used to protect your data at rest.
ReliableDictionaryenumeration uses a sorted data structure ordered by key. To make enumeration efficient, commits are added to a temporary hashtable and later moved into the main sorted data structure post checkpoint. Adds/Updates/Deletes have best case runtime of O(1) and worst case runtime of O(log n), in the case of validation checks on the presence of the key. Gets might be O(1) or O(log n) depending on whether you are reading from a recent commit or from an older commit.
Additional guidelines for volatile Reliable Collections
When deciding to use volatile reliable collections, consider the following:
ReliableDictionarydoes have volatile support
ReliableQueuedoes have volatile support
ReliableConcurrentQueuedoes NOT have volatile support
- Persisted services CANNOT be made volatile. Changing the
falserequires recreating the entire service from scratch
- Volatile services CANNOT be made persisted. Changing the
truerequires recreating the entire service from scratch
HasPersistedStateis a service level config. This means that ALL collections will either be persisted or volatile. You cannot mix volatile and persisted collections
- Quorum loss of a volatile partition results in complete data loss
- Backup and restore is NOT available for volatile services