Skip to content

Commit e336f5d

Browse files
authored
Merge pull request #768 from frstrtr/dash/per-rig-latency-column
dash(web): per-rig TCP latency column in workers-table
2 parents ab1f360 + 2221861 commit e336f5d

6 files changed

Lines changed: 52 additions & 2 deletions

File tree

src/core/stratum_server.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@
1515
#include <cmath>
1616
#include <cstring>
1717
#include <boost/algorithm/string.hpp>
18+
#ifdef __linux__
19+
#include <sys/socket.h>
20+
#include <netinet/in.h>
21+
#include <netinet/tcp.h>
22+
#endif
23+
24+
namespace {
25+
// Sample kernel-measured TCP round-trip time for a connected socket, in ms.
26+
// Same value `ss -ti` prints (tcpi_rtt, microseconds). Linux-only; 0 elsewhere
27+
// or on any error (socket closed, not yet established).
28+
inline double sample_tcp_rtt_ms(int fd) {
29+
#ifdef __linux__
30+
struct tcp_info ti;
31+
socklen_t len = sizeof(ti);
32+
if (fd >= 0 && ::getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == 0)
33+
return static_cast<double>(ti.tcpi_rtt) / 1000.0;
34+
#else
35+
(void)fd;
36+
#endif
37+
return 0.0;
38+
}
39+
} // namespace
1840

1941
namespace core {
2042

@@ -722,6 +744,7 @@ nlohmann::json StratumSession::handle_authorize(const nlohmann::json& params, co
722744
wi.remote_endpoint = socket_.remote_endpoint().address().to_string()
723745
+ ":" + std::to_string(socket_.remote_endpoint().port());
724746
} catch (...) {}
747+
wi.rtt_ms = sample_tcp_rtt_ms(socket_.native_handle());
725748
mining_interface_->register_stratum_worker(session_id_, wi);
726749
}
727750

@@ -1220,6 +1243,8 @@ nlohmann::json StratumSession::handle_submit(const nlohmann::json& params, const
12201243
0.0,
12211244
hashrate_tracker_.get_current_difficulty(),
12221245
accepted_shares_, rejected_shares_, stale_shares_);
1246+
mining_interface_->update_stratum_worker_rtt(session_id_,
1247+
sample_tcp_rtt_ms(socket_.native_handle()));
12231248
}
12241249

12251250
nlohmann::json response;

src/core/stratum_types.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ struct WorkerInfo {
174174
uint64_t stale{0};
175175
std::chrono::steady_clock::time_point connected_at;
176176
std::string remote_endpoint; // "ip:port"
177+
double rtt_ms{0.0}; // TCP round-trip latency in ms (Linux tcpi_rtt); 0 = unknown / non-Linux
177178
};
178179

179180
} // namespace core::stratum

src/core/stratum_work_source.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ class IWorkSource {
9090
uint64_t accepted, uint64_t rejected,
9191
uint64_t stale) = 0;
9292

93+
// Optional: record per-session TCP round-trip latency (ms), sampled from the
94+
// kernel socket by the stratum session. Default no-op so per-coin work sources
95+
// need no override; MiningInterface stores it for dashboard display.
96+
virtual void update_stratum_worker_rtt(const std::string& /*session_id*/,
97+
double /*rtt_ms*/) {}
98+
9399
// ── Work generation ──────────────────────────────────────────────────
94100
// Called per-job-issue (when sending mining.notify). Implementors
95101
// must produce a snapshot consistent with their template state at

