Concurrent PR modification using PullRequestService#update

Using PullRequestService#update on different listeners for the same PR, we got concurrent PR modification, for example updating PR reviewers.

  • Listerner #1 added reviewers to PR:
    Listener1
  • Listener #2 added other reviewers to PR, but implicity removed reviewers added by listener #1 at the same time:
    Listener2

How can we avoid this concurrent modification, so PR will keep all reviewers set by listeners?
Thanks in advance.
Cheers.

1 Like

Hi!

Could you share more of the code that you’ve written? Pull requests use Optimistic Locking to avoid concurrent updates overwriting each other’s changes. This is achieved with the version field of the PullRequestUpdateRequest class. The version field on that class has to match the PullRequest’s version field when updating it, if it is different (because it may have been updated concurrently) the update will fail with an EntityOutOfDateException. If this occurs, you can try reloading the PullRequest object from the PullRequestService and trying again.

Cheers,
Wolfgang Kritzinger
Bitbucket Data Center

1 Like

Could you share more of the code that you’ve written?

We set PR reviewers via different listeners (e.g., Listener #1, Listener #2, ecc.) using the following example code:

import org.springframework.stereotype.Component;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.event.api.EventListener;
import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent;
import com.atlassian.bitbucket.pull.PullRequestService;
import com.atlassian.bitbucket.pull.PullRequest;

@Component
public class MyListener {
	protected final PullRequestService pullRequestService;
	
	public MyListener(@ComponentImport PullRequestService pullRequestService) {
		this.pullRequestService = pullRequestService;
	}
	
	@EventListener
	public void onPullRequestOpenedEvent(PullRequestOpenedEvent event) {
		PullRequest pullRequest = event.getPullRequest();
		Set<String> reviewers = new HashSet<String>();
		...
		pullRequestService.update(
				new PullRequestUpdateRequest.Builder(pullRequest, pullRequest.getVersion())
			        .title(pullRequest.getTitle())	// mandatory as per {@code com.atlassian.bitbucket.pull.PullRequestUpdateRequest#PullRequestUpdateRequest}
	        		.reviewers(reviewers)
			        .build()
		);
	}
}

The version field on that class has to match the PullRequest ’s version field when updating it, if it is different (because it may have been updated concurrently) the update will fail with an EntityOutOfDateException .

No exceptions are raised, the PullRequestService#update method returns with success.

Thanks in advance for your support, @wkritzinger.
Cheers.

@BIF01 In your code, the Set<String> reviewers needs to be pre-populated with the reviewers from the pull request and and reviewers you wish to add have to be addded to the set.

pullRequest.getReviewers().forEach(reviewer -> reviewers.add(reviewer.getUser().getName()));
reviewers.add("reviewer 1");

Yes, it’s already like that, we just omitted part of the code (see ...).
The missing code is:

Set<String> reviewers = new HashSet<String>();
for (PullRequestParticipant reviewer : pullRequest.getReviewers()) {
	reviewers.add(reviewer.getUser().getName());
}
reviewers.add("reviewer");