예제 #1
0
        private static bool?CreateIndexesForASingleInterface(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry,
                                                             Type propertiesClassType, Type grainInterfaceType, Type grainClassType,
                                                             ConsistencyScheme consistencyScheme, bool?grainIndexesAreEager)
        {
            // All the properties in TProperties are scanned for Index annotation.
            // If found, the index is created using the information provided in the annotation.
            var indexesOnInterface = new NamedIndexMap(propertiesClassType);

            foreach (var propInfo in propertiesClassType.GetProperties())
            {
                var indexAttrs = propInfo.GetCustomAttributes <IndexAttribute>(inherit: false);
                foreach (var indexAttr in indexAttrs)
                {
                    var indexName = IndexUtils.PropertyNameToIndexName(propInfo.Name);
                    if (indexesOnInterface.ContainsKey(indexName))
                    {
                        throw new InvalidOperationException($"An index named {indexName} already exists for user-defined grain interface {grainInterfaceType.Name}");
                    }

                    var indexType = (Type)indexTypeProperty.GetValue(indexAttr);
                    if (consistencyScheme == ConsistencyScheme.Transactional)
                    {
                        var transactionalVariantAttr = indexType.GetCustomAttribute <TransactionalIndexVariantAttribute>();
                        if (transactionalVariantAttr != null)
                        {
                            indexType = transactionalVariantAttr.TransactionalIndexType;
                        }
                    }
                    if (indexType.IsGenericTypeDefinition)
                    {
                        // For the (Active|Total) constructors that take (Active|Total)IndexType parameters, leaving the indexType's key type and interface type
                        // generic arguments as "<,>", this fills them in.
                        indexType = indexType.MakeGenericType(propInfo.PropertyType, grainInterfaceType);
                    }

                    // If it's not eager, then it's configured to be lazily updated
                    var isEager = (bool)isEagerProperty.GetValue(indexAttr);
                    if (!grainIndexesAreEager.HasValue)
                    {
                        grainIndexesAreEager = isEager;
                    }
                    var isUnique = (bool)isUniqueProperty.GetValue(indexAttr);

                    ValidateSingleIndex(indexAttr, grainInterfaceType, grainClassType, propertiesClassType, propInfo, grainIndexesAreEager,
                                        consistencyScheme, isEager: isEager, isUnique: isUnique);   // Multiple bools, so use param names for safety

                    var maxEntriesPerBucket = (int)maxEntriesPerBucketProperty.GetValue(indexAttr);
                    if (loader != null)
                    {
                        loader.CreateIndex(propertiesClassType, grainInterfaceType, indexesOnInterface, propInfo, indexName, indexType, isEager, isUnique, maxEntriesPerBucket);
                    }
                    else
                    {
                        IndexFactory.ValidateIndexType(indexType, propInfo, out _, out _);
                    }
                }
            }
            registry[grainInterfaceType] = indexesOnInterface;
            return(grainIndexesAreEager);
        }
