/// <summary> /// OnRemove is the counterpart to OnAdd, both should be implemented if one is. /// Like OnAdd the OnRemove is provided the <see cref="IndexState"/> used for all registration operations and should be used to unregister any additional /// details previously added. /// </summary> /// <param name="tx">The <see cref="ITransaction"/> this Remove operation is part of.</param> /// <param name="state"><see cref="IndexState"/></param> /// <param name="cancellationToken"><see cref="CancellationToken"/></param> /// <returns><see cref="Task"/></returns> /// <remarks> /// Expected behavior for this method is the provided transaction <paramref name="tx"/> should not be explicitly committed under normal behavior. /// If an error occurs an exception is the preferred method to trigger failure, but the transaction can be aborted with <see cref="ITransaction.Abort"/> /// explicitly. /// Both methods will result in complete rollback of the transaction. /// </remarks> protected virtual Task OnRemoveActorAsync(ITransaction tx, IndexState state, CancellationToken cancellationToken) { return(Task.CompletedTask); }
/// <summary> /// Catalogs the provided actor and its identity into this registry. /// This method expects an external transaction to be provided for managing atomicity /// </summary> /// <param name="tx">The <see cref="ITransaction"/> this Add operation is part of.</param> /// <param name="reference"><see cref="ActorReference"/> to store into internal state</param> /// <param name="identity">The underlying identity of the provided actor. This is not the actor id</param> /// <param name="cancellationToken"><see cref="CancellationToken"/></param> /// <returns><see cref="Task"/></returns> protected async Task AddActorAsync(ITransaction tx, ActorReference reference, Identity identity, CancellationToken cancellationToken) { var referenceMap = await GetReferenceMapAsync(); var indexMap = await GetIndexMapAsync(); var indexableRef = new IndexableActorReference(reference); var indexLookup = await referenceMap.TryGetValueAsync(tx, indexableRef, ReliableCollectionDefaults.ReadTimeout, cancellationToken); long index; IndexState state; if (indexLookup.HasValue) { index = indexLookup.Value; var stateLookup = await indexMap.TryGetValueAsync(tx, index, ReliableCollectionDefaults.ReadTimeout, cancellationToken); if (!stateLookup.HasValue) { throw new InvalidOperationException($"{indexableRef.StringRepresentation} is missing its internal IndexState"); } state = stateLookup.Value; IndexState newState; if (Equals(state.Identity, identity)) { newState = state; } else { newState = new IndexState { Reference = reference, Identity = identity, Index = index }; // replace old state await indexMap.SetAsync(tx, index, newState, ReliableCollectionDefaults.WriteTimeout, cancellationToken); } await OnUpdateActorAsync(tx, state, newState, cancellationToken); } else { var identityMap = await GetIdentityMapAsync(); var existingIndex = await identityMap.TryGetValueAsync(tx, identity, ReliableCollectionDefaults.ReadTimeout, cancellationToken); if (existingIndex.HasValue) { var existingState = await indexMap.TryGetValueAsync(tx, existingIndex.Value); throw new InvalidOperationException($"{identity} is already registered to Actor {new IndexableActorReference(existingState.Value.Reference).StringRepresentation}"); } var propertiesMap = await GetPropertiesMapAsync(); index = (long)await propertiesMap.AddOrUpdateAsync(tx, "currentIndex", 1L, (key, value) => (long)value + 1, ReliableCollectionDefaults.WriteTimeout, cancellationToken); state = new IndexState { Reference = reference, Identity = identity, Index = index }; await referenceMap.AddAsync(tx, indexableRef, index, ReliableCollectionDefaults.WriteTimeout, cancellationToken); await indexMap.AddAsync(tx, index, state, ReliableCollectionDefaults.WriteTimeout, cancellationToken); await identityMap.AddAsync(tx, identity, index, ReliableCollectionDefaults.WriteTimeout, cancellationToken); await OnAddActorAsync(tx, state, cancellationToken); } }
/// <summary> /// Implementations that need to allow alterable state should override this method. Additions that already have an internal state will trigger an update /// instead of an add which will pass the old and new state to this method for further re-cataloging activity. /// </summary> /// <param name="tx">The <see cref="ITransaction"/> this Add operation is part of.</param> /// <param name="oldState">The old <see cref="IndexState"/></param> /// <param name="newState">The new <see cref="IndexState"/></param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> protected virtual Task OnUpdateActorAsync(ITransaction tx, IndexState oldState, IndexState newState, CancellationToken cancellationToken) { return(Task.CompletedTask); }