Example #1
0
        public Task DeleteGrain(GrainId grain)
        {
            SiloAddress silo = CalculateTargetSilo(grain);

            if (silo == null)
            {
                // We don't know about any other silos, and we're stopping, so throw
                throw new InvalidOperationException("Grain directory is stopping");
            }

            if (log.IsVerbose)
            {
                log.Verbose("Silo {0} tries to lookup for {1}-->{2} ({3}-->{4})", MyAddress, grain, silo, grain.GetUniformHashCode(), silo.GetConsistentHashCode());
            }

            if (silo.Equals(MyAddress))
            {
                // remove from our partition
                DirectoryPartition.RemoveGrain(grain);
                return(TaskDone.Done);
            }

            // remove from the cache
            DirectoryCache.Remove(grain);

            // send request to the owner
            return(GetDirectoryReference(silo).DeleteGrain(grain, NUM_RETRIES));
        }
Example #2
0
        public List <ActivationAddress> GetLocalDirectoryData(GrainId grain)
        {
            var result = DirectoryPartition.LookUpGrain(grain);

            return(result == null ? null :
                   result.Item1.Select(t => ActivationAddress.GetAddress(t.Item1, grain, t.Item2)).Where(addr => IsValidSilo(addr.Silo)).ToList());
        }
Example #3
0
        private Task UnregisterAsyncImpl(ActivationAddress addr, bool force)
        {
            unregistrationsIssued.Increment();
            SiloAddress owner = CalculateTargetSilo(addr.Grain);

            if (owner == null)
            {
                // We don't know about any other silos, and we're stopping, so throw
                throw new InvalidOperationException("Grain directory is stopping");
            }

            if (log.IsVerbose)
            {
                log.Verbose("Silo {0} is going to unregister grain {1}-->{2} ({3}-->{4})", MyAddress, addr.Grain, owner, addr.Grain.GetUniformHashCode(), owner.GetConsistentHashCode());
            }

            InvalidateCacheEntry(addr);
            if (owner.Equals(MyAddress))
            {
                UnregistrationsLocal.Increment();
                // if I am the owner, remove the old activation locally
                DirectoryPartition.RemoveActivation(addr.Grain, addr.Activation, force);
                return(TaskDone.Done);
            }

            UnregistrationsRemoteSent.Increment();
            // otherwise, notify the owner
            return(GetDirectoryReference(owner).Unregister(addr, force, NUM_RETRIES));
        }
Example #4
0
        public Task UnregisterManyAsync(List <ActivationAddress> addresses)
        {
            unregistrationsManyIssued.Increment();
            return(Task.WhenAll(
                       addresses.GroupBy(a => CalculateTargetSilo(a.Grain))
                       .Select(g =>
            {
                if (g.Key == null)
                {
                    // We don't know about any other silos, and we're stopping, so throw
                    throw new InvalidOperationException("Grain directory is stopping");
                }

                foreach (var addr in g)
                {
                    InvalidateCacheEntry(addr);
                }

                if (MyAddress.Equals(g.Key))
                {
                    // if I am the owner, remove the old activation locally
                    foreach (var addr in g)
                    {
                        UnregistrationsLocal.Increment();
                        DirectoryPartition.RemoveActivation(addr.Grain, addr.Activation, true);
                    }
                    return TaskDone.Done;
                }

                UnregistrationsManyRemoteSent.Increment();
                // otherwise, notify the owner
                return GetDirectoryReference(g.Key).UnregisterMany(g.ToList(), NUM_RETRIES);
            })));
        }
Example #5
0
        public async Task DeleteGrainAsync(GrainId grainId, int hopCount)
        {
            // see if the owner is somewhere else (returns null if we are owner)
            var forwardAddress = this.CheckIfShouldForward(grainId, hopCount, "DeleteGrainAsync");

            // 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(grainId, hopCount, "DeleteGrainAsync");
                this.log.LogWarning($"DeleteGrainAsync - It seems we are not the owner of grain {grainId}, trying to forward it to {forwardAddress} (hopCount={hopCount})");
            }

            if (forwardAddress == null)
            {
                // we are the owner
                DirectoryPartition.RemoveGrain(grainId);
            }
            else
            {
                // otherwise, notify the owner
                DirectoryCache.Remove(grainId);
                await GetDirectoryReference(forwardAddress).DeleteGrainAsync(grainId, hopCount + 1);
            }
        }