예제 #2
0
        private static void GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainClassType)
        {
            // First see if any indexed interfaces on this grain were already encountered on another grain (unless we're
            // in validation mode, which doesn't create the indexes).
            var indexedInterfacesAndProperties = EnumerateIndexedInterfacesForAGrainClassType(grainClassType).ToList();
            var indexedInterfaces = loader != null
                    ? new HashSet <Type>(indexedInterfacesAndProperties.Select(tup => tup.interfaceType).Where(itfType => registry.ContainsKey(itfType)))
                    : new HashSet <Type>();

            var grainIndexesAreEager = indexedInterfaces.Count > 0 ? registry[indexedInterfaces.First()].HasAnyEagerIndex : default(bool?);
            var consistencyScheme    = grainClassType.GetConsistencyScheme();

            // Now find all indexed interfaces we're seeing for the first time (again, unless we're in validation mode).
            foreach (var(grainInterfaceType, propertiesClassType) in indexedInterfacesAndProperties)
            {
                if (!indexedInterfaces.Contains(grainInterfaceType))
                {
                    grainIndexesAreEager = CreateIndexesForASingleInterface(loader, registry, propertiesClassType, grainInterfaceType,
                                                                            grainClassType, consistencyScheme, grainIndexesAreEager);
                    indexedInterfaces.Add(grainInterfaceType);
                }
            }

            IReadOnlyDictionary <string, object> getNullValuesDictionary()
            {
                IEnumerable <(string propName, (string itfName, object nullValue))> getNullPropertyValuesForInterface(Type interfaceType)
                => registry[interfaceType].PropertiesClassType.GetProperties()
                .Select(info => (name: info.Name, nullSpec: (itfname: interfaceType.Name, nullValue: IndexUtils.GetNullValue(info))))
                .Where(p => p.nullSpec.nullValue != null);

                Dictionary <string, (string, object)> addToDict(Dictionary <string, (string, object)> dict, (string propName, (string itfName, object nullValue)nullSpec) current)
                {
                    bool isInDict(string propName)
                    {
                        return(dict.TryGetValue(propName, out (string itfName, object nullValue)prevNullSpec)
                            ? (prevNullSpec.nullValue.Equals(current.nullSpec.nullValue)
                                ? true
                                : throw new IndexConfigurationException($"Property {propName} has conflicting NullValues defined on interfaces {prevNullSpec.itfName} and {current.nullSpec.itfName}"))
                            : false);
                    }

                    if (!isInDict(current.propName))
                    {
                        dict[current.propName] = current.nullSpec;
                    }
                    return(dict);
                }

                return(indexedInterfaces.SelectMany(itf => getNullPropertyValuesForInterface(itf))
                       .Aggregate(new Dictionary <string, (string itfName, object nullValue)>(), (dict, pair) => addToDict(dict, pair))
                       .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.nullValue));
            }

            if (indexedInterfaces.Count > 0)
            {
                registry.SetGrainIndexes(grainClassType, indexedInterfaces.ToArray(), getNullValuesDictionary());
            }
        }
        private async static Task GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainClassType)
        {
            if (!typeof(IIndexableGrain).IsAssignableFrom(grainClassType))
            {
                return;
            }

            if (registry.ContainsGrainType(grainClassType))
            {
                throw new InvalidOperationException($"Grain class type {grainClassType.Name} has already been added to the registry");
            }

            bool?grainIndexesAreEager = null;
            var  indexedInterfaces    = new List <Type>();
            var  consistencyScheme    = grainClassType.GetConsistencyScheme();

            foreach (var(grainInterfaceType, propertiesClassType) in EnumerateIndexedInterfacesForAGrainClassType(grainClassType).Where(tup => !registry.ContainsKey(tup.interfaceType)))
            {
                grainIndexesAreEager = await CreateIndexesForASingleInterface(loader, registry, propertiesClassType, grainInterfaceType,
                                                                              grainClassType, consistencyScheme, grainIndexesAreEager);

                indexedInterfaces.Add(grainInterfaceType);
            }

            IReadOnlyDictionary <string, object> getNullValuesDictionary()
            {
                IEnumerable <(string propName, (string itfName, object nullValue))> getNullPropertyValuesForInterface(Type interfaceType)
                => registry[interfaceType].PropertiesClassType.GetProperties()
                .Select(info => (name: info.Name, nullSpec: (itfname: interfaceType.Name, nullValue: IndexUtils.GetNullValue(info))))
                .Where(p => p.nullSpec.nullValue != null);

                Dictionary <string, (string, object)> addToDict(Dictionary <string, (string, object)> dict, (string propName, (string itfName, object nullValue)nullSpec) current)
                {
                    bool isInDict(string propName)
                    {
                        return(dict.TryGetValue(propName, out (string itfName, object nullValue)prevNullSpec)
                            ? (prevNullSpec.nullValue.Equals(current.nullSpec.nullValue)
                                ? true
                                : throw new IndexConfigurationException($"Property {propName} has conflicting NullValues defined on interfaces {prevNullSpec.itfName} and {current.nullSpec.itfName}"))
                            : false);
                    }

                    if (!isInDict(current.propName))
                    {
                        dict[current.propName] = current.nullSpec;
                    }
                    return(dict);
                }

                return(indexedInterfaces.SelectMany(itf => getNullPropertyValuesForInterface(itf))
                       .Aggregate(new Dictionary <string, (string itfName, object nullValue)>(), (dict, pair) => addToDict(dict, pair))
                       .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.nullValue));
            }

            registry.SetGrainIndexes(grainClassType, indexedInterfaces.ToArray(), getNullValuesDictionary());
        }
