Skip to content

Commit b45a789

Browse files
authored
Implement random scheduling times per APP_KEY to prevents thundering herd problems (librenms#18246)
* Implement random scheduling times per host to prevents thundering herd problems The randomization is stable per host, meaning it will always return the same "random" value per input. This is needed so the scheduler doesn't run the same job multiple times * Apply fixes from StyleCI
1 parent 0814287 commit b45a789

File tree

4 files changed

+54
-14
lines changed

4 files changed

+54
-14
lines changed

LibreNMS/Util/Time.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Carbon\CarbonInterface;
3030
use Carbon\Exceptions\InvalidFormatException;
3131
use Illuminate\Support\Carbon;
32+
use Illuminate\Support\Facades\Config;
3233

3334
class Time
3435
{
@@ -173,4 +174,33 @@ public static function durationToSeconds(string $duration): int
173174

174175
return $duration === '' ? 0 : 300;
175176
}
177+
178+
/**
179+
* Return a random time between the two given times.
180+
*/
181+
public static function randomBetween(string|int $min, string|int $max): Carbon
182+
{
183+
$time = new Carbon($min);
184+
185+
$time->addSeconds(mt_rand(0, (int) $time->diffInSeconds(new Carbon($max), true)));
186+
187+
return $time;
188+
}
189+
190+
/**
191+
* Return a psedudo random time between the two given times.
192+
* The same time will always be returned for a given APP_KEY
193+
*/
194+
public static function pseudoRandomBetween(string|int $min, string|int $max, string $format = 'H:i'): string
195+
{
196+
// Seed the random number generator to get consistent results for a given APP_KEY
197+
mt_srand(crc32(Config::get('app.key') . $min . $max));
198+
199+
$time = self::randomBetween($min, $max);
200+
201+
// Need to restore the seed after
202+
mt_srand();
203+
204+
return $time->format($format);
205+
}
176206
}

app/Console/Commands/MaintenanceFetchOuis.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,13 @@ class MaintenanceFetchOuis extends LnmsCommand
2020

2121
protected string $mac_oui_url = 'https://www.wireshark.org/download/automated/data/manuf';
2222
protected int $min_refresh_days = 6;
23-
protected int $max_wait_seconds = 900;
2423
protected int $upsert_chunk_size = 1000;
2524

2625
public function __construct()
2726
{
2827
parent::__construct();
2928

3029
$this->addOption('force', null, InputOption::VALUE_NONE);
31-
$this->addOption('wait', null, InputOption::VALUE_NONE);
3230
}
3331

3432
/**
@@ -56,14 +54,6 @@ public function handle(): int
5654
return 0;
5755
}
5856

59-
// wait for 0-15 minutes to prevent stampeding herd
60-
if ($this->option('wait')) {
61-
$seconds = random_int(1, $this->max_wait_seconds);
62-
$minutes = (int) round($seconds / 60);
63-
$this->info(trans_choice('commands.maintenance:fetch-ouis.waiting', $minutes, ['minutes' => $minutes]));
64-
sleep($seconds);
65-
}
66-
6757
$this->line(trans('commands.maintenance:fetch-ouis.starting'));
6858

6959
try {

routes/console.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Jobs\PingCheck;
66
use Illuminate\Support\Facades\Artisan;
77
use Illuminate\Support\Facades\Schedule;
8+
use LibreNMS\Util\Time;
89
use Symfony\Component\Process\Process;
910

1011
/*
@@ -199,12 +200,12 @@
199200

200201
// schedule maintenance, should be after all others
201202
$maintenance_log_file = Config::get('log_dir') . '/maintenance.log';
202-
Schedule::command(MaintenanceFetchOuis::class, ['--wait'])
203-
->weeklyOn(0, '1:00')
203+
Schedule::command(MaintenanceFetchOuis::class)
204+
->weeklyOn(0, Time::pseudoRandomBetween('01:00', '01:59'))
204205
->onOneServer()
205206
->appendOutputTo($maintenance_log_file);
206207

207-
Schedule::command(MaintenanceCleanupNetworks::class, [])
208-
->weeklyOn(0, '2:00')
208+
Schedule::command(MaintenanceCleanupNetworks::class)
209+
->weeklyOn(0, Time::pseudoRandomBetween('02:00', '02:59'))
209210
->onOneServer()
210211
->appendOutputTo($maintenance_log_file);

tests/Unit/TimeUtilityTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,23 @@ public function testParseInput(): void
8484

8585
$this->assertNull(Time::parseInput('not a date'));
8686
}
87+
88+
public function testRandomTimeBetween(): void
89+
{
90+
$start = time();
91+
$end = time() + 3600;
92+
$randomTime = Time::randomBetween($start, $end)->format('U');
93+
$this->assertGreaterThanOrEqual($start, $randomTime);
94+
$this->assertLessThanOrEqual($end, $randomTime);
95+
96+
// test with pesudo random
97+
$randomTime = Time::pseudoRandomBetween($start, $end, 'U');
98+
$this->assertGreaterThanOrEqual($start, $randomTime);
99+
$this->assertLessThanOrEqual($end, $randomTime);
100+
101+
$this->assertEquals(
102+
Time::pseudoRandomBetween($start, $end),
103+
Time::pseudoRandomBetween($start, $end),
104+
);
105+
}
87106
}

0 commit comments

Comments
 (0)