Example #6
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);
            }
        }
Example #7
0
        /// <summary>
        /// Adjust local directory following the addition/removal of a silo
        /// </summary>
        protected void AdjustLocalDirectory(SiloAddress silo, bool dead)
        {
            // Determine which activations to remove.
            var activationsToRemove = new List <(GrainId, ActivationId)>();

            foreach (var entry in this.DirectoryPartition.GetItems())
            {
                var(grain, grainInfo) = (entry.Key, entry.Value);
                if (grainInfo.Activation is { } address)
                {
                    // Include any activations from dead silos and from predecessors.
                    var siloIsDead        = dead && address.SiloAddress.Equals(silo);
                    var siloIsPredecessor = address.SiloAddress.IsPredecessorOf(silo);
                    if (siloIsDead || siloIsPredecessor)
                    {
                        activationsToRemove.Add((grain, address.ActivationId));
                    }
                }
            }

            // Remove all defunct activations.
            foreach (var activation in activationsToRemove)
            {
                DirectoryPartition.RemoveActivation(activation.Item1, activation.Item2);
            }
        }
Example #8
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);
            }
        }
Example #9
0
        /// <summary>
        /// Adjust local directory following the removal of a silo by droping all activations located on the removed silo
        /// </summary>
        /// <param name="removedSilo"></param>
        protected void AdjustLocalDirectory(SiloAddress removedSilo)
        {
            var activationsToRemove = (from pair in DirectoryPartition.GetItems()
                                       from pair2 in pair.Value.Instances.Where(pair3 => pair3.Value.SiloAddress.Equals(removedSilo))
                                       select new Tuple <GrainId, ActivationId>(pair.Key, pair2.Key)).ToList();

            // drop all records of activations located on the removed silo
            foreach (var activation in activationsToRemove)
            {
                DirectoryPartition.RemoveActivation(activation.Item1, activation.Item2, true);
            }
        }
Example #10
0
        public async Task RegisterAsync(ActivationAddress address)
        {
            registrationsIssued.Increment();
            SiloAddress owner = CalculateTargetSilo(address.Grain);

            if (owner == null)
            {
                // We don't know about any other silos, and we're stopping, so throw
                throw new InvalidOperationException("Grain directory is stopping");
            }
            if (owner.Equals(MyAddress))
            {
                RegistrationsLocal.Increment();
                // if I am the owner, store the new activation locally
                DirectoryPartition.AddActivation(address.Grain, address.Activation, address.Silo);
            }
            else
            {
                RegistrationsRemoteSent.Increment();
                // otherwise, notify the owner
                int eTag = await GetDirectoryReference(owner).Register(address, NUM_RETRIES);

                if (IsValidSilo(address.Silo))
                {
                    // Caching optimization:
                    // cache the result of a successfull RegisterActivation 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!
                    IReadOnlyList <Tuple <SiloAddress, ActivationId> > cached;
                    if (!DirectoryCache.LookUp(address.Grain, out cached))
                    {
                        cached = new List <Tuple <SiloAddress, ActivationId> >(1)
                        {
                            Tuple.Create(address.Silo, address.Activation)
                        };
                    }
                    else
                    {
                        var newcached = new List <Tuple <SiloAddress, ActivationId> >(cached.Count + 1);
                        newcached.AddRange(cached);
                        newcached.Add(Tuple.Create(address.Silo, address.Activation));
                        cached = newcached;
                    }
                    // update the cache so next local lookup will find this ActivationAddress in the cache and we will save full lookup.
                    DirectoryCache.AddOrUpdate(address.Grain, cached, eTag);
                }
            }
        }
