upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp84
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
2474struct Miner { 2474struct 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)
2582static const size_t MAX_STRATUM_LINE = 64 * 1024; 2583static const size_t MAX_STRATUM_LINE = 64 * 1024;
2583static const size_t MAX_POOL_MINERS = 1000; 2584static 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.
2591static const int64 POOL_CENSUS_SECS = 60; // how often to log the breakdown
2592static 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)
2596static 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// ---------------------------------------------------------------------------
2722static 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; }