src/core/web_server.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3378,6 +3378,8 @@ nlohmann::json MiningInterface::rest_stratum_stats()
33783378
workers_json[worker_key]["accepted"] = workers_json[worker_key]["accepted"].get<uint64_t>() + w.accepted;
33793379
workers_json[worker_key]["rejected"] = workers_json[worker_key]["rejected"].get<uint64_t>() + w.rejected;
33803380
workers_json[worker_key]["stale"] = workers_json[worker_key]["stale"].get<uint64_t>() + w.stale;
3381+
if (w.rtt_ms > workers_json[worker_key].value("rtt_ms", 0.0))
3382+
workers_json[worker_key]["rtt_ms"] = w.rtt_ms;
33813383
workers_json[worker_key]["shares"] = workers_json[worker_key]["shares"].get<uint64_t>() + w.accepted + w.stale;
33823384
// Keep earliest first_seen
33833385
if (first_seen_ts < workers_json[worker_key]["first_seen"].get<uint64_t>())
@@ -3396,7 +3398,8 @@ nlohmann::json MiningInterface::rest_stratum_stats()
33963398
{"stale", w.stale},
33973399
{"shares", w.accepted + w.stale},
33983400
{"connected_seconds", elapsed},
3399-
{"remote_endpoint", w.remote_endpoint}
3401+
{"remote_endpoint", w.remote_endpoint},
3402+
{"rtt_ms", w.rtt_ms}
34003403
};
34013404
}
34023405
}
@@ -6995,6 +6998,14 @@ void MiningInterface::update_stratum_worker(const std::string& session_id,
69956998
}
69966999
}
69977000

7001+
void MiningInterface::update_stratum_worker_rtt(const std::string& session_id, double rtt_ms)
7002+
{
7003+
std::lock_guard<std::mutex> lock(m_stratum_workers_mutex);
7004+
auto it = m_stratum_workers.find(session_id);
7005+
if (it != m_stratum_workers.end())
7006+
it->second.rtt_ms = rtt_ms;
7007+
}
7008+
69987009
std::map<std::string, MiningInterface::WorkerInfo> MiningInterface::get_stratum_workers() const
69997010
{
70007011
std::lock_guard<std::mutex> lock(m_stratum_workers_mutex);

src/core/web_server.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,7 @@ class MiningInterface : public jsonrpccxx::JsonRpc2Server,
14691469
void update_stratum_worker(const std::string& session_id,
14701470
double hashrate, double dead_hashrate, double difficulty,
14711471
uint64_t accepted, uint64_t rejected, uint64_t stale) override;
1472+
void update_stratum_worker_rtt(const std::string& session_id, double rtt_ms) override;
14721473
std::map<std::string, WorkerInfo> get_stratum_workers() const;
14731474

14741475
private:

web-static/stratum.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ <h2>👷 Connected Workers</h2>
628628
<tr>
629629
<th>Worker</th>
630630
<th>Hash Rate</th>
631+
<th title="Kernel TCP round-trip latency (ss -ti tcpi_rtt)">Latency (ms)</th>
631632
<th>Shares</th>
632633
<th>Accepted</th>
633634
<th>Rejected</th>
@@ -985,6 +986,11 @@ <h2>📡 Connections by IP</h2>
985986
.text(w.name.length > 25 ? w.name.substr(0, 12) + '...' + w.name.substr(-8) : w.name)
986987
.attr('title', w.name);
987988
tr.append('td').attr('class', 'hash-rate').text(format_hashrate(s.hash_rate || 0));
989+
var rtt = s.rtt_ms || 0;
990+
var rttClass = rtt <= 0 ? '' : (rtt < 20 ? 'good' : (rtt > 100 ? 'bad' : ''));
991+
tr.append('td').attr('class', rttClass)
992+
.attr('title', rtt <= 0 ? 'unknown (non-Linux node or socket closed)' : '')
993+
.text(rtt > 0 ? d3.format('.1f')(rtt) : '-');
988994
tr.append('td').text(d3.format(',')(s.shares || 0));
989995
tr.append('td').attr('class', 'good').text(d3.format(',')(s.accepted || 0));
990996
tr.append('td').attr('class', wRejectPct > 5 ? 'bad' : '')
@@ -995,7 +1001,7 @@ <h2>📡 Connections by IP</h2>
9951001

9961002
if (workerList.length === 0) {
9971003
tbody.append('tr')
998-
.append('td').attr('colspan', '7')
1004+
.append('td').attr('colspan', '8')
9991005
.style('text-align', 'center')
10001006
.style('color', 'var(--text-muted)')
10011007
.text('No workers connected');

0 commit comments

Comments
 (0)