/// <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); }
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); }
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); }
public ClientSideTypeDescription RegisterTypeIfNeeded(Type type, TypeDescriptionConfig description) { ClientSideTypeDescription result = null; foreach (var client in CacheClients) { result = client.RegisterTypeIfNeeded(type, description); } return(result); }
public ClientSideTypeDescription RegisterTypeIfNeeded(Type type) { ClientSideTypeDescription description = null; foreach (var client in CacheClients) { description = client.RegisterTypeIfNeeded(type); } return(description); }
public ClientSideTypeDescription RegisterType(Type type, bool forceReindex = false) { ClientSideTypeDescription description = null; Parallel.ForEach(CacheClients, client => { description = client.RegisterType(type, forceReindex); }); return(description); }
/// <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); }
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); } } } } }
/// <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); }