2

I'm using a ProgressBar for my console command, and allow the command to restart at some point in the middle of the processing:

$itemCount = 1_000_000;
$startItem = 150_000;

$progressBar = new ProgressBar($output, $itemCount);
$progressBar->setFormat(
    $progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);

$progressBar->start();

if ($startItem !== 0) {
    // unfortunately taken into account in ETA calculation
    $progressBar->advance($startItem);
}

for ($i = $startItem; $i < $itemCount; $i++) {
    usleep(50_000);
    $progressBar->advance();
}

The issue is, even though the command processes only ~20 items per second, the ETA is calculated just as if $startItem items had been processed right at the beginning of the processing:

150038/1000000 [====>-----------------------]  15% 2 secs/13 secs 20.0 MiB

I'm expecting the ETA to be ~12 hours here, not 13 secs.

How can I fix this? Can I somehow give the starting position of the progress bar before starting it, so that the ETA is properly calculated?

yivi
  • 42,438
  • 18
  • 116
  • 138
BenMorel
  • 34,448
  • 50
  • 182
  • 322
  • I can confirm this. Might be best to post an issue on the [Symfony Repo](https://github.com/symfony/symfony). – Bossman May 03 '22 at 11:54

1 Answers1

2

This is not supported by the ProgressBar helper.

The estimated time is calculated by the ProgressBar#getEstimated() method, which is very simple:

public function getEstimated(): float
{
    if (!$this->step) {
         return 0;
    }

    return round((time() - $this->startTime) / $this->step * $this->max);
}

This only takes into account the amount of time since the progress bar has been started (which is set when one calls ProgressBar#start(), or on the constructor otherwise), and the total amount of "steps" it's taken so far.

There is no distinction between "real" steps, and "fake" steps. Even if one were to modify ProgresBar#step before calling start() (e.g. on the constructor), the result would be the same, since the calculation for estimated time would work exactly the same way.

The class is marked final, which I think it's unfortunate for a helper class, so you cannot simply extend it and add your logic (e.g. with an additional int $resumedAt property that one could use when calculating the estimated remaining time).

If you really, really need this, I'd simply make a copy of the class in your project and add the necessary logic there.

As a simple proof of concept, I'd copied ProgressBar into App\ProgressBar, and added this:

private int $resumedSteps = 0;

public function resume(int $max = null, int $step = 0): void
{

    $this->startTime = time();
    $this->resumedStep = $step;
    $this->step = $step;

    if (null !== $max) {
        $this->setMaxSteps($max);
    }

    $this->setProgress($step);

    $this->display();
}

public function getEstimated(): float
{
   if ($this->step === 0 || $this->step === $this->resumedStep) {
      return 0;
   }

   return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * $this->max);
}

public function getRemaining(): float
{
   if ($this->step === 0 || $this->step === $this->resumedStep) {
     return 0;
   }

   return round((time() - $this->startTime) / ($this->step - $this->resumedStep) * ($this->max - $this->step));
}

One would simply use this as:

$itemCount = 1_000_000;

$progressBar = new App\ProgressBar($output, $itemCount);
$progressBar->setFormat(
    $progressBar->getFormatDefinition(ProgressBar::FORMAT_DEBUG)
);

$startItem > 0 ? $progressBar->resume($startItem) : $progressBar->start();
yivi
  • 42,438
  • 18
  • 116
  • 138
  • I've been playing around with this and it needs an additional property like `$resumedAt` as you mentioned. IMO it defiantly needs this support added. – Bossman May 03 '22 at 12:32
  • 2
    If I can make time later this week, I'll submit a PR to symfony. – yivi May 03 '22 at 12:57
  • Nice one. Almost the same with what i experimented with.. – Bossman May 03 '22 at 13:02
  • Thank you for your extensive answer. Looking forward to your Symfony PR! Could you please post a link here if you proceed? – BenMorel May 03 '22 at 15:32
  • 2
    Finally, found the time today, @BenMorel. Let's see if it flies: https://github.com/symfony/symfony/pull/46242 – yivi May 03 '22 at 16:25