public virtual INodeService AddNode(EnhancedServiceParams serviceParams, int weight = 1)
        {
            serviceParams.Require(nameof(serviceParams), "Service Parameters must be set first.");
            weight.RequireAtLeast(1, nameof(weight));

            var node = new NodeService(serviceParams, weight);

            if (NodeQueue.IsEmpty)
            {
                SetPrimaryNode(node);
            }

            NodeQueue.Enqueue(node);

            return(node);
        }
        public virtual IEnhancedOrgService GetService(int threads = 1)
        {
            threads.RequireAtLeast(1);

            ValidateState();

            NodeService node = null;

            lock (dequeueLock)
            {
                while (true)
                {
                    switch (Rules.RouterMode)
                    {
                    case RouterMode.RoundRobin:
                    case null:
                        foreach (var _ in NodeQueue)
                        {
                            NodeQueue.TryDequeue(out node);
                            NodeQueue.Enqueue(node);

                            if (node.Status == NodeStatus.Online)
                            {
                                break;
                            }
                        }
                        break;

                    case RouterMode.WeightedRoundRobin:
                        foreach (var _ in NodeQueue)
                        {
                            var currentNode           = NodeQueue.FirstOrDefault();
                            var latestNode            = NodeQueue.LastOrDefault();
                            var currentNodeExecutions = currentNode?.Pool.Stats.RequestCount;
                            var latestNodeExecutions  = latestNode?.Pool.Stats.RequestCount;

                            if (currentNode?.Status == NodeStatus.Online &&
                                (currentNodeExecutions / (double?)latestNodeExecutions) < (currentNode?.Weight / (double?)latestNode?.Weight))
                            {
                                node = currentNode;
                            }
                            else
                            {
                                NodeQueue.TryDequeue(out node);
                                NodeQueue.Enqueue(node);
                                node = NodeQueue.FirstOrDefault();
                            }

                            if (node?.Status == NodeStatus.Online)
                            {
                                break;
                            }
                        }
                        break;

                    case RouterMode.StaticWithFallback:
                        node = NodeQueue.FirstOrDefault(n => n.IsPrimary)
                               ?? NodeQueue.FirstOrDefault(n => n.Status == NodeStatus.Online);
                        break;

                    case RouterMode.LeastLoaded:
                        node = NodeQueue
                               .Select(n =>
                                       new
                        {
                            n,
                            load = n.Pool.Stats.PendingOperations
                                   .Count(o => o.OperationStatus != Status.Success &&
                                          o.OperationStatus != Status.Failure)
                        })
                               .Where(e => e.n.Status == NodeStatus.Online)
                               .OrderBy(e => e.load)
                               .FirstOrDefault()?.n;
                        break;

                    case RouterMode.LeastLatency:
                        node = NodeQueue
                               .Where(n => n.Status == NodeStatus.Online)
                               .OrderBy(n => n.Latency).FirstOrDefault();
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(Rules.RouterMode), Rules.RouterMode, "Router mode is not supported.");
                    }

                    if (Rules.IsFallbackEnabled == true)
                    {
                        node ??= FallbackNodes.OfType <NodeService>().Union(NodeQueue).FirstOrDefault(n => n.Status == NodeStatus.Online);
                    }

                    // if no nodes found and there is a node with no latency measured (starting up), wait
                    if (node == null && NodeQueue.Union(FallbackNodes.OfType <NodeService>()).Any(n => !n.LatencyHistory.Any()))
                    {
                        Thread.Sleep(100);
                        continue;
                    }

                    break;
                }
            }

            if (node == null)
            {
                throw new NodeSelectException("Cannot find a valid node.");
            }

            var service = node.Pool.GetService(threads);

            if (service.Parameters.AutoRetryParams?.CustomRetryFunctions.Contains(CustomRetry) == false)
            {
                service.Parameters.AutoRetryParams.CustomRetryFunctions?.Add(CustomRetry);
            }

            return(service);
        }