diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp index 7a6b6a4..4ff09b9 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp | |||
| @@ -2474,6 +2474,7 @@ static const double VARDIFF_STALL_CUT = 4.0; // divide difficulty by thi | |||
| 2474 | struct Miner { | 2474 | struct Miner { |
| 2475 | btf_socket_t fd; | 2475 | btf_socket_t fd; |
| 2476 | bool authorised = false; | 2476 | bool authorised = false; |
| 2477 | bool subscribed = false; // completed mining.subscribe (#19 census) | ||
| 2477 | std::string address; | 2478 | std::string address; |
| 2478 | std::string worker; | 2479 | std::string worker; |
| 2479 | std::string readBuf; | 2480 | std::string readBuf; |
| @@ -2582,6 +2583,20 @@ static bool SendLine(btf_socket_t fd, const json& j) | |||
| 2582 | static const size_t MAX_STRATUM_LINE = 64 * 1024; | 2583 | static const size_t MAX_STRATUM_LINE = 64 * 1024; |
| 2583 | static const size_t MAX_POOL_MINERS = 1000; | 2584 | static const size_t MAX_POOL_MINERS = 1000; |
| 2584 | 2585 | ||
| 2586 | // #19 instrumentation: the "broadcast to N miner(s)" line counts connected | ||
| 2587 | // sockets, which says nothing about how many are actually mining. These split | ||
| 2588 | // the connection census into connected / subscribed / authorized / recently | ||
| 2589 | // active, so an operator can tell a real workforce from a pile of half-open | ||
| 2590 | // sessions at a glance. | ||
| 2591 | static const int64 POOL_CENSUS_SECS = 60; // how often to log the breakdown | ||
| 2592 | static const int64 POOL_ACTIVE_WINDOW = 300; // a miner is "active" if it | ||
| 2593 | // authorized and was seen within | ||
| 2594 | // this window (one payout-rotate | ||
| 2595 | // cycle) | ||
| 2596 | static const int64 POOL_UNAUTH_GRACE_SECS = 60; // a connection still not | ||
| 2597 | // authorized after this long is | ||
| 2598 | // idle, not merely starting up | ||
| 2599 | |||
| 2585 | // Non-blocking recv: pull whatever bytes are available now into buf, then | 2600 | // Non-blocking recv: pull whatever bytes are available now into buf, then |
| 2586 | // return one complete line if one is ready. Three outcomes: | 2601 | // return one complete line if one is ready. Three outcomes: |
| 2587 | // true -- line is filled; buf may still contain more lines | 2602 | // true -- line is filled; buf may still contain more lines |
| @@ -2690,6 +2705,65 @@ static void BroadcastJob(bool clean) | |||
| 2690 | } | 2705 | } |
| 2691 | 2706 | ||
| 2692 | // --------------------------------------------------------------------------- | 2707 | // --------------------------------------------------------------------------- |
| 2708 | // #19: connection census. "broadcast to N miner(s)" only counts authorized | ||
| 2709 | // sockets that got a job; it says nothing about the rest of the connection | ||
| 2710 | // table. This logs the full breakdown so an operator can tell a real workforce | ||
| 2711 | // apart from half-open / never-authorized / idle sessions, and flags when the | ||
| 2712 | // single select() loop is running out of room (issue #26). | ||
| 2713 | // | ||
| 2714 | // "Out of room" is not the same question on both platforms, and FD_SETSIZE | ||
| 2715 | // answers a different one on each. On POSIX it bounds the *value* of any | ||
| 2716 | // descriptor handed to FD_SET, so what matters is the highest fd we hold. On | ||
| 2717 | // Windows an fd_set is a counted array of SOCKET handles whose numeric values | ||
| 2718 | // are arbitrary, so what matters is how many we hold, not how large they are. | ||
| 2719 | // FD_SETSIZE comes from <sys/select.h> or winsock2.h; this file must not define | ||
| 2720 | // it, because redefining it after those headers changes nothing. | ||
| 2721 | // --------------------------------------------------------------------------- | ||
| 2722 | static void LogPoolConnectionCensus() | ||
| 2723 | { | ||
| 2724 | int connected = 0, subscribed = 0, authorized = 0, active = 0, neverAuthIdle = 0; | ||
| 2725 | int64 now = GetTime(); | ||
| 2726 | #ifndef _WIN32 | ||
| 2727 | int maxfd = 0; | ||
| 2728 | #endif | ||
| 2729 | for (Miner* m : gMiners) { | ||
| 2730 | connected++; | ||
| 2731 | #ifndef _WIN32 | ||
| 2732 | if ((int)m->fd > maxfd) maxfd = (int)m->fd; | ||
| 2733 | #endif | ||
| 2734 | if (m->subscribed) subscribed++; | ||
| 2735 | if (m->authorised) { | ||
| 2736 | authorized++; | ||
| 2737 | if (m->lastSeen > 0 && now - m->lastSeen <= POOL_ACTIVE_WINDOW) | ||
| 2738 | active++; | ||
| 2739 | } else if (now - m->connectTime >= POOL_UNAUTH_GRACE_SECS) { | ||
| 2740 | neverAuthIdle++; // past the grace period and still not authorized | ||
| 2741 | } | ||
| 2742 | } | ||
| 2743 | |||
| 2744 | #ifndef _WIN32 | ||
| 2745 | int headroom = (int)FD_SETSIZE - maxfd; | ||
| 2746 | LogPrint("pool", "[pool] census: connected=%d subscribed=%d authorized=%d" | ||
| 2747 | " active(last %llds)=%d never-auth-idle=%d" | ||
| 2748 | " (cap=%zu, maxfd=%d, FD_SETSIZE=%d)\n", | ||
| 2749 | connected, subscribed, authorized, (long long)POOL_ACTIVE_WINDOW, | ||
| 2750 | active, neverAuthIdle, MAX_POOL_MINERS, maxfd, (int)FD_SETSIZE); | ||
| 2751 | #else | ||
| 2752 | int headroom = (int)FD_SETSIZE - connected; | ||
| 2753 | LogPrint("pool", "[pool] census: connected=%d subscribed=%d authorized=%d" | ||
| 2754 | " active(last %llds)=%d never-auth-idle=%d" | ||
| 2755 | " (cap=%zu, FD_SETSIZE=%d sockets)\n", | ||
| 2756 | connected, subscribed, authorized, (long long)POOL_ACTIVE_WINDOW, | ||
| 2757 | active, neverAuthIdle, MAX_POOL_MINERS, (int)FD_SETSIZE); | ||
| 2758 | #endif | ||
| 2759 | if (headroom <= 16) | ||
| 2760 | LogPrint("pool", "[pool] WARNING: %d slots left before FD_SETSIZE=%d --" | ||
| 2761 | " the select() loop is about to walk into undefined behaviour" | ||
| 2762 | " (#26). Refuse new miners or rebuild with poll/epoll.\n", | ||
| 2763 | headroom, (int)FD_SETSIZE); | ||
| 2764 | } | ||
| 2765 | |||
| 2766 | // --------------------------------------------------------------------------- | ||
| 2693 | // Stratum message handler -- called for each complete line from a miner | 2767 | // Stratum message handler -- called for each complete line from a miner |
| 2694 | // Returns false if the miner should be disconnected immediately. | 2768 | // Returns false if the miner should be disconnected immediately. |
| 2695 | // --------------------------------------------------------------------------- | 2769 | // --------------------------------------------------------------------------- |
| @@ -2757,6 +2831,7 @@ static bool HandleLine(Miner* m, const std::string& rawLine, | |||
| 2757 | // ------------------------------------------------------------------ | 2831 | // ------------------------------------------------------------------ |
| 2758 | if (method == "mining.subscribe") { | 2832 | if (method == "mining.subscribe") { |
| 2759 | std::string sid = ToHex(&m->fd, 4); | 2833 | std::string sid = ToHex(&m->fd, 4); |
| 2834 | m->subscribed = true; | ||
| 2760 | LogPrint("worker", "[worker->pool] subscribe from fd=%d sid=%s\n", | 2835 | LogPrint("worker", "[worker->pool] subscribe from fd=%d sid=%s\n", |
| 2761 | (int)m->fd, sid.c_str()); | 2836 | (int)m->fd, sid.c_str()); |
| 2762 | reply(json::array({json::array({json::array({"mining.notify",sid})}),sid,4})); | 2837 | reply(json::array({json::array({json::array({"mining.notify",sid})}),sid,4})); |
| @@ -4593,6 +4668,7 @@ static void PoolEventLoop() | |||
| 4593 | int lastHeight = -1; | 4668 | int lastHeight = -1; |
| 4594 | int64 lastRefresh = GetTime(); | 4669 | int64 lastRefresh = GetTime(); |
| 4595 | int64 lastStatsRefresh = 0; | 4670 | int64 lastStatsRefresh = 0; |
| 4671 | int64 lastCensus = 0; // #19 connection census throttle | ||
| 4596 | int blocksFoundThisSession = 0; | 4672 | int blocksFoundThisSession = 0; |
| 4597 | int64 noJobSince = 0; // 0 = have a job | 4673 | int64 noJobSince = 0; // 0 = have a job |
| 4598 | bool warnedNoJob = false; | 4674 | bool warnedNoJob = false; |
| @@ -4810,6 +4886,14 @@ static void PoolEventLoop() | |||
| 4810 | RefreshStats(blocksFoundThisSession, roundShareTotal); | 4886 | RefreshStats(blocksFoundThisSession, roundShareTotal); |
| 4811 | lastStatsRefresh = GetTime(); | 4887 | lastStatsRefresh = GetTime(); |
| 4812 | } | 4888 | } |
| 4889 | |||
| 4890 | // 6. #19: log the connection census periodically. Cheap (one pass over | ||
| 4891 | // gMiners) and it is the single number an operator reads to judge pool | ||
| 4892 | // health, so it should not be an overstatement. | ||
| 4893 | if (GetTime() - lastCensus >= POOL_CENSUS_SECS) { | ||
| 4894 | LogPoolConnectionCensus(); | ||
| 4895 | lastCensus = GetTime(); | ||
| 4896 | } | ||
| 4813 | } | 4897 | } |
| 4814 | 4898 | ||
| 4815 | for (Miner* m : gMiners) { sock_close(m->fd); delete m; } | 4899 | for (Miner* m : gMiners) { sock_close(m->fd); delete m; } |