Merge branch 'master' of https://github.com/Cryptkeeper/Minetrack into uplot

This commit is contained in:
Nick Krecklow 2020-05-14 20:50:51 -05:00
commit 9bdc892815
No known key found for this signature in database
GPG Key ID: 5F149FDE156FFA94
9 changed files with 97 additions and 76 deletions

@ -9,10 +9,6 @@
"pingAll": 3000, "pingAll": 3000,
"connectTimeout": 2500 "connectTimeout": 2500
}, },
"performance": {
"skipUnfurlSrv": false,
"unfurlSrvCacheTtl": 120000
},
"logToDatabase": false, "logToDatabase": false,
"graphDuration": 86400000, "graphDuration": 86400000,
"serverGraphDuration": 180000 "serverGraphDuration": 180000

@ -1,3 +1,4 @@
<<<<<<< HEAD
**5.5.0** *(May 11 2020)* **5.5.0** *(May 11 2020)*
**IMPORTANT** **IMPORTANT**
@ -14,6 +15,14 @@ This update moves ping timestamps to a shared timestamp per round. Meaning that
- Removes the mobile browser detection/manual historical graph load request. It is now automatically loaded given its smaller size. - Removes the mobile browser detection/manual historical graph load request. It is now automatically loaded given its smaller size.
Faster, smaller, more features. Faster, smaller, more features.
=======
**5.4.3** *(May 14 2020)*
- Added support for the optional field `config->skipSrvTimeout` in `config.json`. If a configured server does not return a valid response when unfurling potential SRV records, it will avoid re-unfurling SRV records for this duration in milliseconds. Use a value of `0` to disable this feature altogether.
- Removes support for the `config->performance->skipUnfurlSrv` and `config->performance->unfurlSrvCacheTtl` fields in `config.json
**5.4.2** *(May 13 2020)*
- Fixes a typo causing `_minecraft._tcp.*` SRV records to not resolve.
>>>>>>> 0e5859a82953b9d67ea759a3235080eaf7dbe74c
**5.4.1** *(May 10 2020)* **5.4.1** *(May 10 2020)*
- Adds warnings when the system is pinging more frequently than it is getting replies. - Adds warnings when the system is pinging more frequently than it is getting replies.

@ -18,7 +18,7 @@ class Database {
loadGraphPoints (graphDuration, callback) { loadGraphPoints (graphDuration, callback) {
// Query recent pings // Query recent pings
const endTime = new Date().getTime() const endTime = TimeTracker.getEpochMillis()
const startTime = endTime - graphDuration const startTime = endTime - graphDuration
this.getRecentPings(startTime, endTime, pingData => { this.getRecentPings(startTime, endTime, pingData => {

76
lib/dns.js Normal file

@ -0,0 +1,76 @@
const dns = require('dns')
const logger = require('./logger')
const { TimeTracker } = require('./time')
const config = require('../config')
const SKIP_SRV_TIMEOUT = config.skipSrvTimeout || 60 * 60 * 1000
class DNSResolver {
constructor (ip, port) {
this._ip = ip
this._port = port
}
_skipSrv () {
this._skipSrvUntil = TimeTracker.getEpochMillis() + SKIP_SRV_TIMEOUT
}
_isSkipSrv () {
return this._skipSrvUntil && TimeTracker.getEpochMillis() <= this._skipSrvUntil
}
resolve (callback) {
if (this._isSkipSrv()) {
callback(this._ip, this._port, config.rates.connectTimeout)
return
}
const startTime = TimeTracker.getEpochMillis()
let callbackFired = false
const fireCallback = (ip, port) => {
if (!callbackFired) {
callbackFired = true
// Send currentTime - startTime to provide remaining connectionTime allowance
callback(ip || this._ip, port || this._port, TimeTracker.getEpochMillis() - startTime)
}
}
const timeoutCallback = setTimeout(fireCallback, config.rates.connectTimeout)
dns.resolveSrv('_minecraft._tcp.' + this._ip, (err, records) => {
// Cancel the timeout handler if not already fired
if (!callbackFired) {
clearTimeout(timeoutCallback)
}
// Test if the error indicates a miss, or if the records returned are empty
if ((err && (err.code === 'ENOTFOUND' || err.code === 'ENODATA')) || !records || records.length === 0) {
// Compare config.skipSrvTimeout directly since SKIP_SRV_TIMEOUT has an or'd value
// isValidSkipSrvTimeout == whether the config has a valid skipSrvTimeout value set
const isValidSkipSrvTimeout = typeof config.skipSrvTimeout === 'number' && config.skipSrvTimeout > 0
// Only activate _skipSrv if the skipSrvTimeout value is either NaN or > 0
// 0 represents a disabled flag
if (!this._isSkipSrv() && isValidSkipSrvTimeout) {
this._skipSrv()
logger.log('warn', 'No SRV records were resolved for %s. Minetrack will skip attempting to resolve %s SRV records for %d minutes.', this._ip, this._ip, SKIP_SRV_TIMEOUT / (60 * 1000))
}
fireCallback()
} else {
// Only fires if !err && records.length > 0
fireCallback(records[0].name, records[0].port)
}
})
}
}
module.exports = DNSResolver

@ -9,7 +9,7 @@ winston.add(winston.transports.File, {
winston.add(winston.transports.Console, { winston.add(winston.transports.Console, {
timestamp: () => { timestamp: () => {
const date = new Date() const date = new Date()
return date.toLocaleTimeString() + ' ' + date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear().toString().substring(2, 4) return date.toLocaleTimeString() + ' ' + date.toLocaleDateString()
}, },
colorize: true colorize: true
}) })

@ -12,10 +12,10 @@ const config = require('../config')
function ping (serverRegistration, timeout, callback, version) { function ping (serverRegistration, timeout, callback, version) {
switch (serverRegistration.data.type) { switch (serverRegistration.data.type) {
case 'PC': case 'PC':
serverRegistration.unfurlSrv((host, port) => { serverRegistration.dnsResolver.unfurlSrv((host, port, remainingTimeout) => {
const server = new minecraftJavaPing.MinecraftServer(host, port || 25565) const server = new minecraftJavaPing.MinecraftServer(host, port || 25565)
server.ping(timeout, version, (err, res) => { server.resolve(remainingTimeout, version, (err, res) => {
if (err) { if (err) {
callback(err) callback(err)
} else { } else {

@ -1,9 +1,9 @@
const crypto = require('crypto') const crypto = require('crypto')
const dns = require('dns')
const { GRAPH_UPDATE_TIME_GAP, TimeTracker } = require('./time') const DNSResolver = require('./dns')
const Server = require('./server') const Server = require('./server')
const { GRAPH_UPDATE_TIME_GAP, TimeTracker } = require('./time')
const { getPlayerCountOrNull } = require('./util') const { getPlayerCountOrNull } = require('./util')
const config = require('../config') const config = require('../config')
@ -21,6 +21,7 @@ class ServerRegistration {
this.serverId = serverId this.serverId = serverId
this.data = data this.data = data
this._pingHistory = [] this._pingHistory = []
this.dnsResolver = new DNSResolver(this.data.ip, this.data.port)
} }
handlePing (timestamp, resp, err, version, updateHistoryGraph) { handlePing (timestamp, resp, err, version, updateHistoryGraph) {
@ -254,62 +255,6 @@ class ServerRegistration {
color: this.data.color color: this.data.color
} }
} }
unfurlSrv (callback) {
// Skip unfurling SRV, instantly return pre-configured data
if (config.performance && config.performance.skipUnfurlSrv) {
callback(this.data.ip, this.data.port)
return
}
const timestamp = new Date().getTime()
// If a cached copy exists and is within its TTL, instantly return
if (this._lastUnfurlSrv && timestamp - this._lastUnfurlSrv.timestamp <= config.performance.unfurlSrvCacheTtl) {
callback(this._lastUnfurlSrv.ip, this._lastUnfurlSrv.port)
return
}
// Group callbacks into an array
// Once resolved, fire callbacks sequentially
// This avoids callbacks possibly executing out of order
if (!this._unfurlSrvCallbackQueue) {
this._unfurlSrvCallbackQueue = []
}
this._unfurlSrvCallbackQueue.push(callback)
// Prevent multiple #resolveSrv calls per ServerRegistration
if (!this._isUnfurlingSrv) {
this._isUnfurlingSrv = true
dns.resolveSrv('_minecraft._tcp' + this.data.ip, (_, records) => {
this._lastUnfurlSrv = {
timestamp
}
if (records && records.length > 0) {
this._lastUnfurlSrv.ip = records[0].name
this._lastUnfurlSrv.port = records[0].port
} else {
// Provide fallback to pre-configured data
this._lastUnfurlSrv.ip = this.data.ip
this._lastUnfurlSrv.port = this.data.port
}
// Fire the waiting callbacks in queue
// Release blocking flag to allow new #resolveSrv calls
this._isUnfurlingSrv = false
for (const callback of this._unfurlSrvCallbackQueue) {
callback(this._lastUnfurlSrv.ip, this._lastUnfurlSrv.port)
}
// Reset the callback queue
this._unfurlSrvCallbackQueue = []
})
}
}
} }
module.exports = ServerRegistration module.exports = ServerRegistration

@ -10,7 +10,7 @@ class TimeTracker {
} }
newPointTimestamp () { newPointTimestamp () {
const timestamp = new Date().getTime() const timestamp = TimeTracker.getEpochMillis()
TimeTracker.pushAndShift(this._serverGraphPoints, timestamp, TimeTracker.getMaxServerGraphDataLength()) TimeTracker.pushAndShift(this._serverGraphPoints, timestamp, TimeTracker.getMaxServerGraphDataLength())
@ -53,6 +53,10 @@ class TimeTracker {
return Math.floor(timestamp / 1000) return Math.floor(timestamp / 1000)
} }
static getEpochMillis () {
return new Date().getTime()
}
static getMaxServerGraphDataLength () { static getMaxServerGraphDataLength () {
return Math.ceil(config.serverGraphDuration / config.rates.pingAll) return Math.ceil(config.serverGraphDuration / config.rates.pingAll)
} }

@ -30,15 +30,6 @@ if (!config.serverGraphDuration) {
config.serverGraphDuration = 3 * 60 * 10000 config.serverGraphDuration = 3 * 60 * 10000
} }
if (config.performance && config.performance.skipUnfurlSrv) {
logger.log('warn', '"performance.skipUnfurlSrv" is enabled. Any configured hosts using SRV records may not properly resolve.')
}
if (!config.performance || typeof config.performance.unfurlSrvCacheTtl === 'undefined') {
logger.log('warn', '"performance.unfurlSrvCacheTtl" is not defined in config.json - defaulting to 120 seconds!')
config.performance.unfurlSrvCacheTtl = 2 * 60 * 1000
}
if (!config.logToDatabase) { if (!config.logToDatabase) {
logger.log('warn', 'Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.') logger.log('warn', 'Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.')