Skip to content

Commit 14e2169

Browse files
Performance improvement by doing a checkout and merge only when the label is changing or if it's refresh time
Signed-off-by: Henri Tremblay <[email protected]>
1 parent 1ef3499 commit 14e2169

File tree

3 files changed

+60
-14
lines changed

3 files changed

+60
-14
lines changed

spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepository.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository
151151

152152
private final ObservationRegistry observationRegistry;
153153

154+
/**
155+
* Contains the previousLabel that was checked out to prevent doing it again.
156+
*
157+
* @implNote
158+
* The field is not volatile since we tend to lock around the {@link #refresh(String)} method which uses it.
159+
* However, this method is public si maybe it's a bad bet.
160+
*/
161+
private String previousLabel = "$%^&"; // none existing branch
162+
154163
/**
155164
* This lock is used to ensure thread safety between accessing the local git repo from
156165
* both the ResourceController and the EnvironmentController. See <a href=
@@ -325,18 +334,27 @@ public String refresh(String label) {
325334
Git git = null;
326335
try {
327336
git = createGitClient();
337+
338+
// If we need to pull (which means we need to refresh from the remote),
339+
// we need to fetch, checkout and merge, even on the same label, because if that label is a tag and someone nastily moved it, we need a checkout
340+
// If we do not need to pull
341+
// case a -> we are on the same label, we should do nothing
342+
// case b -> we should checkout
328343
if (shouldPull(git)) {
329344
FetchResult fetchStatus = fetch(git, label);
330345
if (this.deleteUntrackedBranches && fetchStatus != null) {
331346
deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git);
332347
}
333-
}
334348

335-
// checkout after fetch so we can get any new branches, tags, ect.
336-
// if nothing to update so just checkout and merge.
337-
// Merge because remote branch could have been updated before
338-
checkout(git, label);
339-
tryMerge(git, label);
349+
// checkout after fetch, so we can get any new branches, tags, etc.
350+
checkout(git, label);
351+
// merge because remote branch could have been updated
352+
tryMerge(git, label);
353+
}
354+
else if (!label.equals(this.previousLabel)) {
355+
// checkout the new label
356+
checkout(git, label);
357+
}
340358

341359
// always return what is currently HEAD as the version
342360
return git.getRepository().findRef("HEAD").getObjectId().getName();
@@ -496,7 +514,9 @@ private Ref checkout(Git git, String label) throws GitAPIException {
496514
// works for tags and local branches
497515
checkout.setName(label);
498516
}
499-
return checkout.call();
517+
Ref call = checkout.call();
518+
previousLabel = label; // do it after the call to prevent changing it when it fails
519+
return call;
500520
}
501521

502522
protected boolean shouldPull(Git git) throws GitAPIException {

spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryIntegrationTests.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -533,12 +533,13 @@ public void testNewCommitIDWithRefreshRate() throws Exception {
533533
StreamUtils.copy("foo: barNewCommit", Charset.defaultCharset(), out);
534534
testData.getServerGit().getGit().add().addFilepattern("bar.properties").call();
535535
testData.getServerGit().getGit().commit().setMessage("Updated for pull").call();
536+
testData.getServerGit().getGit().tag().setName("testTag").call();
536537
String updatedRemoteVersion = getCommitID(testData.getServerGit().getGit(), "master");
537538

538539
// Set refresh rate to 60 seconds (now it will fetch the remote repo only once)
539540
testData.getRepository().setRefreshRate(60);
540541

541-
// Ask test label configuration first
542+
// Ask test label configuration first, it will fetch but fail on the checkout
542543
try {
543544
testData.getRepository().findOne("bar", "staging", "test");
544545
fail("Should have thrown NoSuchLabelException.");
@@ -547,10 +548,16 @@ public void testNewCommitIDWithRefreshRate() throws Exception {
547548
// OK
548549
}
549550

550-
// do a normal request and verify we get the new version
551+
// do a normal request, the previous branch was the "master" branch, so it won't move
551552
environment = testData.getRepository().findOne("bar", "staging", "master");
552-
assertThat(updatedRemoteVersion).isEqualTo(environment.getVersion());
553+
assertThat(startingRemoteVersion).isEqualTo(environment.getVersion());
553554
Object fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
555+
assertThat("bar").isEqualTo(fooProperty);
556+
557+
// get the test tag which is on the latest commit. It was fetched so it will be found
558+
environment = testData.getRepository().findOne("bar", "staging", "testTag");
559+
assertThat(updatedRemoteVersion).isEqualTo(environment.getVersion());
560+
fooProperty = ConfigServerTestUtils.getProperty(environment, "bar.properties", "foo");
554561
assertThat("barNewCommit").isEqualTo(fooProperty);
555562

556563
// request the prior commit ID and make sure we get it

spring-cloud-config-server/src/test/java/org/springframework/cloud/config/server/environment/JGitEnvironmentRepositoryTests.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import static org.mockito.ArgumentMatchers.any;
8686
import static org.mockito.ArgumentMatchers.anyString;
8787
import static org.mockito.ArgumentMatchers.eq;
88+
import static org.mockito.Mockito.clearInvocations;
8889
import static org.mockito.Mockito.mock;
8990
import static org.mockito.Mockito.mockingDetails;
9091
import static org.mockito.Mockito.never;
@@ -800,17 +801,35 @@ public void testRefreshWithoutFetch() throws Exception {
800801
repo.setUri("http://somegitserver/somegitrepo");
801802
repo.setBasedir(this.basedir);
802803

803-
// Set the refresh rate to 2 seconds and last update before 100ms. There should be
804-
// no remote repo fetch.
804+
// Set the refresh rate to 10 seconds and last update before 100ms. There should be
805+
// no remote repo fetch and not merge.
805806
repo.setLastRefresh(System.currentTimeMillis() - 100);
806-
repo.setRefreshRate(2);
807+
repo.setRefreshRate(10);
807808

808809
repo.refresh("master");
809810

810811
// Verify no fetch nor merge.
811812
verify(git, never()).fetch();
812813
verify(git).checkout();
813-
verify(git).merge();
814+
verify(git, never()).merge();
815+
816+
// the checkout should not be called anymore since we are on the same branch
817+
clearInvocations(git);
818+
repo.refresh("master");
819+
820+
// Verify no fetch, checkout or merge
821+
verify(git, never()).fetch();
822+
verify(git, never()).checkout();
823+
verify(git, never()).merge();
824+
825+
// checkout another branch, but still before the cache expires
826+
clearInvocations(git);
827+
repo.refresh("staging");
828+
829+
// Verify no fetch, a checkout and still no merge
830+
verify(git, never()).fetch();
831+
verify(git).checkout();
832+
verify(git, never()).merge();
814833
}
815834

816835
@Test

0 commit comments

Comments
 (0)