예제 #1
0
        /// <summary>
        ///     Explicitly registers a type on the client and the server. In order to use this version of the method
        ///     the type need to be tagged(attributes need to be associated to its public properties used as keys)
        ///     Explicit type registration is not required.
        ///     It is done automatically when the first Put() is executed for an item of the specified type
        ///     or when a query is first built
        ///     <br />
        ///     As type registration is an expensive operation, you may want to do it during the client initialization.
        /// </summary>
        /// <param name="type"></param>
        public ClientSideTypeDescription RegisterTypeIfNeeded(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (KnownTypes.TryGetValue(type.FullName ?? throw new InvalidOperationException(), out var typeDescription))
            {
                return(typeDescription);
            }

            typeDescription = ClientSideTypeDescription.RegisterType(type);
            KnownTypes[typeDescription.FullTypeName] = typeDescription;


            var request = new RegisterTypeRequest(typeDescription.AsTypeDescription, ShardIndex, ShardsCount);

            var response = Channel.SendRequest(request);

            if (response is ExceptionResponse exResponse)
            {
                throw new CacheException("Error while registering a type on the server", exResponse.Message,
                                         exResponse.CallStack);
            }

            return(typeDescription);
        }
예제 #2
0
        public ClientSideTypeDescription RegisterType(Type type, bool forceReindex = false)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }


            var typeDescription = ClientSideTypeDescription.RegisterType(type);

            KnownTypes[typeDescription.FullTypeName] = typeDescription;


            var request = new RegisterTypeRequest(typeDescription.AsTypeDescription, ShardIndex, ShardsCount);

            var response = Channel.SendRequest(request);

            if (response is ExceptionResponse exResponse)
            {
                throw new CacheException("Error while registering a type on the server", exResponse.Message,
                                         exResponse.CallStack);
            }

            return(typeDescription);
        }
예제 #3
0
        public ClientSideTypeDescription RegisterType(Type type, ClientSideTypeDescription typeDescription, bool forceReindex = false)
        {
            ClientSideTypeDescription result = null;

            foreach (var client in CacheClients)
            {
                result = client.RegisterType(type, typeDescription);
            }

            return(result);
        }
예제 #4
0
        public ClientSideTypeDescription RegisterTypeIfNeeded(Type type, TypeDescriptionConfig description)
        {
            ClientSideTypeDescription result = null;

            foreach (var client in CacheClients)
            {
                result = client.RegisterTypeIfNeeded(type, description);
            }

            return(result);
        }
예제 #5
0
        public ClientSideTypeDescription RegisterTypeIfNeeded(Type type)
        {
            ClientSideTypeDescription description = null;

            foreach (var client in CacheClients)
            {
                description = client.RegisterTypeIfNeeded(type);
            }

            return(description);
        }
예제 #6
0
        public ClientSideTypeDescription RegisterType(Type type, bool forceReindex = false)
        {
            ClientSideTypeDescription description = null;

            Parallel.ForEach(CacheClients, client =>
            {
                description = client.RegisterType(type, forceReindex);
            });

            return(description);
        }
예제 #7
0
        /// <summary>
        ///     Register a type as cacheable with an external description
        ///     Cacheable type descriptions can be provided by attributes on the public properties
        ///     or as <see cref="TypeDescriptionConfig" />.
        ///     If an external description is required, the type must be explicitly registered.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="description"></param>
        public ClientSideTypeDescription RegisterTypeIfNeeded(Type type, TypeDescriptionConfig description)
        {
            if (KnownTypes.TryGetValue(description.FullTypeName, out var typeDescription))
            {
                return(typeDescription);
            }

            typeDescription = ClientSideTypeDescription.RegisterType(type, description);

            KnownTypes[typeDescription.FullTypeName] = typeDescription;


            var request = new RegisterTypeRequest(typeDescription.AsTypeDescription, ShardIndex, ShardsCount);

            var response = Channel.SendRequest(request);

            if (response is ExceptionResponse exResponse)
            {
                throw new CacheException("Error while registering a type on the server", exResponse.Message,
                                         exResponse.CallStack);
            }

            return(typeDescription);
        }
