def IsDone(job_id): """Transactionally check whether a job is done executing. Returns True IFF the job is executing under the execution engine and that internal state is consistent with the job being "done". Raises any errors encountered in execution engine evaluation. """ job = JobFromId(job_id) if not job.use_execution_engine: return False # This comes from an eventually consistent read, but we can treat that as a # relaxed load -- if this ever is true, then that means a transaction before # this call had already marked the job "done". if job.done: return True try: context = task_module.Evaluate( job, event_module.SelectEvent(), evaluators.DispatchByTaskType({ 'find_culprit': evaluators.TaskPayloadLiftingEvaluator(exclude_keys=['commits']) })) if not context: return False for payload in context.values(): status = payload.get('status') if status in {'pending', 'ongoing'}: return False return True except task_module.Error as error: logging.error('Evaluation error: %s', error) raise
def _FormatAndPostBugCommentOnComplete(self): logging.debug('Processing outputs.') if self._IsTryJob(): # There is no comparison metric. title = '<b>%s Job complete. See results below.</b>' % _ROUND_PUSHPIN deferred.defer(_PostBugCommentDeferred, self.bug_id, '\n'.join((title, self.url)), labels=['Pinpoint-Tryjob-Completed'], _retry_options=RETRY_OPTIONS) return # There is a comparison metric. differences = [] result_values = {} if not self.use_execution_engine: differences = self.state.Differences() for change_a, change_b in differences: result_values.setdefault(change_a, self.state.ResultValues(change_a)) result_values.setdefault(change_b, self.state.ResultValues(change_b)) else: logging.debug('Execution Engine: Finding culprits.') context = task_module.Evaluate( self, event_module.SelectEvent(), evaluators.Selector( event_type='select', include_keys={'culprits', 'change', 'result_values'})) differences = [ (change_module.ReconstituteChange(change_a), change_module.ReconstituteChange(change_b)) for change_a, change_b in context.get('performance_bisection', {}).get('culprits', []) ] result_values = { change_module.ReconstituteChange(v.get('change')): v.get('result_values') for v in context.values() if 'change' in v and 'result_values' in v } if not differences: title = "<b>%s Couldn't reproduce a difference.</b>" % _ROUND_PUSHPIN deferred.defer(_PostBugCommentDeferred, self.bug_id, '\n'.join((title, self.url)), labels=['Pinpoint-No-Repro'], _retry_options=RETRY_OPTIONS) return # Collect the result values for each of the differences difference_details = [] commit_infos = [] commits_with_deltas = {} for change_a, change_b in differences: if change_b.patch: commit = change_b.patch else: commit = change_b.last_commit commit_info = commit.AsDict() values_a = result_values[change_a] values_b = result_values[change_b] difference = _FormatDifferenceForBug(commit_info, values_a, values_b, self.state.metric) difference_details.append(difference) commit_infos.append(commit_info) if values_a and values_b: mean_delta = job_state.Mean(values_b) - job_state.Mean( values_a) commits_with_deltas[commit.id_string] = (mean_delta, commit_info) deferred.defer(_UpdatePostAndMergeDeferred, difference_details, commit_infos, list(commits_with_deltas.values()), self.bug_id, self.tags, self.url, _retry_options=RETRY_OPTIONS)
def _FormatAndPostBugCommentOnComplete(self): logging.debug('Processing outputs.') if self._IsTryJob(): # There is no comparison metric. title = '<b>%s Job complete. See results below.</b>' % _ROUND_PUSHPIN deferred.defer(_PostBugCommentDeferred, self.bug_id, '\n'.join((title, self.url)), project=self.project, labels=['Pinpoint-Tryjob-Completed'], _retry_options=RETRY_OPTIONS) return # There is a comparison metric. differences = [] result_values = {} changes_examined = None if not self.use_execution_engine: differences = self.state.Differences() for change_a, change_b in differences: result_values.setdefault(change_a, self.state.ResultValues(change_a)) result_values.setdefault(change_b, self.state.ResultValues(change_b)) changes_examined = self.state.ChangesExamined() else: logging.debug('Execution Engine: Finding culprits.') context = task_module.Evaluate( self, event_module.SelectEvent(), evaluators.Selector( event_type='select', include_keys={'culprits', 'change', 'result_values'})) differences = [ (change_module.ReconstituteChange(change_a), change_module.ReconstituteChange(change_b)) for change_a, change_b in context.get('performance_bisection', {}).get('culprits', []) ] result_values = { change_module.ReconstituteChange(v.get('change')): v.get('result_values') for v in context.values() if 'change' in v and 'result_values' in v } if not differences: # When we cannot find a difference, we want to not only update the issue # with that (minimal) information but also automatically mark the issue # WontFix. This is based on information we've gathered in production that # most issues where we find Pinpoint cannot reproduce the difference end # up invariably as "Unconfirmed" with very little follow-up. title = "<b>%s Couldn't reproduce a difference.</b>" % _ROUND_PUSHPIN deferred.defer(_PostBugCommentDeferred, self.bug_id, '\n'.join((title, self.url)), project=self.project, labels=['Pinpoint-No-Repro'], status='WontFix', _retry_options=RETRY_OPTIONS) return # Collect the result values for each of the differences bug_update_builder = job_bug_update.DifferencesFoundBugUpdateBuilder( self.state.metric) bug_update_builder.SetExaminedCount(changes_examined) for change_a, change_b in differences: if change_b.patch: commit = change_b.patch else: commit = change_b.last_commit values_a = result_values[change_a] values_b = result_values[change_b] bug_update_builder.AddDifference(commit, values_a, values_b) deferred.defer(job_bug_update.UpdatePostAndMergeDeferred, bug_update_builder, self.bug_id, self.tags, self.url, self.project, _retry_options=RETRY_OPTIONS)