コード例 #1
0
        /// <summary>
        /// Gets or creates a distributed object.
        /// </summary>
        /// <typeparam name="T">The type of the distributed object.</typeparam>
        /// <typeparam name="TImpl">The type of the implementation.</typeparam>
        /// <param name="serviceName">The unique name of the service.</param>
        /// <param name="name">The unique name of the object.</param>
        /// <param name="remote">Whether to create the object remotely too.</param>
        /// <param name="factory">The object factory.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>The distributed object.</returns>
        public async Task <T> GetOrCreateAsync <T, TImpl>(
            string serviceName, string name, bool remote,
            Func <string, DistributedObjectFactory, Cluster, SerializationService, ILoggerFactory, TImpl> factory,
            CancellationToken cancellationToken = default)
            where TImpl : DistributedObjectBase, T
        {
            if (_disposed == 1)
            {
                throw new ObjectDisposedException("DistributedObjectFactory");
            }

            var info = new DistributedObjectInfo(serviceName, name);

            async ValueTask <DistributedObjectBase> CreateAsync(DistributedObjectInfo info2, CancellationToken token)
            {
                var x = factory(name, this, _cluster, _serializationService, _loggerFactory);

                x.ObjectDisposed = OnObjectDisposed; // this is why is has to be DistributedObjectBase

                // initialize the object
                if (remote)
                {
                    var requestMessage = ClientCreateProxyCodec.EncodeRequest(x.Name, x.ServiceName);
                    _ = await _cluster.Messaging.SendAsync(requestMessage, token).CfAwait();
                }

                x.OnInitialized();
                _logger.LogDebug("Initialized ({Object}) distributed object.", info2);
                return(x);
            }

            // try to get the object - thanks to the concurrent dictionary there will be only 1 task
            // and if several concurrent requests are made, they will all await that same task

            var o = await _objects.GetOrAddAsync(info, CreateAsync, cancellationToken).CfAwait();

            // race condition: maybe the factory has been disposed and is already disposing
            // objects and will ignore this new object even though it has been added to the
            // dictionary, so take care of it ourselves
            if (_disposed == 1)
            {
                await o.DisposeAsync().CfAwait();

                throw new ObjectDisposedException("DistributedObjectFactory");
            }

            // if the object is a T then we can return it
            if (o is T t)
            {
                return(t);
            }

            // otherwise, the client was already used to retrieve an object with the specified service
            // name and object name, but a different type, for instance IHList<int> vs IHList<string>,
            // and we just cannot support this = throw

            throw new HazelcastException($"A distributed object with the specified service name ({serviceName}) " +
                                         $"and object name ({name}) exists but of type {o.GetType().ToCsString()}, " +
                                         $"instead of {typeof(T).ToCsString()}.");
        }
コード例 #2
0
 public async ValueTask <NearCache> GetOrCreateNearCacheAsync(string name, NearCacheOptions options, CancellationToken cancellationToken = default)
 {
     return(await _caches.GetOrAddAsync(name, async (n, token) =>
     {
         var nearCache = new NearCache(n, _cluster, _serializationService, _loggerFactory, options, GetMaxToleratedMissCount());
         await InitializeNearCache(nearCache).CfAwait();
         return nearCache;
     }, cancellationToken).CfAwait());
 }
