Exemplo n.º 1
0
        /// <summary>
        /// Applies a set of updates to the indexes defined on the grain
        /// </summary>
        /// <param name="interfaceToUpdatesMap">the dictionary of indexes to their corresponding updates</param>
        /// <param name="updateIndexesEagerly">whether indexes should be updated eagerly or lazily; must always be true for transactional indexes</param>
        /// <param name="onlyUniqueIndexesWereUpdated">a flag to determine whether only unique indexes were updated; unused for transactional indexes</param>
        /// <param name="numberOfUniqueIndexesUpdated">determine the number of updated unique indexes; unused for transactional indexes</param>
        /// <param name="writeStateIfConstraintsAreNotViolated">whether the state should be written to storage if no constraint is violated;
        ///                                                     must always be true for transactional indexes</param>
        private protected override async Task ApplyIndexUpdates(InterfaceToUpdatesMap interfaceToUpdatesMap,
                                                                bool updateIndexesEagerly,
                                                                bool onlyUniqueIndexesWereUpdated,
                                                                int numberOfUniqueIndexesUpdated,
                                                                bool writeStateIfConstraintsAreNotViolated)
        {
            Debug.Assert(writeStateIfConstraintsAreNotViolated, "Transactional index writes must only be called when updating the grain state (not on activation change).");

            // For Transactional, the grain-state write has already been done by the time we get here.
            if (!interfaceToUpdatesMap.IsEmpty)
            {
                Debug.Assert(updateIndexesEagerly, "Transactional indexes cannot be configured to be lazy; this misconfiguration should have been caught in ValidateSingleIndex.");
                IEnumerable <Task> getIndexUpdateTasks(Type grainInterfaceType, IReadOnlyDictionary <string, IMemberUpdate> updates)
                {
                    var indexInterfaces = this._grainIndexes[grainInterfaceType];

                    foreach (var(indexName, mu) in updates.Where(kvp => kvp.Value.OperationType != IndexOperationType.None).OrderBy(kvp => kvp.Key))
                    {
                        var indexInfo     = indexInterfaces.NamedIndexes[indexName];
                        var updateToIndex = new MemberUpdateOverriddenMode(mu, IndexUpdateMode.Transactional) as IMemberUpdate;
                        yield return(indexInfo.IndexInterface.ApplyIndexUpdate(this.SiloIndexManager,
                                                                               this.iIndexableGrain, updateToIndex.AsImmutable(), indexInfo.MetaData, base.BaseSiloAddress));
                    }
                }

                // Execute each index update individually, in an invariant sequence, to avoid deadlocks when locking multiple index buckets.
                // The invariant sequence must apply across all grains, since a single indexed interface may be on multiple grains.
                // Therefore, order by interface name and within that by index name.
                // TODO performance: safely execute multiple index updates in parallel.
                foreach (var updateTask in interfaceToUpdatesMap.OrderBy(kvp => kvp.Key.FullName).SelectMany(kvp => getIndexUpdateTasks(kvp.Key, kvp.Value)))
                {
                    await updateTask;
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Applies a set of updates to the indexes defined on the grain
        /// </summary>
        /// <param name="interfaceToUpdatesMap">the dictionary of indexes to their corresponding updates</param>
        /// <param name="updateIndexesEagerly">whether indexes should be updated eagerly or lazily</param>
        /// <param name="onlyUniqueIndexesWereUpdated">a flag to determine whether only unique indexes were updated</param>
        /// <param name="numberOfUniqueIndexesUpdated">determine the number of updated unique indexes</param>
        /// <param name="writeStateIfConstraintsAreNotViolated">whether the state should be written to storage if no constraint is violated</param>
        private protected override async Task ApplyIndexUpdates(InterfaceToUpdatesMap interfaceToUpdatesMap,
                                                                bool updateIndexesEagerly,
                                                                bool onlyUniqueIndexesWereUpdated,
                                                                int numberOfUniqueIndexesUpdated,
                                                                bool writeStateIfConstraintsAreNotViolated)
        {
            // If there is no update to the indexes, we should only write back the state of the grain, if requested.
            if (interfaceToUpdatesMap.IsEmpty)
            {
                if (writeStateIfConstraintsAreNotViolated)
                {
                    await base.writeGrainStateFunc();
                }
                return;
            }

            // HashIndexBucketState will not actually perform an index removal (Delete) if the index is not marked tentative.
            // Therefore we must do a two-step approach here; mark a tentative Delete, then do the non-tentative Delete.
            var updateEagerUniqueIndexesTentatively = numberOfUniqueIndexesUpdated > 1 || interfaceToUpdatesMap.HasAnyDeletes;

            // Apply any unique index updates eagerly.
            if (numberOfUniqueIndexesUpdated > 0)
            {
                try
                {
                    // If there is more than one unique index to update, then updates to the unique indexes should be tentative
                    // so they are not visible to readers before making sure that all uniqueness constraints are satisfied.
                    await this.ApplyIndexUpdatesEagerly(interfaceToUpdatesMap, UpdateIndexType.Unique, updateEagerUniqueIndexesTentatively);
                }
                catch (UniquenessConstraintViolatedException ex)
                {
                    // If any uniqueness constraint is violated and we have more than one unique index defined, then all tentative
                    // updates must be undone, then the exception is thrown back to the user code.
                    if (updateEagerUniqueIndexesTentatively)
                    {
                        await this.UndoTentativeChangesToUniqueIndexesEagerly(interfaceToUpdatesMap);
                    }
                    throw ex;
                }
            }

            if (updateIndexesEagerly)
            {
                var updateIndexTypes = UpdateIndexType.None;
                if (updateEagerUniqueIndexesTentatively)
                {
                    updateIndexTypes |= UpdateIndexType.Unique;
                }
                if (!onlyUniqueIndexesWereUpdated)
                {
                    updateIndexTypes |= UpdateIndexType.NonUnique;
                }

                if (updateIndexTypes != UpdateIndexType.None || writeStateIfConstraintsAreNotViolated)
                {
                    await Task.WhenAll(new[]
                    {
                        updateIndexTypes != UpdateIndexType.None ? base.ApplyIndexUpdatesEagerly(interfaceToUpdatesMap, updateIndexTypes, isTentative: false) : null,
                        writeStateIfConstraintsAreNotViolated ? base.writeGrainStateFunc() : null
                    }.Coalesce());
                }
            }
            else // !updateIndexesEagerly
            {
                this.ApplyIndexUpdatesLazilyWithoutWait(interfaceToUpdatesMap);
                if (writeStateIfConstraintsAreNotViolated)
                {
                    await base.writeGrainStateFunc();
                }
            }

            // If everything was successful, the before images are updated
            this.UpdateBeforeImages(interfaceToUpdatesMap);
        }
Exemplo n.º 3
0
 private void ApplyIndexUpdatesLazilyWithoutWait(InterfaceToUpdatesMap updatesByInterface)
 => base.ApplyIndexUpdatesLazily(updatesByInterface).Ignore();
Exemplo n.º 4
0
 private Task UndoTentativeChangesToUniqueIndexesEagerly(InterfaceToUpdatesMap interfaceToUpdatesMap)
 => Task.WhenAll(interfaceToUpdatesMap.Select(kvp => base.ApplyIndexUpdatesEagerly(kvp.Key, MemberUpdateReverseTentative.Reverse(kvp.Value),
                                                                                   UpdateIndexType.Unique, isTentative: false)));
 private protected void UpdateBeforeImages(InterfaceToUpdatesMap interfaceToUpdatesMap)
 => this._grainIndexes.UpdateBeforeImages(interfaceToUpdatesMap);
 private protected Task ApplyIndexUpdatesLazily(InterfaceToUpdatesMap interfaceToUpdatesMap)
 => Task.WhenAll(interfaceToUpdatesMap.Select(kvp => this.GetWorkflowQueue(kvp.Key).AddToQueue(new IndexWorkflowRecord(interfaceToUpdatesMap.WorkflowIds[kvp.Key],
                                                                                                                       base.iIndexableGrain, kvp.Value).AsImmutable())));
 private protected Task ApplyIndexUpdatesEagerly(InterfaceToUpdatesMap interfaceToUpdatesMap,
                                                 UpdateIndexType updateIndexTypes, bool isTentative = false)
 => Task.WhenAll(interfaceToUpdatesMap.Select(kvp => this.ApplyIndexUpdatesEagerly(kvp.Key, kvp.Value, updateIndexTypes, isTentative)));
Exemplo n.º 8
0
 /// <summary>
 /// Applies a set of updates to the indexes defined on the grain
 /// </summary>
 /// <param name="interfaceToUpdatesMap">the dictionary of indexes to their corresponding updates</param>
 /// <param name="updateIndexesEagerly">whether indexes should be updated eagerly or lazily</param>
 /// <param name="onlyUniqueIndexesWereUpdated">a flag to determine whether only unique indexes were updated</param>
 /// <param name="numberOfUniqueIndexesUpdated">determine the number of updated unique indexes</param>
 /// <param name="writeStateIfConstraintsAreNotViolated">whether writing back
 ///             the state to the storage should be done if no constraint is violated</param>
 private protected abstract Task ApplyIndexUpdates(InterfaceToUpdatesMap interfaceToUpdatesMap,
                                                   bool updateIndexesEagerly, bool onlyUniqueIndexesWereUpdated,
                                                   int numberOfUniqueIndexesUpdated, bool writeStateIfConstraintsAreNotViolated);