예제 #8
0
        public void FeedMany <T>(IEnumerable <T> items, bool excludeFromEviction, int packetSize)
        {
            if (items == null)
            {
                throw new ArgumentNullException(nameof(items));
            }

            var itemType = typeof(T);

            ClientSideTypeDescription description = null;
            var    needPack = itemType != typeof(CachedObject);
            string typeName = null;

            var sessionId = Guid.NewGuid().ToString();

            if (needPack)
            {
                description = RegisterTypeIfNeeded(itemType);
                typeName    = description.FullTypeName;
            }


            using (var enumerator = items.GetEnumerator())
            {
                var endLoop = false;

                while (!endLoop)
                {
                    var packet = new CachedObject[packetSize];
                    var toPack = new T[packetSize];

                    var count = 0;
                    for (var i = 0; i < packetSize; i++)
                    {
                        if (enumerator.MoveNext())
                        {
                            var item = enumerator.Current;
                            toPack[i] = item;
                            count++;
                        }
                        else
                        {
                            endLoop = true;
                            break;
                        }
                    }


                    Parallel.For(0, count, new ParallelOptions {
                        MaxDegreeOfParallelism = 10
                    }, (i, loopState) =>
                    {
                        var item       = toPack[i];
                        var packedItem = needPack ? CachedObject.Pack(item, description) : item as CachedObject;

                        if (typeName == null)
                        {
                            typeName = packedItem?.FullTypeName;
                        }

                        packet[i] = packedItem;
                    }
                                 );


                    if (typeName != null) // null only for empty collection
                    {
                        var request = new PutRequest(typeName)
                        {
                            ExcludeFromEviction = excludeFromEviction,
                            SessionId           = sessionId,
                            EndOfSession        = endLoop
                        };

                        foreach (var cachedObject in packet)
                        {
                            if (cachedObject != null)
                            {
                                request.Items.Add(cachedObject);
                            }
                        }

                        var response = Channel.SendRequest(request);
                        if (response is ExceptionResponse exResponse)
                        {
                            throw new CacheException("Error while writing an object to the cache", exResponse.Message,
                                                     exResponse.CallStack);
                        }
                    }
                }
            }
        }
