Esempio n. 1
0
        public async Task UnregisterAsync(GrainAddress address, UnregistrationCause cause, int hopCount)
        {
            (hopCount > 0 ? UnregistrationsRemoteReceived : unregistrationsIssued).Increment();

            if (hopCount == 0)
            {
                InvalidateCacheEntry(address);
            }

            // see if the owner is somewhere else (returns null if we are owner)
            var forwardaddress = this.CheckIfShouldForward(address.GrainId, hopCount, "UnregisterAsync");

            // on all silos other than first, we insert a retry delay and recheck owner before forwarding
            if (hopCount > 0 && forwardaddress != null)
            {
                await Task.Delay(RETRY_DELAY);

                forwardaddress = this.CheckIfShouldForward(address.GrainId, hopCount, "UnregisterAsync");
                this.log.LogWarning($"UnregisterAsync - It seems we are not the owner of activation {address}, trying to forward it to {forwardaddress} (hopCount={hopCount})");
            }

            if (forwardaddress == null)
            {
                // we are the owner
                UnregistrationsLocal.Increment();

                DirectoryPartition.RemoveActivation(address.GrainId, address.ActivationId, cause);
            }
            else
            {
                UnregistrationsRemoteSent.Increment();
                // otherwise, notify the owner
                await GetDirectoryReference(forwardaddress).UnregisterAsync(address, cause, hopCount + 1);
            }
        }