예제 #4
0
        private async static Task <bool?> CreateIndexesForASingleInterface(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type propertiesArgType,
                                                                           Type userDefinedIGrain, Type userDefinedGrainImpl, bool?grainIndexesAreEager)
        {
            // All the properties in TProperties are scanned for Index annotation.
            // If found, the index is created using the information provided in the annotation.
            NamedIndexMap indexesOnGrain        = new NamedIndexMap();
            var           interfaceHasLazyIndex = false; // Use a separate value from grainIndexesAreEager in case we change to allow mixing eager and lazy on a single grain.

            foreach (PropertyInfo propInfo in propertiesArgType.GetProperties())
            {
                var indexAttrs = propInfo.GetCustomAttributes <IndexAttribute>(inherit: false);
                foreach (var indexAttr in indexAttrs)
                {
                    string indexName = "__" + propInfo.Name;
                    if (indexesOnGrain.ContainsKey(indexName))
                    {
                        throw new InvalidOperationException($"An index named {indexName} already exists for user-defined grain interface {userDefinedIGrain.Name}");
                    }

                    Type indexType = (Type)indexTypeProperty.GetValue(indexAttr);
                    if (indexType.IsGenericTypeDefinition)
                    {
                        indexType = indexType.MakeGenericType(propInfo.PropertyType, userDefinedIGrain);
                    }

                    // If it's not eager, then it's configured to be lazily updated
                    bool isEager = (bool)isEagerProperty.GetValue(indexAttr);
                    if (!isEager)
                    {
                        interfaceHasLazyIndex = true;
                    }
                    if (!grainIndexesAreEager.HasValue)
                    {
                        grainIndexesAreEager = isEager;
                    }
                    bool isUnique = (bool)isUniqueProperty.GetValue(indexAttr);

                    ValidateSingleIndex(indexAttr, userDefinedIGrain, userDefinedGrainImpl, propertiesArgType, propInfo, grainIndexesAreEager, isEager, isUnique);

                    int maxEntriesPerBucket = (int)maxEntriesPerBucketProperty.GetValue(indexAttr);
                    if (loader != null)
                    {
                        await loader.CreateIndex(propertiesArgType, userDefinedIGrain, indexesOnGrain, propInfo, indexName, indexType, isEager, isUnique, maxEntriesPerBucket);
                    }
                }
            }
            registry[userDefinedIGrain] = indexesOnGrain;
            if (interfaceHasLazyIndex && loader != null)
            {
                await loader.RegisterWorkflowQueues(userDefinedIGrain, userDefinedGrainImpl);
            }
            return(grainIndexesAreEager);
        }
예제 #5
0
        internal async static Task <IndexRegistry> GetIndexRegistry(ApplicationPartsIndexableGrainLoader loader, Type[] grainTypes)
        {
            var registry = new IndexRegistry();

            foreach (var grainType in grainTypes)
            {
                if (registry.ContainsKey(grainType))
                {
                    throw new InvalidOperationException($"Precondition violated: GetGrainClassIndexes should not encounter a duplicate type ({IndexUtils.GetFullTypeName(grainType)})");
                }
                await GetIndexesForASingleGrainType(loader, registry, grainType);
            }
            return(registry);
        }
예제 #6
0
        internal static IndexRegistry GetIndexRegistry(ApplicationPartsIndexableGrainLoader loader, IEnumerable <Type> grainClassTypes)
        {
            var registry = new IndexRegistry();

            foreach (var grainClassType in grainClassTypes)
            {
                if (registry.ContainsGrainType(grainClassType))
                {
                    throw new InvalidOperationException($"Precondition violated: GetIndexRegistry should not encounter a duplicate grain class type ({IndexUtils.GetFullTypeName(grainClassType)})");
                }
                GetIndexesForASingleGrainType(loader, registry, grainClassType);
            }
            return(registry);
        }