コード例 #3
0
        /// <summary>
        /// Gets or creates a distributed object.
        /// </summary>
        /// <typeparam name="T">The type of the distributed object.</typeparam>
        /// <typeparam name="TImpl">The type of the implementation.</typeparam>
        /// <param name="serviceName">The unique name of the service.</param>
        /// <param name="name">The unique name of the object.</param>
        /// <param name="remote">Whether to create the object remotely too.</param>
        /// <param name="factory">The object factory.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>The distributed object.</returns>
        public async Task <T> GetOrCreateAsync <T, TImpl>(
            string serviceName, string name, bool remote,
            Func <string, DistributedObjectFactory, Cluster, ISerializationService, ILoggerFactory, TImpl> factory,
            CancellationToken cancellationToken = default)
            where TImpl : DistributedObjectBase, T
        {
            if (_disposed == 1)
            {
                throw new ObjectDisposedException("DistributedObjectFactory");
            }
            await _cluster.ThrowIfNotConnected().CAF();

            var k = new DistributedObjectInfo(serviceName, name);

            async ValueTask <DistributedObjectBase> CreateAsync(DistributedObjectInfo info, CancellationToken token)
            {
                var x = factory(name, this, _cluster, _serializationService, _loggerFactory);

                x.OnDispose = ObjectDisposed; // this is why is has to be DistributedObjectBase

                // initialize the object
                if (remote)
                {
                    var requestMessage = ClientCreateProxyCodec.EncodeRequest(x.Name, x.ServiceName);
                    _ = await _cluster.Messaging.SendAsync(requestMessage, token).CAF();
                }

                x.OnInitialized();
                _logger.LogDebug("Initialized '{ServiceName}/{Name}' distributed object.", info.ServiceName, info.Name);
                return(x);
            }

            // try to get the object - thanks to the concurrent dictionary there will be only 1 task
            // and if several concurrent requests are made, they will all await that same task

            var o = await _objects.GetOrAddAsync(k, CreateAsync, cancellationToken).CAF();

            // race condition: maybe the factory has been disposed and is already disposing
            // objects and will ignore this new object even though it has been added to the
            // dictionary, so take care of it ourselves
            if (_disposed == 1)
            {
                await o.DisposeAsync().CAF();

                throw new ObjectDisposedException("DistributedObjectFactory");
            }

            if (o is T t)
            {
                return(t);
            }

            // if the object that was retrieved is not of the right type, it's a problem
            // preserve the existing object, but throw
            throw new InvalidCastException("A distributed object with the specified service name and name, but "
                                           + "with a different type, has already been created.");
        }
        public async Task Test()
        {
            var d = new ConcurrentAsyncDictionary <string, ValueItem <int> >();
            var i = 0;

            ValueTask <ValueItem <int> > Factory(string key, CancellationToken _)
            {
                i++;
                return(new ValueTask <ValueItem <int> >(new ValueItem <int>(2)));
            }

            var v = await d.GetOrAddAsync("a", Factory);

            Assert.AreEqual(2, v.Value);
            Assert.AreEqual(1, i);

            v = await d.GetOrAddAsync("a", Factory);

            Assert.AreEqual(2, v.Value);
            Assert.AreEqual(1, i);

            var entries = new List <string>();

            await foreach (var(key, value) in d)
            {
                entries.Add($"{key}:{value}");
            }

            Assert.AreEqual(1, entries.Count);
            Assert.AreEqual("a:2", entries[0]);

            var(hasValue, gotValue) = await d.TryGetAsync("a");

            Assert.IsTrue(hasValue);
            Assert.AreEqual(2, gotValue.Value);

            Assert.IsTrue(d.TryRemove("a"));
            Assert.IsFalse(d.TryRemove("a"));

            (hasValue, _) = await d.TryGetAsync("a");

            Assert.IsFalse(hasValue);
コード例 #5
0
        /// <summary>
        /// Tries to get a value from, or add a value to, the cache.
        /// </summary>
        /// <param name="keyData">The key data.</param>
        /// <param name="valueFactory">A factory that accepts the key data and returns the value data.</param>
        /// <returns>An attempt at getting or adding a value to the cache.</returns>
        public async Task <Attempt <object> > TryGetOrAddAsync(IData keyData, Func <IData, Task <IData> > valueFactory)
        {
            // if it's in the cache already, return it
            // (and TryGetAsync counts a hit)
            // (otherwise, TryGetAsync counts a miss, so we don't have to do it here)
            var(hasEntry, valueObject) = await TryGetAsync(keyData).CfAwait();

            if (hasEntry)
            {
                return(valueObject);
            }

            // kick eviction policy if needed
            if (_evictionPolicy != EvictionPolicy.None && _entries.Count >= _maxSize)
            {
                await EvictEntries().CfAwait();
            }

            // if the cache is full, directly return the un-cached value
            if (_evictionPolicy == EvictionPolicy.None && _entries.Count >= _maxSize && !await _entries.ContainsKeyAsync(keyData).CfAwait())
            {
                return(Attempt.Fail(ToCachedValue(await valueFactory(keyData).CfAwait())));
            }

            async ValueTask <NearCacheEntry> CreateEntry(IData _, CancellationToken __)
            {
                var valueData = await valueFactory(keyData).CfAwait();

                var cachedValue = ToCachedValue(valueData);

                return(CreateCacheEntry(keyData, cachedValue)); // null if cachedValue is null
            }

            var entry = await _entries.GetOrAddAsync(keyData, CreateEntry).CfAwait();

            if (entry != null) // null if ValueObject would have been null
            {
                Statistics.NotifyEntryAdded();
                return(entry.ValueObject);
            }

            // the entry will not stick in _entries
            // and we haven't notified statistics

            return(Attempt.Failed);
        }