Esempio n. 2
0
        // used for testing to (carefully!) allow two clients in the same process
        private async Task StartInternal(CancellationToken cancellationToken)
        {
            var retryFilter = ServiceProvider.GetService <IClientConnectionRetryFilter>();

            var gatewayManager = this.ServiceProvider.GetRequiredService <GatewayManager>();

            await ExecuteWithRetries(
                async() => await gatewayManager.StartAsync(cancellationToken),
                retryFilter,
                cancellationToken);

            var generation = -SiloAddress.AllocateNewGeneration(); // Client generations are negative

            MessageCenter = ActivatorUtilities.CreateInstance <ClientMessageCenter>(this.ServiceProvider, localAddress, generation, clientId);
            MessageCenter.RegisterLocalMessageHandler(this.HandleMessage);
            await ExecuteWithRetries(
                async() => await MessageCenter.StartAsync(cancellationToken),
                retryFilter,
                cancellationToken);

            CurrentActivationAddress = GrainAddress.NewActivationAddress(MessageCenter.MyAddress, clientId.GrainId);

            this.gatewayObserver = new ClientGatewayObserver(gatewayManager);
            this.InternalGrainFactory.CreateObjectReference <IClientGatewayObserver>(this.gatewayObserver);

            await ExecuteWithRetries(
                async() => await this.ServiceProvider.GetRequiredService <ClientClusterManifestProvider>().StartAsync(),
                retryFilter,
                cancellationToken);

            ClientStatistics.Start(MessageCenter, clientId.GrainId);
Esempio n. 3
0
        private GrainAddress GenerateActivationAddress()
        {
            var grainId  = LegacyGrainId.GetGrainIdForTesting(Guid.NewGuid());
            var siloAddr = SiloAddress.New(new IPEndPoint(IPAddress.Loopback, 5000), ++generation);

            return(GrainAddress.NewActivationAddress(siloAddr, grainId));
        }
Esempio n. 4
0
        // used for testing to (carefully!) allow two clients in the same process
        private async Task StartInternal(CancellationToken cancellationToken)
        {
            var retryFilterInterface = ServiceProvider.GetService <IClientConnectionRetryFilter>();
            Func <Exception, CancellationToken, Task <bool> > retryFilter = RetryFilter;

            var gatewayManager = this.ServiceProvider.GetRequiredService <GatewayManager>();

            await ExecuteWithRetries(async() => await gatewayManager.StartAsync(cancellationToken), retryFilter);

            var generation = -SiloAddress.AllocateNewGeneration(); // Client generations are negative

            MessageCenter = ActivatorUtilities.CreateInstance <ClientMessageCenter>(this.ServiceProvider, localAddress, generation, clientId);
            MessageCenter.RegisterLocalMessageHandler(this.HandleMessage);
            MessageCenter.Start();
            CurrentActivationAddress = GrainAddress.NewActivationAddress(MessageCenter.MyAddress, clientId.GrainId);

            this.gatewayObserver = new ClientGatewayObserver(gatewayManager);
            this.InternalGrainFactory.CreateObjectReference <IClientGatewayObserver>(this.gatewayObserver);

            await ExecuteWithRetries(
                async() => await this.ServiceProvider.GetRequiredService <ClientClusterManifestProvider>().StartAsync(),
                retryFilter);

            ClientStatistics.Start(MessageCenter, clientId.GrainId);

            async Task ExecuteWithRetries(Func <Task> task, Func <Exception, CancellationToken, Task <bool> > shouldRetry)
            {
                while (true)
                {
                    try
                    {
                        await task();

                        return;
                    }
                    catch (Exception exception) when(shouldRetry != null)
                    {
                        var retry = await shouldRetry(exception, cancellationToken);

                        if (!retry)
                        {
                            throw;
                        }
                    }
                }
            }

            async Task <bool> RetryFilter(Exception exception, CancellationToken cancellationToken)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(false);
                }

                if (retryFilterInterface is { })
                {
                    var result = await retryFilterInterface.ShouldRetryConnectionAttempt(exception, cancellationToken);

                    return(result && !cancellationToken.IsCancellationRequested);
                }
Esempio n. 5
0
        public async Task DoNotOverrideEntry()
        {
            var expected = new GrainAddress
            {
                ActivationId = ActivationId.NewId(),
                GrainId      = GrainId.Parse("user/someraondomuser_" + Guid.NewGuid().ToString("N")),
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
            };

            var differentActivation = new GrainAddress
            {
                ActivationId = ActivationId.NewId(),
                GrainId      = expected.GrainId,
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
            };

            var differentSilo = new GrainAddress
            {
                ActivationId = expected.ActivationId,
                GrainId      = expected.GrainId,
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.14:1000@4583")
            };

            Assert.Equal(expected, await this.grainDirectory.Register(expected));
            Assert.Equal(expected, await this.grainDirectory.Register(differentActivation));
            Assert.Equal(expected, await this.grainDirectory.Register(differentSilo));

            Assert.Equal(expected, await this.grainDirectory.Lookup(expected.GrainId));
        }
Esempio n. 6
0
        public bool TryLookupInCache(GrainId grainId, out GrainAddress address)
        {
            var grainType = grainId.Type;

            if (grainType.IsClient() || grainType.IsSystemTarget())
            {
                ThrowUnsupportedGrainType(grainId);
            }

            if (this.cache.LookUp(grainId, out address, out var version))
            {
                // If the silo is dead, remove the entry
                if (IsKnownDeadSilo(address.SiloAddress, new MembershipVersion(version)))
                {
                    address = default;
                    this.cache.Remove(grainId);
                }
                else
                {
                    // Entry found and valid -> return it
                    return(true);
                }
            }

            return(false);
        }
Esempio n. 7
0
        public async Task <GrainAddress> Register(GrainAddress address)
        {
            var grainType = address.GrainId.Type;

            if (grainType.IsClient() || grainType.IsSystemTarget())
            {
                ThrowUnsupportedGrainType(address.GrainId);
            }

            address.MembershipVersion = this.clusterMembershipService.CurrentSnapshot.Version;

            var result = await GetGrainDirectory(grainType).Register(address);

            // Check if the entry point to a dead silo
            if (IsKnownDeadSilo(result))
            {
                // Remove outdated entry and retry to register
                await GetGrainDirectory(grainType).Unregister(result);

                result = await GetGrainDirectory(grainType).Register(address);
            }

            // Cache update
            this.cache.AddOrUpdate(result, (int)result.MembershipVersion.Value);

            return(result);
        }
Esempio n. 8
0
 public static ActivationAddress ToActivationAddress(this GrainAddress addr)
 {
     return(ActivationAddress.GetAddress(
                addr.SiloAddress,
                addr.GrainId,
                ActivationId.GetActivationId(UniqueKey.Parse(addr.ActivationId.AsSpan()))));
 }
Esempio n. 9
0
 public static ActivationAddress ToActivationAddress(this GrainAddress addr)
 {
     return(ActivationAddress.GetAddress(
                SiloAddress.FromParsableString(addr.SiloAddress),
                GrainId.FromParsableString(addr.GrainId),
                ActivationId.GetActivationId(UniqueKey.Parse(addr.ActivationId.AsSpan()))));
 }
        public void AddOrUpdate(GrainAddress value, int version)
        {
            var entry = new GrainDirectoryCacheEntry(value, version, initialExpirationTimer);

            // Notice that LRU should know how to throw the oldest entry if the cache is full
            cache.Add(value.GrainId, entry);
        }
Esempio n. 11
0
        public async Task <GrainAddress> Register(GrainAddress address)
        {
            var entry  = GrainDirectoryEntity.FromGrainAddress(this.clusterId, address);
            var result = await this.tableDataManager.InsertTableEntryAsync(entry);

            // Possible race condition?
            return(result.isSuccess ? address : await Lookup(address.GrainId));
        }
 internal GrainDirectoryCacheEntry(GrainAddress value, int etag, TimeSpan expirationTimer)
 {
     Address         = value;
     ETag            = etag;
     ExpirationTimer = expirationTimer;
     LastRefreshed   = CoarseStopwatch.StartNew();
     NumAccesses     = 0;
 }
Esempio n. 13
0
 public static GrainDirectoryEntity FromGrainAddress(string clusterId, GrainAddress address)
 {
     return(new GrainDirectoryEntity
     {
         PartitionKey = clusterId,
         RowKey = GrainIdToRowKey(address.GrainId),
         SiloAddress = address.SiloAddress.ToParsableString(),
         ActivationId = address.ActivationId.ToParsableString(),
     });
 }
Esempio n. 14
0
 public static GrainDirectoryEntity FromGrainAddress(string clusterId, GrainAddress address)
 {
     return(new GrainDirectoryEntity
     {
         PartitionKey = clusterId,
         RowKey = HttpUtility.UrlEncode(address.GrainId, Encoding.UTF8),
         SiloAddress = address.SiloAddress,
         ActivationId = address.ActivationId,
     });
 }
Esempio n. 15
0
        public async Task <AddressAndTag> RegisterAsync(GrainAddress address, int hopCount)
        {
            var counterStatistic = hopCount > 0 ? this.RegistrationsSingleActRemoteReceived : this.registrationsSingleActIssued;

            counterStatistic.Increment();

            // see if the owner is somewhere else (returns null if we are owner)
            var forwardAddress = this.CheckIfShouldForward(address.GrainId, hopCount, "RegisterAsync");

            // on all silos other than first, we insert a retry delay and recheck owner before forwarding
            if (hopCount > 0 && forwardAddress != null)
            {
                await Task.Delay(RETRY_DELAY);

                forwardAddress = this.CheckIfShouldForward(address.GrainId, hopCount, "RegisterAsync");
                if (forwardAddress is object)
                {
                    int hash = unchecked ((int)address.GrainId.GetUniformHashCode());
                    this.log.LogWarning($"RegisterAsync - It seems we are not the owner of activation {address} (hash: {hash:X}), trying to forward it to {forwardAddress} (hopCount={hopCount})");
                }
            }

            if (forwardAddress == null)
            {
                RegistrationsSingleActLocal.Increment();

                var result = DirectoryPartition.AddSingleActivation(address);
                return(result);
            }
            else
            {
                RegistrationsSingleActRemoteSent.Increment();

                // otherwise, notify the owner
                AddressAndTag result = await GetDirectoryReference(forwardAddress).RegisterAsync(address, hopCount + 1);

                // Caching optimization:
                // cache the result of a successfull RegisterSingleActivation call, only if it is not a duplicate activation.
                // this way next local lookup will find this ActivationAddress in the cache and we will save a full lookup!
                if (result.Address == null)
                {
                    return(result);
                }

                if (!address.Equals(result.Address) || !IsValidSilo(address.SiloAddress))
                {
                    return(result);
                }

                // update the cache so next local lookup will find this ActivationAddress in the cache and we will save full lookup.
                DirectoryCache.AddOrUpdate(address, result.VersionTag);

                return(result);
            }
        }
Esempio n. 16
0
 public async Task Unregister(GrainAddress address, UnregistrationCause cause)
 {
     try
     {
         await GetGrainDirectory(address.GrainId.Type).Unregister(address);
     }
     finally
     {
         this.cache.Remove(address);
     }
 }
Esempio n. 17
0
        public Task UnregisterAfterNonexistingActivation(GrainAddress addr, SiloAddress origin)
        {
            log.Trace("UnregisterAfterNonexistingActivation addr={0} origin={1}", addr, origin);

            if (origin == null || this.directoryMembership.MembershipCache.Contains(origin))
            {
                // the request originated in this cluster, call unregister here
                return(UnregisterAsync(addr, UnregistrationCause.NonexistentActivation, 0));
            }
            else
            {
                // the request originated in another cluster, call unregister there
                var remoteDirectory = GetDirectoryReference(origin);
                return(remoteDirectory.UnregisterAsync(addr, UnregistrationCause.NonexistentActivation));
            }
        }
Esempio n. 18
0
        public async Task RegisterLookupUnregisterLookup()
        {
            var expected = new GrainAddress
            {
                ActivationId = ActivationId.NewId(),
                GrainId      = GrainId.Parse("user/someraondomuser_" + Guid.NewGuid().ToString("N")),
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
            };

            Assert.Equal(expected, await this.grainDirectory.Register(expected));

            Assert.Equal(expected, await this.grainDirectory.Lookup(expected.GrainId));

            await this.grainDirectory.Unregister(expected);

            Assert.Null(await this.grainDirectory.Lookup(expected.GrainId));
        }
Esempio n. 19
0
        public async Task Unregister(GrainAddress address)
        {
            var result = await this.tableDataManager.ReadSingleTableEntryAsync(this.clusterId, HttpUtility.UrlEncode(address.GrainId, Encoding.UTF8));

            // No entry found
            if (result == null)
            {
                return;
            }

            // Check if the entry in storage match the one we were asked to delete
            var entity = result.Item1;

            if (entity.ActivationId == address.ActivationId)
            {
                await this.tableDataManager.DeleteTableEntryAsync(GrainDirectoryEntity.FromGrainAddress(this.clusterId, address), entity.ETag);
            }
        }
Esempio n. 20
0
        public async Task Unregister(GrainAddress address)
        {
            var result = await this.tableDataManager.ReadSingleTableEntryAsync(this.clusterId, GrainDirectoryEntity.GrainIdToRowKey(address.GrainId));

            // No entry found
            if (result.Entity is null)
            {
                return;
            }

            // Check if the entry in storage match the one we were asked to delete
            var entity = result.Item1;

            if (entity.ActivationId == address.ActivationId.ToParsableString())
            {
                await this.tableDataManager.DeleteTableEntryAsync(GrainDirectoryEntity.FromGrainAddress(this.clusterId, address), entity.ETag.ToString());
            }
        }
Esempio n. 21
0
        public async Task UnregisterMany()
        {
            const int N = 250;
            const int R = 40;

            // Create and insert N entries
            var addresses = new List <GrainAddress>();

            for (var i = 0; i < N; i++)
            {
                var addr = new GrainAddress
                {
                    ActivationId = ActivationId.NewId(),
                    GrainId      = GrainId.Parse("user/someraondomuser_" + Guid.NewGuid().ToString("N")),
                    SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
                };
                addresses.Add(addr);
                await this.grainDirectory.Register(addr);
            }

            // Modify the Rth entry locally, to simulate another activation tentative by another silo
            var oldActivation = addresses[R].ActivationId;

            addresses[R].ActivationId = ActivationId.NewId();

            // Batch unregister
            await this.grainDirectory.UnregisterMany(addresses);

            // Now we should only find the old Rth entry
            for (int i = 0; i < N; i++)
            {
                if (i == R)
                {
                    var addr = await this.grainDirectory.Lookup(addresses[i].GrainId);

                    Assert.NotNull(addr);
                    Assert.Equal(oldActivation, addr.ActivationId);
                }
                else
                {
                    Assert.Null(await this.grainDirectory.Lookup(addresses[i].GrainId));
                }
            }
        }
        public bool LookUp(GrainId key, out GrainAddress result, out int version)
        {
            NumAccesses++;      // for stats

            // Here we do not check whether the found entry is expired.
            // It will be done by the thread managing the cache.
            // This is to avoid situation where the entry was just expired, but the manager still have not run and have not refereshed it.
            if (!cache.TryGetValue(key, out var tmp))
            {
                result  = default;
                version = default;
                return(false);
            }

            NumHits++;      // for stats
            tmp.NumAccesses++;
            result  = tmp.Address;
            version = tmp.ETag;
            return(true);
        }
Esempio n. 23
0
        public async Task DoNotDeleteDifferentActivationIdEntry()
        {
            var expected = new GrainAddress
            {
                ActivationId = ActivationId.NewId(),
                GrainId      = GrainId.Parse("user/someraondomuser_" + Guid.NewGuid().ToString("N")),
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
            };

            var otherEntry = new GrainAddress
            {
                ActivationId = ActivationId.NewId(),
                GrainId      = expected.GrainId,
                SiloAddress  = SiloAddress.FromParsableString("10.0.23.12:1000@5678")
            };

            Assert.Equal(expected, await this.grainDirectory.Register(expected));
            await this.grainDirectory.Unregister(otherEntry);

            Assert.Equal(expected, await this.grainDirectory.Lookup(expected.GrainId));
        }
Esempio n. 24
0
 /// <summary>
 /// Looks up the cached value by the given key.
 /// </summary>
 /// <param name="cache">grain directory cache to look up results from</param>
 /// <param name="key">key for the lookup</param>
 /// <param name="result">value if the key is found, undefined otherwise</param>
 /// <returns>true if the given key is in the cache</returns>
 public static bool LookUp(this IGrainDirectoryCache cache, GrainId key, out GrainAddress result)
 {
     return(cache.LookUp(key, out result, out _));
 }
Esempio n. 25
0
 public bool TryCachedLookup(GrainId grainId, out GrainAddress address) => (address = GetLocalCacheData(grainId)) is not null;
Esempio n. 26
0
 public GrainAddress TryAddSingleActivation(GrainAddress address)
 {
     if (Activation is { } existing)
     {
         return(existing);
     }
Esempio n. 27
0
 public void InvalidateCacheEntry(GrainAddress activationAddress)
 {
     DirectoryCache.Remove(activationAddress);
 }
Esempio n. 28
0
        public async Task <AddressAndTag> RegisterAsync(GrainAddress address, int hopCount)
        {
            router.RegistrationsSingleActRemoteReceived.Increment();

            return(await router.RegisterAsync(address, hopCount));
        }
Esempio n. 29
0
 public Task UnregisterAsync(GrainAddress address, UnregistrationCause cause, int hopCount)
 {
     return(router.UnregisterAsync(address, cause, hopCount));
 }
Esempio n. 30
0
        private GrainAddress SelectAddress(List <GrainAddress> results, GrainId grainId)
        {
            GrainAddress unadjustedResult = null;

            if (results is { Count : > 0 })