public static async T.Task <IEnumerable <Node> > GetNodesAsync(this CloudUtilities u, string lowerNodeName, string higherNodeName, int?count, CancellationToken token) { var nodesTable = u.GetNodesTable(); var partitionQuery = u.GetPartitionQueryString(u.NodesPartitionKey); var lastRegistrationKey = u.GetRegistrationKey(lowerNodeName); var registrationEnd = u.GetRegistrationKey(higherNodeName); var registrationRangeQuery = u.GetRowKeyRangeString(lastRegistrationKey, registrationEnd); var q = TableQuery.CombineFilters( partitionQuery, TableOperators.And, registrationRangeQuery); var registrations = (await nodesTable.QueryAsync <ComputeClusterRegistrationInformation>(q, count, token)).Select(r => r.Item3).ToList(); if (!registrations.Any()) { return(new Node[0]); } var firstHeartbeat = u.GetHeartbeatKey(registrations[0].NodeName.ToLowerInvariant()); var lastHeartbeat = u.GetHeartbeatKey(registrations[registrations.Count - 1].NodeName.ToLowerInvariant()); var heartbeatRangeQuery = u.GetRowKeyRangeString(firstHeartbeat, lastHeartbeat, true); q = TableQuery.CombineFilters( partitionQuery, TableOperators.And, heartbeatRangeQuery); var heartbeats = (await nodesTable.QueryAsync <ComputeClusterNodeInformation>(q, null, token)).ToDictionary(h => h.Item3.Name.ToLowerInvariant(), h => (h.Item3, h.Item4)); return(registrations.Select(r => { var nodeName = r.NodeName.ToLowerInvariant(); var node = new Node() { NodeRegistrationInfo = r, Name = nodeName, }; if (heartbeats.TryGetValue(nodeName, out (ComputeClusterNodeInformation, DateTimeOffset)n)) { node.LastHeartbeatTime = n.Item2; node.RunningJobCount = n.Item1.Jobs.Count; if (n.Item2.AddSeconds(u.Option.MaxMissedHeartbeats * u.Option.HeartbeatIntervalSeconds) > DateTimeOffset.UtcNow) { node.Health = NodeHealth.OK; node.State = NodeState.Online; // TODO: adding events } else { node.Health = NodeHealth.Error; } } return node; })); }