/** * Calculate a new estimate based on the available data, and store it to the * database. * * @param array $config */ public function estimate(array $config = array()) { $config += array('iterations' => MonteCarlo::DEFAULT_ITERATIONS, 'timeout' => MonteCarlo::DEFAULT_TIME_LIMIT); $db = $this->db; // Group samples by week. $samples = new TimeGroupedSampleSetCollection(604800); $samplesResultSet = $db->createQueryBuilder()->select('s.when', 'sv_bugs.value + sv_tasks.value + IFNULL(sv_plans.value, 0) AS value')->from('samples', 's')->innerJoin('s', 'sample_values', 'sv_bugs', 's.version = sv_bugs.version && s.when = sv_bugs.when && sv_bugs.key="critical_bugs"')->innerJoin('s', 'sample_values', 'sv_tasks', 's.version = sv_tasks.version && s.when = sv_tasks.when && sv_tasks.key="critical_tasks"')->leftJoin('s', 'sample_values', 'sv_plans', 's.version = sv_plans.version && s.when = sv_plans.when && sv_plans.key="critical_plans"')->where('s.version = "8.0"')->andWhere('sv_bugs.value IS NOT NULL')->andWhere('sv_tasks.value IS NOT NULL')->andWhere('NOT (sv_plans.when IS NULL XOR sv_plans.value IS NULL)')->orderBy($db->quoteIdentifier('when'), 'ASC')->execute(); $lastResult = null; while ($result = $samplesResultSet->fetchObject()) { $lastResult = $result; $samples->insert(new Sample($db->convertToPhpValue($result->when, 'datetime')->getTimestamp(), $db->convertToPhpValue($result->value, 'smallint'))); } // Insert empty before run, update if successful. $db->insert($db->quoteIdentifier('estimates'), array($db->quoteIdentifier('when') => $lastResult->when, $db->quoteIdentifier('version') => '8.0', $db->quoteIdentifier('data') => '', $db->quoteIdentifier('started') => $db->convertToDatabaseValue(new DateTime(), 'datetime'))); // Close connection during processing to prevent "Database has gone away" exception. $db->close(); // Give samples twice the weight of those from six months before. $geometricRandom = new GeometricWeighted(new Random(), 0, $samples->length() - 1, pow(2, 1 / 24)); $sampleSelector = new TimeGroupedRandomSampleSelector($samples, $geometricRandom); $monteCarlo = new MonteCarlo($sampleSelector); $update = array(); try { set_time_limit($config['timeout'] + 30); $estimateDistribution = $monteCarlo->runDistribution($config['iterations'], MonteCarlo::DEFAULT_BUCKET_SIZE, $config['timeout']); $update += array($db->quoteIdentifier('note') => 'Run completed in ' . (time() - $_SERVER['REQUEST_TIME']) . ' seconds' . ' after ' . ($estimateDistribution->getSuccessCount() + $estimateDistribution->getFailureCount()) . ' iterations'); } catch (IncreasingException $e) { $estimateDistribution = $e->getDistribution(); $update += array($db->quoteIdentifier('note') => 'Run terminated due to increasing issue count' . ' after ' . ($estimateDistribution->getSuccessCount() + $estimateDistribution->getFailureCount()) . ' iterations'); } catch (TimeoutException $e) { $estimateDistribution = $e->getDistribution(); $update += array($db->quoteIdentifier('note') => 'Run terminated due to timeout' . ' after ' . ($estimateDistribution->getSuccessCount() + $estimateDistribution->getFailureCount()) . ' iterations'); } try { $estimateInterval = $estimateDistribution->getMedian(true); $estimateDate = new DateTime('@' . $_SERVER['REQUEST_TIME']); $estimateDate->add(DateInterval::createFromDateString($estimateInterval . ' seconds')); $update += array($db->quoteIdentifier('estimate') => $db->convertToDatabaseValue($estimateDate, 'date')); } catch (\RuntimeException $e) { $update += array($db->quoteIdentifier('estimate') => null); } $update += array($db->quoteIdentifier('data') => serialize($estimateDistribution), $db->quoteIdentifier('completed') => $db->convertToDatabaseValue(new DateTime(), 'datetime')); $db->connect(); $db->update($db->quoteIdentifier('estimates'), $update, array($db->quoteIdentifier('when') => $lastResult->when, $db->quoteIdentifier('version') => '8.0')); }
/** * Test with a sample set that will never complete and hit the timeout. * * @expectedException \DrupalReleaseDate\MonteCarlo\TimeoutException * * @expectedExceptionMessage Run aborted during iteration 1 * * @covers \DrupalReleaseDate\MonteCarlo * @uses \DrupalReleaseDate\EstimateDistribution * @uses \DrupalReleaseDate\MonteCarlo\TimeoutException<extended> * @uses \DrupalReleaseDate\NumberGenerator\Cyclic<extended> * @uses \DrupalReleaseDate\Sampling\Sample * @uses \DrupalReleaseDate\Sampling\SampleSet * @uses \DrupalReleaseDate\Sampling\SampleSetRandomSampleSelector */ public function testTimeoutRun() { $sampleset = new SampleSet(); $sampleset->insert(new Sample(10, 10)); $sampleset->insert(new Sample(20, 10)); $randomGenerator = new CyclicGenerator(1, $sampleset->length() - 1); $sampleSelector = new SampleSetRandomSampleSelector($sampleset, $randomGenerator); $montecarlo = new MonteCarlo($sampleSelector); $median = $montecarlo->runMedian(100, 10, 5); }