/// <summary> /// Start orchestrator host, jobs will be started based on their directed graph edges /// </summary> /// <param name="context">context</param> /// <returns>this</returns> public OrchestratorHost <TKey, TNode, TEdge> Start(IWorkContext context) { context.Verify(nameof(context)).IsNotNull(); int running = Interlocked.CompareExchange(ref _running, 1, 0); if (running == 1) { return(this); } _processedDict.Clear(); _runningKeys.Clear(); _graphContext = new GraphTopologicalContext <TKey, TEdge>(maxLevels: 1, equalityComparer: _graph.KeyCompare); context.Telemetry.Verbose(context, "Starting Orchestrator Host"); RunningTask = Task.Run(() => RunJobGraph(context)) .ContinueWith(x => _running = 0); context.Telemetry.Verbose(context, "Started Orchestrator Host"); return(this); }
/// <summary> /// <para>Return a list representing a topological sort based on edges is defined by a directed graph.</para> /// <para> /// The first item in the return list will be nodes with no edges. The next item (if there is one) /// will be nodes that only have dependencies on the previous list items, and so on. /// /// Uses two list to keep track of what nodes have been processed and stopped nodes. Stopped nodes /// stop the sorting for that edge(s). /// </para> /// </summary> /// <param name="graphContext">Graph topological context</param> /// <returns>List of list of nodes</returns> public static IList <IList <TNode> > TopologicalSort <TKey, TNode, TEdge>(this IReadOnlyGraphMap <TKey, TNode, TEdge> self, GraphTopologicalContext <TKey, TEdge> graphContext) where TNode : IGraphNode <TKey> where TEdge : IGraphEdge <TKey> { self.Verify(nameof(self)).IsNotNull(); graphContext.Verify(nameof(graphContext)).IsNotNull(); var visit = new HashSet <TKey>(graphContext.ProcessedNodeKeys, self.KeyCompare); var stopNodes = new HashSet <TKey>(graphContext.StopNodeKeys, self.KeyCompare); var orderList = new List <IList <TNode> >(); var nodeCounts = new List <(TNode Node, int Count)>(); IReadOnlyList <TEdge> edgesToUse = self.Edges.Values .Where(x => TestEdge(graphContext.EdgeType, x)) .ToList(); while (true) { nodeCounts.Clear(); var nodesToCount = self.Nodes.Values.Where(x => !visit.Contains(x.Key) && !stopNodes.Contains(x.Key)) .ToList(); if (nodesToCount.Count == 0) { return(orderList); } foreach (var node in nodesToCount) { // Count forward (own) edges int forwardCount = edgesToUse .OfType <GraphEdge <TKey> >() .Where(x => !visit.Contains(x.FromNodeKey)) .Where(x => self.KeyCompare.Equals(x.ToNodeKey, node.Key)) .Count(); int dependOnCount = edgesToUse .OfType <GraphDependOnEdge <TKey> >() .Where(x => !visit.Contains(x.ToNodeKey)) .Where(x => self.KeyCompare.Equals(x.FromNodeKey, node.Key)) .Count(); var phase1 = edgesToUse .OfType <GraphDependOnEdge <TKey> >() .Where(x => self.KeyCompare.Equals(x.FromNodeKey, node.Key)).ToList(); var phase2 = phase1 .SelectMany(x => self.GetLinkedNodes(self.CreateFilter().Include(x.ToNodeKey))).ToList(); var phase3 = phase2 .Where(x => !visit.Contains(x.Key)).ToList(); int depthDependsOnCount = phase3 .Count(); nodeCounts.Add((node, forwardCount + dependOnCount + depthDependsOnCount)); } var zero = nodeCounts .Where(x => x.Count == 0) .Select(x => x.Node) .ToList(); if (zero.Count == 0) { return(orderList); } orderList.Add(zero); if (orderList.Count == graphContext.MaxLevels) { return(orderList); } zero.ForEach(x => visit.Add(x.Key)); } }