Example #11
0
        /// <summary>
        /// Registers a new activation, in single activation mode, with the directory service.
        /// If there is already an activation registered for this grain, then the new activation will
        /// not be registered and the address of the existing activation will be returned.
        /// Otherwise, the passed-in address will be returned.
        /// <para>This method must be called from a scheduler thread.</para>
        /// </summary>
        /// <param name="address">The address of the potential new activation.</param>
        /// <returns>The address registered for the grain's single activation.</returns>
        public async Task <ActivationAddress> RegisterSingleActivationAsync(ActivationAddress address)
        {
            registrationsSingleActIssued.Increment();
            SiloAddress owner = CalculateTargetSilo(address.Grain);

            if (owner == null)
            {
                // We don't know about any other silos, and we're stopping, so throw
                throw new InvalidOperationException("Grain directory is stopping");
            }
            if (owner.Equals(MyAddress))
            {
                RegistrationsSingleActLocal.Increment();
                // if I am the owner, store the new activation locally
                Tuple <ActivationAddress, int> returnedAddress = DirectoryPartition.AddSingleActivation(address.Grain, address.Activation, address.Silo);
                return(returnedAddress == null ? null : returnedAddress.Item1);
            }
            else
            {
                RegistrationsSingleActRemoteSent.Increment();
                // otherwise, notify the owner
                Tuple <ActivationAddress, int> returnedAddress = await GetDirectoryReference(owner).RegisterSingleActivation(address, NUM_RETRIES);

                // 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 (returnedAddress == null || returnedAddress.Item1 == null)
                {
                    return(null);
                }

                if (!address.Equals(returnedAddress.Item1) || !IsValidSilo(address.Silo))
                {
                    return(returnedAddress.Item1);
                }

                var cached = new List <Tuple <SiloAddress, ActivationId> >(1)
                {
                    Tuple.Create(address.Silo, address.Activation)
                };
                // update the cache so next local lookup will find this ActivationAddress in the cache and we will save full lookup.
                DirectoryCache.AddOrUpdate(address.Grain, cached, returnedAddress.Item2);
                return(returnedAddress.Item1);
            }
        }
Example #12
0
        // Note that this implementation stops processing directory change requests (Register, Unregister, etc.) when the Stop event is raised.
        // This means that there may be a short period during which no silo believes that it is the owner of directory information for a set of
        // grains (for update purposes), which could cause application requests that require a new activation to be created to time out.
        // The alternative would be to allow the silo to process requests after it has handed off its partition, in which case those changes
        // would receive successful responses but would not be reflected in the eventual state of the directory.
        // It's easy to change this, if we think the trade-off is better the other way.
        public void Stop()
        {
            // This will cause remote write requests to be forwarded to the silo that will become the new owner.
            // Requests might bounce back and forth for a while as membership stabilizes, but they will either be served by the
            // new owner of the grain, or will wind up failing. In either case, we avoid requests succeeding at this silo after we've
            // begun stopping, which could cause them to not get handed off to the new owner.

            //mark Running as false will exclude myself from CalculateGrainDirectoryPartition(grainId)
            Running = false;

            if (maintainer != null)
            {
                maintainer.Stop();
            }

            DirectoryPartition.Clear();
            DirectoryCache.Clear();
        }
Example #13
0
        // helper method to avoid code duplication inside UnregisterManyAsync
        private void UnregisterOrPutInForwardList(IEnumerable <GrainAddress> addresses, UnregistrationCause cause, int hopCount,
                                                  ref Dictionary <SiloAddress, List <GrainAddress> > forward, List <Task> tasks, string context)
        {
            foreach (var address in addresses)
            {
                // see if the owner is somewhere else (returns null if we are owner)
                var forwardAddress = this.CheckIfShouldForward(address.GrainId, hopCount, context);

                if (forwardAddress != null)
                {
                    AddToDictionary(ref forward, forwardAddress, address);
                }
                else
                {
                    // we are the owner
                    UnregistrationsLocal.Increment();

                    DirectoryPartition.RemoveActivation(address.GrainId, address.ActivationId, cause);
                }
            }
        }
