Skip to content

Commit 5f42b05

Browse files
committed
Solved N+1 problem in Jira API calls for the "backport" command
1 parent 54f1b0e commit 5f42b05

File tree

1 file changed

+96
-19
lines changed

1 file changed

+96
-19
lines changed

src/JiraCLI/Issue/IssueCloner.php

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ class IssueCloner
5252
*/
5353
protected $queryFields = array('summary', 'issuelinks');
5454

55+
/**
56+
* Project of an issue.
57+
*
58+
* @var array
59+
*/
60+
private $_issueProjects = array();
61+
5562
/**
5663
* IssueCloner constructor.
5764
*
@@ -81,23 +88,101 @@ public function getIssues($jql, $link_name, $link_direction, array $link_project
8188
$walker = new Walker($this->jiraApi);
8289
$walker->push($jql, implode(',', $this->_getQueryFields()));
8390

84-
$ret = array();
91+
$cache = array();
8592

8693
foreach ( $walker as $issue ) {
8794
foreach ( $link_project_keys as $link_project_key ) {
88-
$linked_issue = $this->_getLinkedIssue($issue, $link_name, $link_direction, $link_project_key);
95+
$linked_issue = $this->_getLinkedIssue($issue, $link_name, $link_direction);
8996

90-
if ( is_object($linked_issue) && $this->isAlreadyProcessed($issue, $linked_issue) ) {
91-
continue;
97+
if ( $linked_issue !== null ) {
98+
$this->_addToProjectDetectionQueue($linked_issue);
9299
}
93100

94-
$ret[] = array($issue, $linked_issue, $link_project_key);
101+
$cache[] = array($issue, $linked_issue, $link_project_key);
102+
}
103+
}
104+
105+
$this->_processProjectDetectionQueue();
106+
107+
$ret = array();
108+
109+
foreach ( $cache as $cached_data ) {
110+
list($issue, $linked_issue, $link_project_key) = $cached_data;
111+
112+
if ( $linked_issue === null ) {
113+
$ret[] = $cached_data;
114+
continue;
115+
}
116+
117+
if ( $this->isLinkAccepted($issue, $linked_issue)
118+
&& $this->_getIssueProject($linked_issue) === $link_project_key
119+
&& !$this->isAlreadyProcessed($issue, $linked_issue)
120+
) {
121+
$ret[] = $cached_data;
95122
}
96123
}
97124

98125
return $ret;
99126
}
100127

128+
/**
129+
* Adds an issue to the project detection queue.
130+
*
131+
* @param Issue $issue Issue.
132+
*
133+
* @return void
134+
*/
135+
private function _addToProjectDetectionQueue(Issue $issue)
136+
{
137+
$this->_issueProjects[$issue->getKey()] = null;
138+
}
139+
140+
/**
141+
* Detects projects for queued issues.
142+
*
143+
* @return void
144+
*/
145+
private function _processProjectDetectionQueue()
146+
{
147+
$issues_without_projects = array_keys(array_filter($this->_issueProjects, function ($project_key) {
148+
return $project_key === null;
149+
}));
150+
151+
if ( !$issues_without_projects ) {
152+
return;
153+
}
154+
155+
$walker = new Walker($this->jiraApi);
156+
$walker->push(
157+
'key IN (' . implode(',', $issues_without_projects) . ')',
158+
'project'
159+
);
160+
161+
foreach ( $walker as $linked_issue ) {
162+
$linked_issue_project_data = $linked_issue->get('project');
163+
$this->_issueProjects[$linked_issue->getKey()] = $linked_issue_project_data['key'];
164+
}
165+
}
166+
167+
/**
168+
* Returns an issue project.
169+
*
170+
* @param Issue $issue Issue.
171+
*
172+
* @return string
173+
* @throws \RuntimeException When issue's project isn't available.
174+
*/
175+
private function _getIssueProject(Issue $issue)
176+
{
177+
$issue_key = $issue->getKey();
178+
179+
if ( array_key_exists($issue_key, $this->_issueProjects) ) {
180+
return $this->_issueProjects[$issue_key];
181+
}
182+
183+
throw new \RuntimeException('The issue "' . $issue_key . '" project wasn\'t queried.');
184+
}
185+
101186
/**
102187
* Builds custom field map.
103188
*
@@ -131,17 +216,16 @@ private function _getQueryFields()
131216
}
132217

133218
/**
134-
* Returns issue, which backports given issue.
219+
* Returns issue, which backports given issue (project not matched yet).
135220
*
136-
* @param Issue $issue Issue.
137-
* @param string $link_name Link name.
138-
* @param integer $link_direction Link direction.
139-
* @param string $link_project_key Link project key.
221+
* @param Issue $issue Issue.
222+
* @param string $link_name Link name.
223+
* @param integer $link_direction Link direction.
140224
*
141225
* @return Issue|null
142226
* @throws \InvalidArgumentException When link direction isn't valid.
143227
*/
144-
private function _getLinkedIssue(Issue $issue, $link_name, $link_direction, $link_project_key)
228+
private function _getLinkedIssue(Issue $issue, $link_name, $link_direction)
145229
{
146230
foreach ( $issue->get('issuelinks') as $issue_link ) {
147231
if ( $issue_link['type']['name'] !== $link_name ) {
@@ -159,14 +243,7 @@ private function _getLinkedIssue(Issue $issue, $link_name, $link_direction, $lin
159243
}
160244

161245
if ( array_key_exists($check_key, $issue_link) ) {
162-
$linked_issue = new Issue($issue_link[$check_key]);
163-
$linked_issue_data = $this->jiraApi->getIssue($linked_issue->getKey(), 'project')->getResult();
164-
165-
if ( $this->isLinkAccepted($issue, $linked_issue)
166-
&& $linked_issue_data['fields']['project']['key'] === $link_project_key
167-
) {
168-
return $linked_issue;
169-
}
246+
return new Issue($issue_link[$check_key]);
170247
}
171248
}
172249

0 commit comments

Comments
 (0)