private function jobThatWasCreated($relativeTime) { $wasCreatedAt = T\Moment::fromTimestamp(strtotime($relativeTime), T\now()->seconds()); $job = $this->getMockBuilder('Recruiter\\JobAfterFailure')->disableOriginalConstructor()->setMethods(['createdAt', 'scheduleAt'])->getMock(); $job->expects($this->any())->method('createdAt')->will($this->returnValue($wasCreatedAt)); return $job; }
public function cleanScheduled(Interval $gracePeriod = null) { $upperLimit = T\now(); if (!is_null($gracePeriod)) { $upperLimit = $upperLimit->before($gracePeriod); } return $this->repository->cleanScheduled($upperLimit); }
public function testNotWithstandingCrashesJobsAreEventuallyPerformed() { $this->limitTo(100)->forAll(Generator\bind(Generator\choose(1, 4), function ($workers) { return Generator\tuple(Generator\constant($workers), Generator\seq(Generator\oneOf(Generator\map(function ($durationAndTag) { list($duration, $tag) = $durationAndTag; return ['enqueueJob', $duration, $tag]; }, Generator\tuple(Generator\nat(), Generator\elements(['generic', 'fast-lane']))), Generator\map(function ($workerIndex) { return ['restartWorkerGracefully', $workerIndex]; }, Generator\choose(0, $workers - 1)), Generator\map(function ($workerIndex) { return ['restartWorkerByKilling', $workerIndex]; }, Generator\choose(0, $workers - 1)), Generator\constant('restartRecruiterGracefully'), Generator\constant('restartRecruiterByKilling'), Generator\map(function ($milliseconds) { return ['sleep', $milliseconds]; }, Generator\choose(1, 1000))))); }))->hook(Listener\log('/tmp/recruiter-test-iterations.log'))->hook(Listener\collectFrequencies())->disableShrinking()->then(function ($tuple) { list($workers, $actions) = $tuple; $this->clean(); $this->start($workers); foreach ($actions as $action) { $this->logAction($action); if (is_array($action)) { $arguments = $action; $method = array_shift($arguments); call_user_func_array([$this, $method], $arguments); } else { $this->{$action}(); } } $estimatedTime = max(count($actions) * 4, 60); Timeout::inSeconds($estimatedTime, function () { return "all {$this->jobs} jobs to be performed. Now is " . date('c') . " Logs: " . $this->files(); })->until(function () { return $this->jobRepository->countArchived() === $this->jobs; }); $at = T\now(); $statistics = $this->recruiter->statistics($tag = null, $at); $this->assertInvariantsOnStatistics($statistics); // TODO: remove duplication $statisticsByTag = []; $cumulativeThroughput = 0; foreach (['generic', 'fast-lane'] as $tag) { $statisticsByTag[$tag] = $this->recruiter->statistics($tag, $at); $this->assertInvariantsOnStatistics($statisticsByTag[$tag]); $cumulativeThroughput += $statisticsByTag[$tag]['throughput']['value']; } var_dump($statistics, $statisticsByTag); // TODO: add tolerance $this->assertEquals($statistics['throughput']['value'], $cumulativeThroughput); }); }
private static function estimateHowManyRetriesIn($timeTable) { $now = T\now()->seconds(); $howManyRetries = 0; $timeWindowInSeconds = 0; foreach ($timeTable as $timeWindow => $rescheduleTime) { $timeWindowInSeconds = $now - strtotime($timeWindow, $now) - $timeWindowInSeconds; if ($timeWindowInSeconds <= 0) { throw new Exception("Time window `{$timeWindow}` is invalid, must be in the past"); } $rescheduleTimeInSeconds = strtotime($rescheduleTime, $now) - $now; if ($rescheduleTimeInSeconds <= 0) { throw new Exception("Reschedule time `{$rescheduleTime}` is invalid, must be in the future"); } if ($rescheduleTimeInSeconds > $timeWindowInSeconds) { throw new Exception("Reschedule time `{$rescheduleTime}` is invalid, must be greater than the time window"); } $howManyRetries += floor($timeWindowInSeconds / $rescheduleTimeInSeconds); } return $howManyRetries; }
private function aJob() { $workable = $this->getMockBuilder('Recruiter\\Workable')->getMock(); return Job::around($workable, $this->repository)->scheduleAt(T\now()->before(T\seconds(5))); }
public function inBackground() { return $this->scheduleAt(T\now()); }
public function completedWith($result) { $this->endedAt = T\now(); $this->completedWith = $result; }
public function recentHistory($group = null, T\Moment $at = null, array $query = []) { if ($at === null) { $at = T\now(); } $lastMinute = array_merge($query, ['last_execution.ended_at' => ['$gt' => T\MongoDate::from($at->before(T\minute(1))), '$lte' => T\MongoDate::from($at)]]); if ($group !== null) { $lastMinute['group'] = $group; } $document = $this->archived->aggregate($pipeline = [['$match' => $lastMinute], ['$project' => ['latency' => ['$subtract' => ['$last_execution.started_at', '$last_execution.scheduled_at']], 'execution_time' => ['$subtract' => ['$last_execution.ended_at', '$last_execution.started_at']]]], ['$group' => ['_id' => 1, 'throughput' => ['$sum' => 1], 'latency' => ['$avg' => '$latency'], 'execution_time' => ['$avg' => '$execution_time']]]]); if (!$document['ok']) { throw new RuntimeException("Pipeline failed: " . var_export($pipeline, true)); } if (count($document['result']) === 0) { $throughputPerMinute = 0.0; $averageLatency = 0.0; $averageExecutionTime = 0; } else { if (count($document['result']) === 1) { $throughputPerMinute = (double) $document['result'][0]['throughput']; $averageLatency = $document['result'][0]['latency'] / 1000; $averageExecutionTime = $document['result'][0]['execution_time'] / 1000; } else { throw new RuntimeException("Result was not ok: " . var_export($document, true)); } } return ['throughput' => ['value' => $throughputPerMinute, 'value_per_second' => $throughputPerMinute / 60.0], 'latency' => ['average' => $averageLatency], 'execution_time' => ['average' => $averageExecutionTime]]; }