예제 #9
0
        /// <summary>
        ///     FeedMany is non transactional but it does not lock the cache. To be used for massive feeds
        ///     Can feed with business objects (they will be packed to CachedObject) or directly with CachedObject
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="items"></param>
        /// <param name="excludeFromEviction"></param>
        /// <param name="packetSize"></param>
        public void FeedMany <T>(IEnumerable <T> items, bool excludeFromEviction, int packetSize)
        {
            if (items == null)
            {
                throw new ArgumentNullException(nameof(items));
            }

            // Lazy type registration
            var itemType = typeof(T);

            ClientSideTypeDescription description = null;
            var    needPack = itemType != typeof(CachedObject);
            string typeName = null;

            var sessionId = Guid.NewGuid().ToString();

            if (needPack)
            {
                description = RegisterTypeIfNeeded(itemType);
                typeName    = description.FullTypeName;
            }

            // initialize one request per node
            var requests = new PutRequest[CacheClients.Count];


            // Parallelize both nodes and requests (multiple requests for the same node are executed in parallel if multiple connections are available in the pool)
            var tasks = new List <Task>();

            foreach (var item in items)
            {
                if (typeName == null)
                {
                    typeName = (item as CachedObject)?.FullTypeName;
                }

                var packedItem = needPack ? CachedObject.Pack(item, description) : item as CachedObject;

                var node = WhichNode(packedItem);

                if (requests[node] == null)
                {
                    requests[node] = new PutRequest(typeName)
                    {
                        ExcludeFromEviction = excludeFromEviction,
                        SessionId           = sessionId
                    }
                }
                ;

                var request = requests[node];


                request.Items.Add(packedItem);

                if (request.Items.Count == packetSize)
                {
                    var task = Task.Factory.StartNew(re =>
                    {
                        var put   = (PutRequest)re;
                        var split = put.SplitWithMaxSize();

                        foreach (var putRequest in split)
                        {
                            var response =
                                CacheClients[node].Channel.SendRequest(putRequest);
                            if (response is ExceptionResponse exResponse)
                            {
                                throw new CacheException(
                                    "Error while writing an object to the cache",
                                    exResponse.Message, exResponse.CallStack);
                            }
                        }
                    }, request);


                    tasks.Add(task);
                    requests[node] = new PutRequest(typeName)
                    {
                        ExcludeFromEviction = excludeFromEviction,
                        SessionId           = sessionId
                    };
                }
            }


            //send the last packet left for each node
            for (var node = 0; node < CacheClients.Count; node++)
            {
                var request = requests[node];


                if (request != null)
                {
                    request.EndOfSession = true;

                    var n    = node;
                    var task = Task.Factory.StartNew(re =>
                    {
                        var response = CacheClients[n].Channel.SendRequest((PutRequest)re);
                        if (response is ExceptionResponse exResponse)
                        {
                            throw new CacheException(
                                "Error while writing an object to the cache",
                                exResponse.Message, exResponse.CallStack);
                        }
                    }, request);

                    tasks.Add(task);
                }
            }

            try
            {
                Task.WaitAll(tasks.ToArray());
                Dbg.Trace($"{tasks.Count} tasks finished");
            }
            catch (AggregateException e)
            {
                if (e.InnerException != null)
                {
                    throw e.InnerException;
                }
            }
        }
        /// <summary>
        ///     Factory method used to create a precompiled type description.
        ///     This version of the method uses an external description (no need to attach attributes to the public properties)
        ///     In order to be cacheable, a type must be serializable and must have exactly one primary key
        ///     Optionally it can have multiple unique keys and index keys
        /// </summary>
        /// <param name="type"> </param>
        /// <param name="typeDescription"> </param>
        /// <returns> </returns>
        public static ClientSideTypeDescription RegisterType(Type type, TypeDescriptionConfig typeDescription)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (!type.IsSerializable)
            {
                throw new NotSupportedException(
                          $"the type {type} is not serializable so it can not be registered as cacheable type");
            }

            var result = new ClientSideTypeDescription
            {
                TypeName       = type.Name,
                FullTypeName   = type.FullName,
                UseCompression = typeDescription.UseCompression
            };

            var props = type.GetProperties();

            foreach (var info in props)
            {
                if (typeDescription.Keys.ContainsKey(info.Name))
                {
                    var propertyDescription = typeDescription.Keys[info.Name];

                    result._propertyDescriptionByName[info.Name] = propertyDescription;

                    var key = new ClientSideKeyInfo(info, propertyDescription);

                    if (key.KeyType == KeyType.None)
                    {
                        continue;
                    }

                    if (key.KeyType == KeyType.Primary)
                    {
                        result.PrimaryKeyField = key;
                    }
                    else if (key.KeyType == KeyType.Unique)
                    {
                        result._uniqueKeyFields.Add(key);
                    }
                    else if (key.KeyType == KeyType.ScalarIndex)
                    {
                        result._indexFields.Add(key);
                    }
                    else if (key.KeyType == KeyType.ListIndex)
                    {
                        result._listFields.Add(key);
                    }
                }
            }

            //check if the newly registered type is valid
            if (result.PrimaryKeyField == null)
            {
                throw new NotSupportedException($"no primary key defined for type {type}");
            }

            return(result);
        }
        /// <summary>
        ///     Factory method used to create a precompiled type description.
        ///     This version of the method uses a tagged type ( Attributes are attached to the public properties
        ///     which are indexed in the cache)
        ///     In order to be cacheable, a type must be serializable and must have exactly one primary key
        ///     Optionally it can have multiple unique keys and index keys
        /// </summary>
        /// <param name="type"> type to register (must be properly decorated) </param>
        /// <returns> not null type description if successful </returns>
        public static ClientSideTypeDescription RegisterType(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }


            var useCompression = false;
            var storage        = type.GetCustomAttributes(typeof(StorageAttribute), false).FirstOrDefault();

            if (storage != null)
            {
                var storageParams = (StorageAttribute)storage;
                useCompression = storageParams.UseCompression;
            }

            var result = new ClientSideTypeDescription
            {
                UseCompression = useCompression,
                TypeName       = type.Name,
                FullTypeName   = type.FullName
            };

            var props = type.GetProperties();

            foreach (var info in props)
            {
                var key = new ClientSideKeyInfo(info);

                if (key.KeyType == KeyType.None)
                {
                    continue;
                }

                if (key.KeyType == KeyType.Primary)
                {
                    result.PrimaryKeyField = key;
                }
                else if (key.KeyType == KeyType.Unique)
                {
                    result._uniqueKeyFields.Add(key);
                }
                else if (key.KeyType == KeyType.ScalarIndex)
                {
                    result._indexFields.Add(key);
                }
                else if (key.KeyType == KeyType.ListIndex)
                {
                    result._listFields.Add(key);
                }


                var propertyDescription = new PropertyDescription
                {
                    PropertyName = key.Name,
                    KeyDataType  = key.KeyDataType,
                    KeyType      = key.KeyType,
                    Ordered      = key.IsOrdered
                };

                result._propertyDescriptionByName[key.Name] = propertyDescription;
            }

            //check if the newly registered type is valid
            if (result.PrimaryKeyField == null)
            {
                throw new NotSupportedException($"No primary key defined for type {type}");
            }

            return(result);
        }