From 2f6a4a843ccaf4cdbaa045493c4f3abd256b5956 Mon Sep 17 00:00:00 2001 From: Paul Hanyzewski Date: Fri, 11 Aug 2023 15:55:39 -0600 Subject: [PATCH 1/2] feat: allow the number of speed tests ran daily to be configurable --- README.md | 3 +++ config/config.go | 24 +++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a14cf54..b4feb60 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ To configure log verbosity, set the `VERBOSITY` environment variable to one of t | `PING_INTERVAL` | ping interval in seconds | `"60"` | | `PING_REQUESTS` | number of requests each test | `"600"` | | `REALTIME` | enable real-time features if on paid plan | `"true"` | +| `SPEED_TEST_DAILY_TOTAL` | approximate number of speed test to run per day | `"6"` | | `VERBOSITY` | controls log level. must be one of `debug`, `info`, `warn`, `error` | `"info"` | ## Flags @@ -180,6 +181,8 @@ Usage: imup api endpoint for imup realtime reloadable configuration, default is https://api.imup.io/v1/realtime/config -should-run-speed-test-address string api endpoint for imup realtime speed tests, default is https://api.imup.io/v1/realtime/shouldClientRunSpeedTest + -speed-test-daily-total string + configure the total number of speed tests to run in a day, [1-32] -speed-test-results-address string api endpoint for imup realtime speed test results, default is https://api.imup.io/v1/realtime/speedTestResults -speed-test-status-update-address string diff --git a/config/config.go b/config/config.go index 3b5a8b2..83a4798 100644 --- a/config/config.go +++ b/config/config.go @@ -48,6 +48,7 @@ var ( shouldRunSpeedTestAddress *string speedTestResultsAddress *string speedTestStatusUpdateAddress *string + speedTestTotal *string verbosity *string insecureSpeedTest *bool @@ -104,6 +105,7 @@ type Reloadable interface { PingRequestsCount() int ConnRequestsCount() int IMUPDataLen() int + SpeedTestDaily() int } // cfg is intentionally declared in the global space, but un-exported @@ -136,6 +138,7 @@ type config struct { PingDelay int PingInterval int PingRequests int + SpeedTestTotal int PingAddressesExternal []string @@ -189,6 +192,7 @@ func New() (Reloadable, error) { shouldRunSpeedTestAddress = flag.String("should-run-speed-test-address", "", fmt.Sprintf("api endpoint for imup realtime speed tests, default is %s/v1/realtime/shouldClientRunSpeedTest", ImUpAPIHost)) speedTestResultsAddress = flag.String("speed-test-results-address", "", fmt.Sprintf("api endpoint for imup realtime speed test results, default is %s/v1/realtime/speedTestResults", ImUpAPIHost)) speedTestStatusUpdateAddress = flag.String("speed-test-status-update-address", "", fmt.Sprintf("api endpoint for imup real-time speed test status updates, default is %s/v1/realtime/speedTestStatusUpdate", ImUpAPIHost)) + speedTestTotal = flag.String("speed-test-daily-total", "", "configure the total number of speed tests to run in a day, [1-32]") verbosity = flag.String("verbosity", "", "verbosity for log output [debug, info, warn, error], default is info") insecureSpeedTest = flag.Bool("insecure", false, "run insecure speed tests (ws:// and not wss://), default is false") @@ -214,15 +218,13 @@ func New() (Reloadable, error) { cfg.BlocklistedIPs = strings.Split(util.ValueOr(blocklistedIPs, "BLOCKLISTED_IPS", ""), ",") cfg.ConfigVersion = util.ValueOr(configVersion, "CONFIG_VERSION", "dev-preview") //todo: placeholder for reloadable configs cfg.Group = util.ValueOr(groupID, "GROUP_ID", "") - - cfg.PingAddressInternal = util.ValueOr(pingAddressInternal, "PING_ADDRESS_INTERNAL", cfg.discoverGateway()) cfg.LivenessCheckInAddress = util.ValueOr(livenessCheckInAddress, "IMUP_LIVENESS_CHECKIN_ADDRESS", fmt.Sprintf("%s/v1/realtime/livenesscheckin", ImUpAPIHost)) cfg.RealtimeAuthorized = util.ValueOr(realtimeAuthorized, "IMUP_REALTIME_AUTHORIZED", fmt.Sprintf("%s/v1/auth/realtimeAuthorized", ImUpAPIHost)) cfg.RealtimeConfig = util.ValueOr(realtimeConfig, "IMUP_REALTIME_CONFIG", fmt.Sprintf("%s/v1/realtime/config", ImUpAPIHost)) cfg.ShouldRunSpeedTestAddress = util.ValueOr(shouldRunSpeedTestAddress, "IMUP_SHOULD_RUN_SPEEDTEST_ADDRESS", fmt.Sprintf("%s/v1/realtime/shouldClientRunSpeedTest", ImUpAPIHost)) cfg.SpeedTestResultsAddress = util.ValueOr(speedTestResultsAddress, "IMUP_SPEED_TEST_RESULTS_ADDRESS", fmt.Sprintf("%s/v1/realtime/speedTestResults", ImUpAPIHost)) cfg.SpeedTestStatusUpdateAddress = util.ValueOr(speedTestStatusUpdateAddress, "IMUP_SPEED_TEST_STATUS_ADDRESS", fmt.Sprintf("%s/v1/realtime/speedTestStatusUpdate", ImUpAPIHost)) - + cfg.PingAddressInternal = util.ValueOr(pingAddressInternal, "PING_ADDRESS_INTERNAL", "") cfg.PingAddressesExternal = strings.Split(util.ValueOr(pingAddressesExternal, "PING_ADDRESS", "1.1.1.1/32,1.0.0.1/32,8.8.8.8/32,8.8.4.4/32"), ",") var err error @@ -268,6 +270,12 @@ func New() (Reloadable, error) { panic(err) } + SpeedTestTotalStr := util.ValueOr(speedTestTotal, "SPEED_TEST_DAILY_TOTAL", "6") + cfg.SpeedTestTotal, err = strconv.Atoi(SpeedTestTotalStr) + if err != nil { + panic(err) + } + logFilePathStr := util.ValueOr(logFile, "LOG_FILE", "") cfg.InsecureSpeedTest = util.BooleanValueOr(insecureSpeedTest, "INSECURE_SPEED_TEST", "false") cfg.FileLogger = util.BooleanValueOr(logToFile, "LOG_TO_FILE", "false") @@ -279,6 +287,10 @@ func New() (Reloadable, error) { cfg.logLevel = util.LevelMap(verbosity, "VERBOSITY", "info") + if !cfg.NoDiscoverGateway && cfg.PingAddressInternal == "" { + cfg.PingAddressInternal = cfg.discoverGateway() + } + var w io.Writer if logFilePathStr != "" { w = logToThisFile(logFilePathStr) @@ -565,3 +577,9 @@ func (c *config) IMUPDataLen() int { defer mu.RUnlock() return cfg.IMUPDataLength } + +func (c *config) SpeedTestDaily() int { + mu.RLock() + defer mu.RUnlock() + return cfg.SpeedTestTotal +} From c4982050702e03a6966482595a2458869b0492b4 Mon Sep 17 00:00:00 2001 From: Paul Hanyzewski Date: Fri, 11 Aug 2023 16:07:03 -0600 Subject: [PATCH 2/2] feat: update helper utility for creating pseudo random sleep times to use the new config --- helpers.go | 18 +++++++++++------ helpers_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++------ run.go | 2 +- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/helpers.go b/helpers.go index e14ce58..15b3ff5 100644 --- a/helpers.go +++ b/helpers.go @@ -8,18 +8,24 @@ import ( "gonum.org/v1/gonum/stat/distuv" ) -// timePeriodMinutes is the desired interval between tests -// the default (fixed) configuration is one tests every four hours -const timePeriodMinutes = 4 * 60 - // speedTestInterval takes advantage of a poisson distribution // to generate pseudo random speed tests // this distribution helps guarantee a consistent number of speed tests // with a smaller chance of frequent speed tests consuming large amounts of data // or saturating a network -func speedTestInterval() time.Duration { +// +// numTests is the desired number of speed tests to run, within a 24 hour period +func speedTestInterval(numTests int) time.Duration { + if numTests < 1 { + numTests = 1 + } + + if numTests > 32 { + numTests = 32 + } + t := distuv.Poisson{ - Lambda: timePeriodMinutes, + Lambda: float64(1440 / numTests), }.Rand() return time.Duration(t * float64(time.Minute)) diff --git a/helpers_test.go b/helpers_test.go index 5ae1b57..740442d 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -8,15 +8,15 @@ import ( func Test_SpeedTestFrequency(t *testing.T) { is := is.New(t) - hours := 24 - maxTests := 8 + minutesInDay := 1440 + maxTests := 6 // tf := func() int { numTests := 0 - for i := 1; i <= hours; { - t1 := speedTestInterval() - i += int(t1.Hours()) + for i := 1; i < minutesInDay; { + t1 := speedTestInterval(maxTests) + i += int(t1.Minutes()) numTests++ } @@ -24,7 +24,49 @@ func Test_SpeedTestFrequency(t *testing.T) { } results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()} - is.True(max(results) <= maxTests) + is.True(max(results) <= maxTests+1) is.True(min(results) > maxTests/2) +} + +func Test_MaxSpeedTestFrequency(t *testing.T) { + is := is.New(t) + minutesInDay := 1440 + maxTests := 32 + + // + tf := func() int { + numTests := 0 + for i := 1; i < minutesInDay; { + t1 := speedTestInterval(maxTests) + i += int(t1.Minutes()) + numTests++ + } + + return numTests + } + + results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()} + is.True(max(results) <= maxTests+2) + is.True(min(results) > maxTests/2) +} + +func Test_MinSpeedTestFrequency(t *testing.T) { + is := is.New(t) + minutesInDay := 1440 + minTests := 1 + + // + tf := func() int { + numTests := 0 + for i := 1; i < minutesInDay; { + t1 := speedTestInterval(minTests) + i += int(t1.Minutes()) + numTests++ + } + return numTests + } + + results := []int{tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf(), tf()} + is.True(max(results) <= minTests+1) } diff --git a/run.go b/run.go index a1e0c8e..ec2f984 100644 --- a/run.go +++ b/run.go @@ -229,7 +229,7 @@ func run(ctx context.Context, shutdown chan os.Signal) error { // collects speed test data using the ndt7 protocol // data is collected pseudo randomly, every 4 hours go func() { - ticker := time.NewTicker(speedTestInterval()) + ticker := time.NewTicker(speedTestInterval(imup.cfg.SpeedTestDaily())) defer ticker.Stop() for { if imup.cfg.SpeedTests() {