예제 #1
0
        public void TestTimeoutMultiple()
        {
            var c1 = ChannelManager.CreateChannel <int>();
            var c2 = ChannelManager.CreateChannel <int>();
            var c3 = ChannelManager.CreateChannel <int>();

            Func <Task> p = async() =>
            {
                try
                {
                    await MultiChannelAccess.ReadFromAnyAsync(TimeSpan.FromSeconds(2), c1, c2, c3);

                    throw new Exception("Timeout did not happen?");
                }
                catch (TimeoutException)
                {
                }
            };

            var t = p();

            if (!t.Wait(TimeSpan.FromSeconds(3)))
            {
                throw new Exception("Failed to get timeout");
            }
        }
예제 #2
0
        public void TestMultiTypeReadWrite()
        {
            var c1 = ChannelManager.CreateChannel <int>();
            var c2 = ChannelManager.CreateChannel <string>();

            c1.WriteNoWait(1);
            c2.WriteNoWait("2");

            var r = MultiChannelAccess.ReadFromAnyAsync(c1.AsUntyped(), c2.AsUntyped()).WaitForTask().Result;

            if (r == null)
            {
                throw new Exception("Unexpected null result");
            }
            if (r.Channel != c1)
            {
                throw new Exception("Unexpected read channel");
            }

            if (!(r.Value is int))
            {
                throw new Exception("Priority changed?");
            }
            if ((int)r.Value != 1)
            {
                throw new Exception("Bad value?");
            }

            r = MultiChannelAccess.ReadFromAnyAsync(c1.RequestRead(), c2.RequestRead()).WaitForTask().Result;
            if (r == null)
            {
                throw new Exception("Unexpected null result");
            }
            if (r.Channel != c2)
            {
                throw new Exception("Unexpected read channel");
            }
            if (!(r.Value is string))
            {
                throw new Exception("Priority changed?");
            }
            if ((string)r.Value != "2")
            {
                throw new Exception("Bad value?");
            }

            var t = new [] { c1.AsUntyped().RequestWrite(4) }.WriteToAnyAsync();

            if (c1.Read() != 4)
            {
                throw new Exception("Bad value?");
            }

            t.WaitForTask().Wait();
        }
예제 #3
0
 public void TestInvalidMultiAccessOperation()
 {
     Assert.Throws <InvalidOperationException>(() =>
     {
         try
         {
             var c1 = ChannelManager.CreateChannel <int>();
             MultiChannelAccess.ReadOrWriteAnyAsync(MultisetRequest.Read(c1), MultisetRequest.Write(1, c1)).WaitForTask().Wait();
         }
         catch (AggregateException aex)
         {
             if (aex.InnerExceptions.Count == 1)
             {
                 throw aex.InnerExceptions.First();
             }
             throw;
         }
     });
 }