Example #14
0
        public async Task <List <ActivationAddress> > FullLookup(GrainId grain)
        {
            fullLookups.Increment();

            SiloAddress silo = CalculateTargetSilo(grain, false);

            // No need to check that silo != null since we're passing excludeThisSiloIfStopping = false

            if (log.IsVerbose)
            {
                log.Verbose("Silo {0} fully lookups for {1}-->{2} ({3}-->{4})", MyAddress, grain, silo, grain.GetUniformHashCode(), silo.GetConsistentHashCode());
            }

            // We assyme that getting here means the grain was not found locally (i.e., in TryFullLookup()).
            // We still check if we own the grain locally to avoid races between the time TryFullLookup() and FullLookup() were called.
            if (silo.Equals(MyAddress))
            {
                LocalDirectoryLookups.Increment();
                var localResult = DirectoryPartition.LookUpGrain(grain);
                if (localResult == null)
                {
                    // it can happen that we cannot find the grain in our partition if there were
                    // some recent changes in the membership
                    if (log.IsVerbose2)
                    {
                        log.Verbose2("FullLookup mine {0}=none", grain);
                    }
                    return(new List <ActivationAddress>());
                }
                var a = localResult.Item1.Select(t => ActivationAddress.GetAddress(t.Item1, grain, t.Item2)).Where(addr => IsValidSilo(addr.Silo)).ToList();
                if (log.IsVerbose2)
                {
                    log.Verbose2("FullLookup mine {0}={1}", grain, a.ToStrings());
                }
                LocalDirectorySuccesses.Increment();
                return(a);
            }

            // Just a optimization. Why sending a message to someone we know is not valid.
            if (!IsValidSilo(silo))
            {
                throw new OrleansException(String.Format("Current directory at {0} is not stable to perform the lookup for grain {1} (it maps to {2}, which is not a valid silo). Retry later.", MyAddress, grain, silo));
            }

            RemoteLookupsSent.Increment();
            Tuple <List <Tuple <SiloAddress, ActivationId> >, int> result = await GetDirectoryReference(silo).LookUp(grain, NUM_RETRIES);

            // update the cache
            List <Tuple <SiloAddress, ActivationId> > entries = result.Item1.Where(t => IsValidSilo(t.Item1)).ToList();
            List <ActivationAddress> addresses = entries.Select(t => ActivationAddress.GetAddress(t.Item1, grain, t.Item2)).ToList();

            if (log.IsVerbose2)
            {
                log.Verbose2("FullLookup remote {0}={1}", grain, addresses.ToStrings());
            }

            if (entries.Count > 0)
            {
                DirectoryCache.AddOrUpdate(grain, entries, result.Item2);
            }

            return(addresses);
        }
Example #15
0
        public async Task <AddressAndTag> LookupAsync(GrainId grainId, int hopCount = 0)
        {
            (hopCount > 0 ? RemoteLookupsReceived : fullLookups).Increment();

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

            // 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(grainId, hopCount, "LookUpAsync");
                if (forwardAddress is object)
                {
                    int hash = unchecked ((int)grainId.GetUniformHashCode());
                    this.log.LogWarning($"LookupAsync - It seems we are not the owner of grain {grainId} (hash: {hash:X}), trying to forward it to {forwardAddress} (hopCount={hopCount})");
                }
            }

            if (forwardAddress == null)
            {
                // we are the owner
                LocalDirectoryLookups.Increment();
                var localResult = DirectoryPartition.LookUpActivation(grainId);
                if (localResult.Address == null)
                {
                    // it can happen that we cannot find the grain in our partition if there were
                    // some recent changes in the membership
                    if (log.IsEnabled(LogLevel.Trace))
                    {
                        log.Trace("FullLookup mine {0}=none", grainId);
                    }
                    localResult.Address    = default;
                    localResult.VersionTag = GrainInfo.NO_ETAG;
                    return(localResult);
                }

                if (log.IsEnabled(LogLevel.Trace))
                {
                    log.Trace("FullLookup mine {0}={1}", grainId, localResult.Address);
                }
                LocalDirectorySuccesses.Increment();
                return(localResult);
            }
            else
            {
                // Just a optimization. Why sending a message to someone we know is not valid.
                if (!IsValidSilo(forwardAddress))
                {
                    throw new OrleansException($"Current directory at {MyAddress} is not stable to perform the lookup for grainId {grainId} (it maps to {forwardAddress}, which is not a valid silo). Retry later.");
                }

                RemoteLookupsSent.Increment();
                var result = await GetDirectoryReference(forwardAddress).LookupAsync(grainId, hopCount + 1);

                // update the cache
                if (result.Address is { } address&& IsValidSilo(address.SiloAddress))
                {
                    DirectoryCache.AddOrUpdate(address, result.VersionTag);
                }

                if (log.IsEnabled(LogLevel.Trace))
                {
                    log.Trace("FullLookup remote {0}={1}", grainId, result.Address);
                }

                return(result);
            }
        }
Example #16
0
 public AddressAndTag GetLocalDirectoryData(GrainId grain)
 {
     return(DirectoryPartition.LookUpActivation(grain));
 }