diff --git a/lib/Command/TargetAdd.php b/lib/Command/TargetAdd.php index b56d250..0d24c1b 100644 --- a/lib/Command/TargetAdd.php +++ b/lib/Command/TargetAdd.php @@ -88,19 +88,21 @@ HELP); * @return callable(int, int): void */ private static function progress(OutputInterface $output): callable { - $start = microtime(true); $shown = 0.0; + $first = null; // [time, files done] when the first files were in $inPlace = $output->isDecorated(); - return static function (int $done, int $total) use ($output, $start, &$shown, $inPlace): void { + return static function (int $done, int $total) use ($output, &$shown, &$first, $inPlace): void { $now = microtime(true); + if ($first === null && $done > 0) { + $first = [$now, $done]; + } if ($done < $total && $now - $shown < ($inPlace ? 1 : 30)) { return; } $shown = $now; $line = sprintf('Reading the index: %s/%s files (%d%%)', number_format($done), number_format($total), intdiv(100 * $done, max(1, $total))); - $elapsed = $now - $start; - if ($done < $total && $done > 0 && $elapsed >= 5) { - $left = (int)ceil($elapsed / (float)$done * (float)($total - $done) / 60.0); + $left = $first === null ? null : self::minutesLeft($now - $first[0], $done - $first[1], $total - $done, $total); + if ($left !== null) { $line .= $left <= 1 ? ', less than a minute left' : sprintf(', about %d min left', $left); } if ($inPlace) { @@ -110,4 +112,17 @@ HELP); } }; } + + /** + * Minutes left for reading the index, from the rate since the first files were in: the first + * file also carries the time to connect, which made an S3 location with 4,568 index files + * say "about 642 min left" for something done within a minute. No estimate until 5% of the + * files, or 50 files, came in after that, and at least 5 seconds passed. + */ + public static function minutesLeft(float $elapsed, int $done, int $left, int $total): ?int { + if ($left <= 0 || $done <= 0 || $elapsed < 5.0 || ($done < 50 && $done * 20 < $total)) { + return null; + } + return (int)ceil($elapsed / (float)$done * (float)$left / 60.0); + } } diff --git a/tests/Unit/Command/TargetAddEstimateTest.php b/tests/Unit/Command/TargetAddEstimateTest.php new file mode 100644 index 0000000..8c63a4c --- /dev/null +++ b/tests/Unit/Command/TargetAddEstimateTest.php @@ -0,0 +1,30 @@ +assertNull(TargetAdd::minutesLeft(6.0, 10, 4558, 4568)); + } + + public function testEstimateOnceEnoughFilesCameIn(): void { + // 300 files in 10 s: 4,268 left at 30 a second is about 2.4 minutes + $this->assertSame(3, TargetAdd::minutesLeft(10.0, 300, 4268, 4568)); + } + + public function testFivePercentOfASmallIndexIsEnough(): void { + // 6 of 100 files in 6 s: 94 left at one a second is about 1.6 minutes + $this->assertSame(2, TargetAdd::minutesLeft(6.0, 6, 94, 100)); + } + + public function testNoEstimateInTheFirstSecondsOrWhenDone(): void { + $this->assertNull(TargetAdd::minutesLeft(2.0, 500, 4000, 4500)); + $this->assertNull(TargetAdd::minutesLeft(60.0, 4568, 0, 4568)); + } +}