예제 #7
0
        /// <summary>
        /// Configure silo to use indexing using a configure action.
        /// </summary>
        public static ISiloHostBuilder UseIndexing(this ISiloHostBuilder builder, Action <IndexingOptions> configureOptions = null)
        {
            // This is necessary to get the configured NumWorkflowQueuesPerInterface for IndexFactory.RegisterIndexWorkflowQueueGrainServices.
            var indexingOptions = new IndexingOptions();

            configureOptions?.Invoke(indexingOptions);

            return(builder.AddSimpleMessageStreamProvider(IndexingConstants.INDEXING_STREAM_PROVIDER_NAME)
                   .AddMemoryGrainStorage(IndexingConstants.INDEXING_WORKFLOWQUEUE_STORAGE_PROVIDER_NAME)
                   .AddMemoryGrainStorage(IndexingConstants.INDEXING_STORAGE_PROVIDER_NAME)
                   .AddMemoryGrainStorage(IndexingConstants.MEMORY_STORAGE_PROVIDER_NAME)
                   .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(SiloBuilderExtensions).Assembly))
                   .ConfigureServices(services => services.UseIndexing(indexingOptions))
                   .ConfigureServices((context, services) => ApplicationPartsIndexableGrainLoader.RegisterGrainServices(context, services, indexingOptions))
                   .UseTransactions());
        }
예제 #8
0
 private void EnsureGrainStorage()
 {
     if (_grainStorage == null)
     {
         var implementation = TypeCodeMapper.GetImplementation(this.SiloIndexManager.GrainTypeResolver, typeof(V));
         if (implementation == null || (grainImplClass = implementation.GrainClass) == null ||
             !this.SiloIndexManager.CachedTypeResolver.TryResolveType(grainImplClass, out Type implType))
         {
             throw new IndexException($"The grain implementation class {implementation.GrainClass} for grain" +
                                      " interface {IndexUtils.GetFullTypeName(typeof(V))} was not resolved.");
         }
         _grainStorage = implType.GetGrainStorage(this.SiloIndexManager.ServiceProvider);
         bool isFaultTolerant = ApplicationPartsIndexableGrainLoader.IsSubclassOfRawGenericType(typeof(IndexableGrain <,>), implType);
         _indexedFieldPrefix = isFaultTolerant ? "UserState." : string.Empty;
     }
 }
예제 #9
0
        private async static Task GetIndexesForASingleGrainType(ApplicationPartsIndexableGrainLoader loader, IndexRegistry registry, Type grainType)
        {
            Type[] interfaces           = grainType.GetInterfaces();
            bool?  grainIndexesAreEager = null;

            // If there is an interface that directly extends IIndexableGrain<TProperties>...
            Type iIndexableGrain = interfaces.Where(itf => itf.IsGenericType && itf.GetGenericTypeDefinition() == typeof(IIndexableGrain <>)).FirstOrDefault();

            if (iIndexableGrain != null)
            {
                // ... and its generic argument is a class (TProperties)...
                Type propertiesArgType = iIndexableGrain.GetGenericArguments()[0];
                if (propertiesArgType.GetTypeInfo().IsClass)
                {
                    // ... then add the indexes for all the descendant interfaces of IIndexableGrain<TProperties>; these interfaces are defined by end-users.
                    foreach (Type userDefinedIGrain in interfaces.Where(itf => iIndexableGrain != itf && iIndexableGrain.IsAssignableFrom(itf) && !registry.ContainsKey(itf)))
                    {
                        grainIndexesAreEager = await CreateIndexesForASingleInterface(loader, registry, propertiesArgType, userDefinedIGrain, grainType, grainIndexesAreEager);
                    }
                }
            }
        }
예제 #10
0
 public static void Validate(Assembly assembly)
 {
     var grainClassTypes = ApplicationPartsIndexableGrainLoader.GetAssemblyIndexedConcreteGrainClasses(assembly);
     var _ = ApplicationPartsIndexableGrainLoader.GetIndexRegistry(null, grainClassTypes);
 }
예제 #11
0
 public static async Task Validate(Type[] grainTypes)
 {
     var _ = await ApplicationPartsIndexableGrainLoader.GetIndexRegistry(null, grainTypes);
 }
 public static void Validate(Type[] types)
 {
     var _ = ApplicationPartsIndexableGrainLoader.GetIndexRegistry(null, types);
 }