예제 #4
0
        public void TestMultiAccessOperation()
        {
            var c1 = ChannelManager.CreateChannel <int>();
            var c2 = ChannelManager.CreateChannel <int>();

            // Copy c2 + 1 => c1
            Func <Task> p1 = async() => {
                var val = await c2.ReadAsync();

                while (true)
                {
                    var res = await MultiChannelAccess.ReadOrWriteAnyAsync(MultisetRequest.Read(c2), MultisetRequest.Write(val, c1));

                    if (res.IsRead)
                    {
                        val = res.Value + 1;
                    }
                }
            };

            // Copy c1 => c2
            Func <Task> p2 = async() => {
                var val = 1;
                for (var i = 0; i < 10; i++)
                {
                    await c2.WriteAsync(val);

                    val = await c1.ReadAsync();
                }

                c1.Retire();
                c2.Retire();

                if (val != 10)
                {
                    throw new InvalidProgramException("Bad counter!");
                }
            };

            // Wait for shutdown
            try
            {
                Task.WhenAll(p1(), p2()).WaitForTask().Wait();
            }
            catch (Exception ex)
            {
                // Filter out all ChannelRetired exceptions
                if (ex is AggregateException)
                {
                    var rex = (from n in (ex as AggregateException).InnerExceptions
                               where !(n is RetiredException)
                               select n);

                    if (rex.Count() == 1)
                    {
                        throw rex.First();
                    }
                    else if (rex.Count() != 0)
                    {
                        throw new AggregateException(rex);
                    }
                }
                else
                {
                    throw;
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Runs the broker process.
        /// </summary>
        /// <param name="node">This nodes information</param>
        /// <param name="maxconnections">The maximum number of connections to allow</param>
        /// <returns>An awaitable task.</returns>
        public static Task RunAsync(PeerInfo node, int maxconnections = 50)
        {
            // The primary table for finding peers
            var peers = new Dictionary <EndPoint, Tuple <Task, IWriteChannel <ConnectionRequest> > >();

            // The peers listed by key
            var peersbykey = new Dictionary <Key, EndPoint>();

            // The MRU cache of peers
            var mrucache = new MRUCache <EndPoint, Key>(maxconnections, TimeSpan.FromDays(10));

            return(AutomationExtensions.RunTask(
                       new
            {
                Request = Channels.ConnectionBrokerRequests.ForRead,
                Registrations = Channels.ConnectionBrokerRegistrations.ForRead,
                Stats = Channels.ConnectionBrokerStats.ForRead,
                SelfHandler = Channels.RemoteRequests.ForWrite,
                Routing = Channels.RoutingTableRequests.ForWrite
            },

                       async self =>
            {
                log.Debug($"Broker is now running");
                while (true)
                {
                    log.Debug($"Broker is waiting for requests ...");
                    var mreq = await MultiChannelAccess.ReadFromAnyAsync(
                        self.Stats.RequestRead(),
                        self.Registrations.RequestRead(),
                        self.Request.RequestRead()
                        );

                    if (mreq.Channel == self.Stats)
                    {
                        log.Debug($"Broker got stat request");
                        var req = (IWriteChannel <ConnectionStatsResponse>)mreq.Value;
                        await req.WriteAsync(new ConnectionStatsResponse()
                        {
                            EndPoints = peers.Count,
                            Keys = peersbykey.Count,
                            Stats = (Channels.ConnectionBrokerRequests.Get() as ProfilingChannel <ConnectionRequest>)?.ReportStats()
                        });
                    }
                    else if (mreq.Channel == self.Registrations)
                    {
                        var req = (ConnectionRegistrationRequest)mreq.Value;
                        log.Debug($"Broker got {(req.IsTerminate ? "termination" : "registration")} request");
                        if (req.IsTerminate)
                        {
                            // Make sure we do not have stale stuff in the MRU cache
                            if (req.Peer != null && req.Peer.Address != null)
                            {
                                mrucache.Remove(req.Peer.Address);
                            }

                            if (req.Peer.Address != null && peers.TryGetValue(req.Peer.Address, out var c) && c.Item2 == req.Channel)
                            {
                                peers.Remove(req.Peer.Address);
                                if (req.Peer.Key != null)
                                {
                                    peersbykey.Remove(req.Peer.Key);
                                }
                            }

                            if (req.UpdateRouting)
                            {
                                log.Debug($"Removing peer in routing table due to termination of connection {req.Peer.Key} - {req.Peer.Address}");
                                await self.Routing.RemovePeerAsync(req.Peer.Key);
                            }
                        }
                        else
                        {
                            if (req.Peer.Address != null && peers.TryGetValue(req.Peer.Address, out var c) && (c.Item2 == req.Channel || c == null))
                            {
                                if (c == null)
                                {
                                    peers[req.Peer.Address] = new Tuple <Task, IWriteChannel <ConnectionRequest> >(null, req.Channel);
                                }

                                if (!peersbykey.ContainsKey(req.Peer.Key))
                                {
                                    peersbykey[req.Peer.Key] = req.Peer.Address;
                                }
                            }

                            if (req.UpdateRouting)
                            {
                                log.Debug($"Adding new peer to routing table {req.Peer.Key} - {req.Peer.Address}");
                                await self.Routing.AddPeerAsync(req.Peer.Key, req.Peer);
                            }
                        }
                    }
                    else
                    {
                        var req = (ConnectionRequest)mreq.Value;
                        log.Debug($"Broker got connection request for {req.EndPoint}");

                        // Check if we request ourselves
                        if (node.Key.Equals(req.Key) || node.Address.Equals(req.EndPoint))
                        {
                            log.Debug($"Broker got self-request, forwarding to owner");
                            await self.SelfHandler.WriteAsync(req);
                            continue;
                        }

                        Tuple <Task, IWriteChannel <ConnectionRequest> > peer = null;
                        try
                        {
                            // Existing connection, update MRU
                            var overflow = mrucache.Add(req.EndPoint, req.Key);

                            // If we have too many connections, kill one now
                            if (overflow != null)
                            {
                                // We could make this also take the closest k peers into account
                                log.Debug($"Broker has too many connections, closing {req.EndPoint}");
                                await peers[overflow].Item2.RetireAsync();
                            }

                            if (!peers.TryGetValue(req.EndPoint, out peer))
                            {
                                log.Debug($"Broker is starting a connection to {req.EndPoint}");
                                mrucache.Add(req.EndPoint, req.Key);
                                peer = peers[req.EndPoint] =
                                    PeerConnection.CreatePeer(
                                        node,
                                        new PeerInfo(req.Key, req.EndPoint),
                                        () => ConnectToPeerAsync(req.EndPoint),
                                        REQ_BUFFER_SIZE
                                        );

                                if (req.Key != null)
                                {
                                    peersbykey[req.Key] = req.EndPoint;
                                }
                            }

                            await peer.Item2.WriteAsync(req);
                        }
                        catch (Exception ex)
                        {
                            log.Warn("Failed to send request to peer", ex);
                            try { await req.Response.WriteAsync(new ConnectionResponse()
                                {
                                    Exception = ex
                                }); }
                            catch (Exception ex2) { log.Warn("Failed to write failure response", ex2); }

                            if (peer != null)
                            {
                                try { peer.Item2.AsWriteOnly().Dispose(); }
                                catch (Exception ex2) { log.Warn("Failed to terminate write channel", ex2); }

                                try { await peer.Item1; }
                                catch (Exception ex2) { log.Warn("Peer connection stopped with error", ex2); }
                            }

                            peers.Remove(req.EndPoint);
                        }
                    }
                }
            }
                       ));
        }
예제 #6
0
        /// <summary>
        /// Runs the router and forwarder
        /// </summary>
        /// <returns>An awaitable result.</returns>
        /// <param name="owner">The owner of the routing table.</param>
        /// <param name="k">The redundancy parameter.</param>
        /// <param name="buffersize">The size of the forwarding buffer.</param>
        public static Task RunAsync(PeerInfo owner, int k, int buffersize = 10)
        {
            return(AutomationExtensions.RunTask(
                       new
            {
                Requests = Channels.RoutingTableRequests.ForRead,
                Stats = Channels.RoutingTableStats.ForRead,
                PeerReq = Channels.PeerRequests.ForWrite
            },

                       async self =>
            {
                // Setup the routing table, and add us to it
                var table = new RoutingTable(owner.Key, k);
                table.Add(owner);
                log.Debug($"Router is now running");

                // Set up the error handler
                Func <RoutingRequest, Exception, Task> errorHandler = async(t, ex) =>
                {
                    log.Warn("Routing operation failed, sending failure response to requester", ex);
                    try
                    {
                        await t.Response.WriteAsync(new RoutingResponse()
                        {
                            Exception = ex, Succes = false
                        });
                    }
                    catch (Exception ex2)
                    {
                        log.Warn("Failed to forward error message", ex2);
                    }
                };

                using (var tp = new TaskPool <RoutingRequest>(buffersize, errorHandler))
                    while (true)
                    {
                        // Wait for requests
                        log.Debug($"Router is waiting for requests ...");
                        var r = await MultiChannelAccess.ReadFromAnyAsync(self.Stats.RequestRead(), self.Requests.RequestRead());
                        if (r.Channel == self.Stats)
                        {
                            log.Debug($"Router got stat request");

                            var m = (IWriteChannel <RoutingStatsResponse>)r.Value;
                            await m.WriteAsync(new RoutingStatsResponse()
                            {
                                Count = table.Count,
                                Stats = (Channels.RoutingTableRequests.Get() as ProfilingChannel <RoutingRequest>)?.ReportStats()
                            });
                            continue;
                        }

                        var data = (RoutingRequest)r.Value;
                        try
                        {
                            log.Debug($"Router got request {data.Operation}");
                            // Multiplex the operation
                            switch (data.Operation)
                            {
                            case RoutingOperation.Add:
                                {
                                    var success = table.Add(data.Data, out var isNew);
                                    if (data.Response != null)
                                    {
                                        await tp.Run(data, () => data.Response.WriteAsync(new RoutingResponse()
                                        {
                                            Succes = true, IsNew = isNew
                                        }));
                                    }

                                    // If the peer is new, discover what peers it knows
                                    if (isNew)
                                    {
                                        log.Debug($"New peer, requesting refresh");
                                        await tp.Run(data, () => self.PeerReq.WriteAsync(new PeerRequest()
                                        {
                                            Operation = PeerOperation.Refresh,
                                            Key = data.Data.Key
                                        }));
                                        log.Debug($"Peer refresh requested");
                                    }

                                    break;
                                }

                            case RoutingOperation.Remove:
                                {
                                    var sucess = table.RemoveKey(data.Key);
                                    if (data.Response != null)
                                    {
                                        await tp.Run(data, () => data.Response.WriteAsync(new RoutingResponse()
                                        {
                                            Succes = sucess
                                        }));
                                    }
                                    break;
                                }

                            case RoutingOperation.Lookup:
                                {
                                    var peers = table.Nearest(data.Key, k, data.OnlyKBucket);
                                    await tp.Run(data, () => data.Response.WriteAsync(new RoutingResponse()
                                    {
                                        Succes = true, Peers = peers
                                    }));
                                    break;
                                }

                            default:
                                throw new Exception($"Operation not supported: {data.Operation}");
                            }
                        }
                        catch (Exception ex)
                        {
                            await errorHandler(data, ex);
                        }
                    }
            }));
        }
예제 #7
0
        /// <summary>
        /// Runs the MRU cache
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="selfinfo">This peer's information</param>
        /// <param name="storesize">The size of the MRU store</param>
        /// <param name="maxage">The maximum amount of time items are stored</param>
        /// <param name="buffersize">The size of the parallel processing buffer</param>
        private static Task RunMRUAsync(PeerInfo selfinfo, int storesize, TimeSpan maxage, int buffersize)
        {
            var storechan = Channel.Create <MRUInternalStore>();

            return(AutomationExtensions.RunTask(new
            {
                Request = Channels.MRURequests.ForRead,
                Routing = Channels.RoutingTableRequests.ForWrite,
                Stats = Channels.MRUStats.ForRead,
                Store = storechan.AsRead()
            },
                                                async self =>
            {
                var cache = new MRUCache <Key, byte[]>(storesize, maxage);
                var store = new MRUCache <Key, byte[]>(int.MaxValue, maxage);

                log.Debug($"Store is now running");

                // Set up a shared error handler for logging and reporting errors
                Func <MRURequest, Exception, Task> errorHandler = async(req, ex) =>
                {
                    log.Warn("Failed to process request, sending error", ex);
                    try { await req.SendResponseAsync(ex); }
                    catch (Exception ex2) { log.Warn("Failed to forward error report", ex2); }
                };

                using (var tp = new TaskPool <MRURequest>(buffersize, errorHandler))
                    while (true)
                    {
                        log.Debug($"Store is waiting for requests ...");
                        var mreq = await MultiChannelAccess.ReadFromAnyAsync(
                            self.Stats.RequestRead(),
                            self.Store.RequestRead(),
                            self.Request.RequestRead()
                            );

                        if (mreq.Channel == self.Stats)
                        {
                            log.Debug($"Store got stat request");
                            var r = (IWriteChannel <MRUStatResponse>)mreq.Value;

                            await r.WriteAsync(new MRUStatResponse()
                            {
                                Items = cache.Count + store.Count,
                                Oldest = new DateTime(Math.Min(cache.OldestItem.Ticks, store.OldestItem.Ticks)),
                                Size = cache.Select(x => x.Value.Length).Sum() + store.Select(x => x.Value.Length).Sum(),
                                Stats = (Channels.MRURequests.Get() as ProfilingChannel <MRURequest>)?.ReportStats()
                            });
                            continue;
                        }

                        if (mreq.Channel == self.Store)
                        {
                            var sreq = (MRUInternalStore)mreq.Value;

                            log.Debug($"Store got internal store request");
                            var shouldBroadCast = sreq.Peers != null && !store.TryGetValue(sreq.Key, out _);
                            store.Add(sreq.Key, sreq.Data);

                            // We currently rely on the injector to broadcast,
                            // If we enable this, we need some logic to figure out
                            // the source of the Add, to both allow re-insertion
                            // and avoid repeated broadcasts if two peers determine
                            // they are *the* handling peer

                            //if (shouldBroadCast)
                            //await tp.Run(new MRURequest() { }, () => BroadcastValueAsync(selfinfo, sreq));

                            continue;
                        }

                        var req = (MRURequest)mreq.Value;
                        log.Debug($"Store got request {req.Operation}");
                        try
                        {
                            switch (req.Operation)
                            {
                            case MRUOperation.Add:
                                {
                                    // Always store it in our cache
                                    cache.Add(req.Key, req.Data);

                                    // Process long-term if needed
                                    await tp.Run(req, () => StoreLongTermAsync(selfinfo, self.Routing, storechan.AsWrite(), req.Key, req.Data));

                                    // Respond that we completed
                                    await tp.Run(req, () => req.SendResponseAsync(req.Key, null));
                                    break;
                                }

                            case MRUOperation.Get:
                                {
                                    var res = cache.TryGetValue(req.Key, out var data);
                                    if (!res)
                                    {
                                        res = store.TryGetValue(req.Key, out data);
                                    }

                                    await tp.Run(req, () => req.SendResponseAsync(req.Key, data, res));
                                    break;
                                }

                            case MRUOperation.Expire:
                                cache.ExpireOldItems();
                                store.ExpireOldItems();
                                await tp.Run(req, () => req.SendResponseAsync(null, null));
                                break;

                            default:
                                throw new Exception($"Unable to handle request with type {req.Operation}");
                            }
                            log.Debug($"Store completed request {req.Operation}");
                        }
                        catch (Exception ex)
                        {
                            await errorHandler(req, ex);
                        }
                    }
            }));
        }
예제 #8
0
        public void TestMultiTypeReadWrite()
        {
            var c1 = ChannelManager.CreateChannel <int>();
            var c2 = ChannelManager.CreateChannel <string>();
            var c3 = ChannelManager.CreateChannel <long>();

            c1.WriteNoWait(1);
            c2.WriteNoWait("2");
            c3.WriteNoWait(3);

            // Using explicit .AsUntyped() calls
            var r = MultiChannelAccess.ReadFromAnyAsync(c1.AsUntyped(), c2.AsUntyped(), c3.AsUntyped()).WaitForTask().Result;

            if (r == null)
            {
                throw new UnittestException("Unexpected null result");
            }
            if (r.Channel != c1)
            {
                throw new UnittestException("Unexpected read channel");
            }

            if (!(r.Value is int))
            {
                throw new UnittestException("Priority changed?");
            }
            if ((int)r.Value != 1)
            {
                throw new UnittestException("Bad value?");
            }

            // Using explicit .RequestRead() calls
            r = MultiChannelAccess.ReadFromAnyAsync(c1.RequestRead(), c2.RequestRead(), c3.RequestRead()).WaitForTask().Result;
            if (r == null)
            {
                throw new UnittestException("Unexpected null result");
            }
            if (r.Channel != c2)
            {
                throw new UnittestException("Unexpected read channel");
            }
            if (!(r.Value is string))
            {
                throw new UnittestException("Priority changed?");
            }
            if ((string)r.Value != "2")
            {
                throw new UnittestException("Bad value?");
            }

            // Using channels directly
            r = MultiChannelAccess.ReadFromAnyAsync(c1, c2, c3).WaitForTask().Result;
            if (r == null)
            {
                throw new UnittestException("Unexpected null result");
            }
            if (r.Channel != c3)
            {
                throw new UnittestException("Unexpected read channel");
            }

            if (!(r.Value is long))
            {
                throw new UnittestException("Priority changed?");
            }
            if ((long)r.Value != 3)
            {
                throw new UnittestException("Bad value?");
            }

            // Writing with untyped channel request
            var t = new [] { c1.AsUntyped().RequestWrite(4) }.WriteToAnyAsync();

            if (c1.Read() != 4)
            {
                throw new UnittestException("Bad value?");
            }

            t.WaitForTask().Wait();

            // Writing with a typed write request, using mixed types
            var t2 = MultiChannelAccess.WriteToAnyAsync(c1.RequestWrite(5), c2.RequestWrite("6"));

            if (c1.Read() != 5)
            {
                throw new UnittestException("Bad value?");
            }

            t2.WaitForTask().Wait();
        }
예제 #9
0
        /// <summary>
        /// Runs the peer process
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="selfinfo">The information describing the peer.</param>
        /// <param name="k">The redundancy parameter.</param>
        /// <param name="storesize">The maximum size of the local store</param>
        /// <param name="maxage">The maximum age of items in the cache</param>
        /// <param name="initialContactlist">Initial list of peers to contact.</param>
        /// <param name="requests">The request channel for local management</param>
        public static async Task RunPeer(PeerInfo selfinfo, int k, int storesize, TimeSpan maxage, EndPoint[] initialContactlist, IReadChannel <PeerRequest> requests)
        {
            try
            {
                if (selfinfo == null)
                {
                    throw new ArgumentNullException(nameof(selfinfo));
                }
                if (initialContactlist == null)
                {
                    throw new ArgumentNullException(nameof(initialContactlist));
                }

                var ip = selfinfo.Address as IPEndPoint;
                if (ip == null)
                {
                    throw new ArgumentException($"Unable to convert {nameof(selfinfo.Address)} to a {nameof(IPEndPoint)}", nameof(selfinfo));
                }

                log.Debug($"Starting a peer with key {selfinfo.Key} and address {selfinfo.Address}");

                using (var scope = new IsolatedChannelScope())
                {
                    var sock = new TcpListener(ip);
                    sock.Start();

                    // Set up the helper processes
                    var router  = RoutingProcess.RunAsync(selfinfo, k);
                    var broker  = ConnectionBroker.RunAsync(selfinfo);
                    var values  = MRUProcess.RunAsync(selfinfo, storesize, maxage);
                    var remoter = RemoteProcess.RunAsync(selfinfo);

                    log.Debug("Started router, broker, and value store");

                    // Handle new connections
                    var listener = ListenAsync(selfinfo, sock);

                    log.Debug("Started listener");

                    // Start discovery of peers
                    var discovery = DiscoveryProcess.RunAsync(selfinfo, initialContactlist);

                    log.Debug("Started discovery");

                    // The process handling requests to the local node
                    var proc = AutomationExtensions.RunTask(
                        new
                    {
                        Requests = requests,
                        Refresh  = Channels.PeerRequests.ForRead,

                        // Add these, so this process terminates the others as well
                        BrokerReg  = Channels.ConnectionBrokerRegistrations.ForWrite,
                        BrokerReq  = Channels.ConnectionBrokerRequests.ForWrite,
                        BrokerStat = Channels.ConnectionBrokerStats.ForWrite,
                        MRUReq     = Channels.MRURequests.ForWrite,
                        MRUStat    = Channels.MRUStats.ForWrite,
                        RouteReq   = Channels.RoutingTableRequests.ForWrite,
                        RouteStat  = Channels.RoutingTableStats.ForWrite,
                    },
                        async self =>
                    {
                        log.Debug("Running peer main loop");

                        try
                        {
                            while (true)
                            {
                                var req = (await MultiChannelAccess.ReadFromAnyAsync(self.Requests, self.Refresh)).Value;
                                log.Debug($"Peer {selfinfo.Key} got message: {req.Operation}");
                                switch (req.Operation)
                                {
                                case PeerOperation.Add:
                                    await HandleAddOperation(selfinfo, req, k);
                                    break;

                                case PeerOperation.Find:
                                    await HandleFindOperation(selfinfo, req, k);
                                    break;

                                case PeerOperation.Stats:
                                    await HandleStatsOperation(req);
                                    break;

                                case PeerOperation.Refresh:
                                    await HandleRefreshOperation(selfinfo, req, k);
                                    break;

                                default:
                                    await req.Response.WriteAsync(new PeerResponse()
                                    {
                                        SuccessCount = -1
                                    });
                                    break;
                                }
                                log.Debug($"Peer {selfinfo.Key} handled message: {req.Operation}");
                            }
                        }
                        catch (Exception ex)
                        {
                            if (!ex.IsRetiredException())
                            {
                                log.Warn($"Terminating peer {selfinfo.Key} due to error", ex);
                            }
                            throw;
                        }
                        finally
                        {
                            log.Debug($"Terminating peer {selfinfo.Key}");
                        }
                    }
                        );

                    log.Debug("Started main handler");

                    // Set up a process that periodically emits refresh operations
                    var refresher = AutomationExtensions.RunTask(
                        new { Control = Channels.PeerRequests.ForWrite },
                        async self =>
                    {
                        var respchan = Channel.Create <PeerResponse>();
                        while (true)
                        {
                            // Sleep, but exit if the parent does
                            if (await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(10)), proc) == proc)
                            {
                                return;
                            }

                            await self.Control.WriteAsync(new PeerRequest()
                            {
                                Operation = PeerOperation.Refresh,
                                Response  = respchan
                            });
                            await respchan.ReadAsync();
                        }
                    }
                        );

                    log.Debug("Started refresh process, peer is now live");
                    await proc;
                    await router;
                    await broker;
                    await values;
                    await remoter;
                    await discovery;
                    await refresher;

                    await Task.WhenAll(router, broker, values, remoter, discovery, refresher);
                }
            }
            catch (Exception ex)
            {
                log.Warn("Failed to start peer", ex);

                try { await requests.RetireAsync(); }
                catch (Exception ex2) { log.Warn("Failed to stop the input channel", ex2); }

                log.Debug($"Peer with key {selfinfo.Key} and address {selfinfo.Address} stopped...");

                throw;
            }
        }