Switch GetDiff to use the new diff cache

Instead of running GetDiff using the old diff cache and sanity checking
the results using the new diff cache, we switch the implementation to
use the new diff cache, and temporariy keep running the old diff cache
async to export metrics.

The new logic in this change is still gated with a config (false as
default) to rollout the new diff cache gradually on selective hosts. I
also removed the use-new-diff-cache request parameter as this is not
needed anymore (was used for debugging).

Change-Id: I34e10b2165bf0669c736ab17ebc2a871ccfbb8bd
This commit is contained in:
Youssef Elghareeb
2021-03-15 17:39:53 +01:00
parent 586d48f57e
commit 02ac16d7af
7 changed files with 60 additions and 74 deletions

View File

@@ -19,13 +19,15 @@ import com.google.inject.AbstractModule;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
public class FileInfoJsonModule extends AbstractModule { public class FileInfoJsonModule extends AbstractModule {
/** Use the new diff cache implementation {@link FileInfoJsonNewImpl}. */
private final boolean useNewDiffCache; private final boolean useNewDiffCache;
/** Used to dark launch the new diff cache with the list files endpoint. */ /** Used to dark launch the new diff cache with the list files endpoint. */
private final boolean runNewDiffCacheAsync; private final boolean runNewDiffCacheAsync;
public FileInfoJsonModule(@GerritServerConfig Config cfg) { public FileInfoJsonModule(@GerritServerConfig Config cfg) {
this.useNewDiffCache = cfg.getBoolean("cache", "diff_cache", "useNewDiffCache", false); this.useNewDiffCache =
cfg.getBoolean("cache", "diff_cache", "runNewDiffCache_ListFiles", false);
this.runNewDiffCacheAsync = this.runNewDiffCacheAsync =
cfg.getBoolean("cache", "diff_cache", "runNewDiffCacheAsync_listFiles", false); cfg.getBoolean("cache", "diff_cache", "runNewDiffCacheAsync_listFiles", false);
} }

View File

@@ -82,8 +82,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
@Assisted("patchSetA") PatchSet.Id patchSetA, @Assisted("patchSetA") PatchSet.Id patchSetA,
@Assisted("patchSetB") PatchSet.Id patchSetB, @Assisted("patchSetB") PatchSet.Id patchSetB,
DiffPreferencesInfo diffPrefs, DiffPreferencesInfo diffPrefs,
CurrentUser currentUser, CurrentUser currentUser);
boolean useNewDiffCache);
PatchScriptFactory create( PatchScriptFactory create(
ChangeNotes notes, ChangeNotes notes,
@@ -91,8 +90,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
int parentNum, int parentNum,
PatchSet.Id patchSetB, PatchSet.Id patchSetB,
DiffPreferencesInfo diffPrefs, DiffPreferencesInfo diffPrefs,
CurrentUser currentUser, CurrentUser currentUser);
boolean useNewDiffCache);
} }
/** These metrics are temporary for launching the new redesigned diff cache. */ /** These metrics are temporary for launching the new redesigned diff cache. */
@@ -139,10 +137,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
private ChangeNotes notes; private ChangeNotes notes;
private final boolean runNewDiffCacheAsync; private final boolean runNewDiffCache;
// TODO(ghareeb): temporary field used for testing. Please remove.
private final boolean useNewDiffCache;
@AssistedInject @AssistedInject
PatchScriptFactory( PatchScriptFactory(
@@ -162,8 +157,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
@Assisted("patchSetA") @Nullable PatchSet.Id patchSetA, @Assisted("patchSetA") @Nullable PatchSet.Id patchSetA,
@Assisted("patchSetB") PatchSet.Id patchSetB, @Assisted("patchSetB") PatchSet.Id patchSetB,
@Assisted DiffPreferencesInfo diffPrefs, @Assisted DiffPreferencesInfo diffPrefs,
@Assisted CurrentUser currentUser, @Assisted CurrentUser currentUser) {
@Assisted boolean useNewDiffCache) {
this.repoManager = grm; this.repoManager = grm;
this.psUtil = psUtil; this.psUtil = psUtil;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
@@ -183,9 +177,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.diffPrefs = diffPrefs; this.diffPrefs = diffPrefs;
this.currentUser = currentUser; this.currentUser = currentUser;
this.runNewDiffCacheAsync = this.runNewDiffCache = cfg.getBoolean("cache", "diff_cache", "runNewDiffCache_GetDiff", false);
cfg.getBoolean("cache", "diff_cache", "runNewDiffCacheAsync_getDiff", false);
this.useNewDiffCache = useNewDiffCache;
changeId = patchSetB.changeId(); changeId = patchSetB.changeId();
} }
@@ -208,8 +200,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
@Assisted int parentNum, @Assisted int parentNum,
@Assisted PatchSet.Id patchSetB, @Assisted PatchSet.Id patchSetB,
@Assisted DiffPreferencesInfo diffPrefs, @Assisted DiffPreferencesInfo diffPrefs,
@Assisted CurrentUser currentUser, @Assisted CurrentUser currentUser) {
@Assisted boolean useNewDiffCache) {
this.repoManager = grm; this.repoManager = grm;
this.psUtil = psUtil; this.psUtil = psUtil;
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
@@ -229,9 +220,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
this.diffPrefs = diffPrefs; this.diffPrefs = diffPrefs;
this.currentUser = currentUser; this.currentUser = currentUser;
this.runNewDiffCacheAsync = this.runNewDiffCache = cfg.getBoolean("cache", "diff_cache", "runNewDiffCache_GetDiff", false);
cfg.getBoolean("cache", "diff_cache", "runNewDiffCacheAsync_getDiff", false);
this.useNewDiffCache = useNewDiffCache;
changeId = patchSetB.changeId(); changeId = patchSetB.changeId();
checkArgument(parentNum >= 0, "parentNum must be >= 0"); checkArgument(parentNum >= 0, "parentNum must be >= 0");
@@ -270,28 +259,15 @@ public class PatchScriptFactory implements Callable<PatchScript> {
} }
bId = edit.get().getEditCommit(); bId = edit.get().getEditCommit();
} }
if (runNewDiffCache) {
if (useNewDiffCache) { PatchScript patchScript = getPatchScriptWithNewDiffCache(git, aId, bId);
FileDiffOutput fileDiffOutput = // TODO(ghareeb): remove the async run. This is temporarily used to keep sanity checking
aId == null // the results while rolling out the new diff cache.
? diffOperations.getModifiedFileAgainstParent( runOldDiffCacheAsyncAndExportMetrics(git, aId, bId, patchScript);
notes.getProjectName(), return patchScript;
bId, } else {
parentNum == -1 ? null : parentNum + 1, return getPatchScriptWithOldDiffCache(git, aId, bId);
fileName,
diffPrefs.ignoreWhitespace)
: diffOperations.getModifiedFile(
notes.getProjectName(), aId, bId, fileName, diffPrefs.ignoreWhitespace);
return newBuilder().toPatchScriptNew(git, fileDiffOutput);
} }
PatchScriptBuilder patchScriptBuilder = newBuilder();
PatchList list = listFor(keyFor(aId, bId, diffPrefs.ignoreWhitespace));
PatchListEntry content = list.get(fileName);
PatchScript patchScript = patchScriptBuilder.toPatchScriptOld(git, list, content);
if (runNewDiffCacheAsync) {
runNewDiffCacheAsyncAndExportMetrics(git, aId, bId, patchScript);
}
return patchScript;
} catch (PatchListNotAvailableException e) { } catch (PatchListNotAvailableException e) {
throw new NoSuchChangeException(changeId, e); throw new NoSuchChangeException(changeId, e);
} catch (DiffNotAvailableException e) { } catch (DiffNotAvailableException e) {
@@ -311,24 +287,14 @@ public class PatchScriptFactory implements Callable<PatchScript> {
} }
} }
private void runNewDiffCacheAsyncAndExportMetrics( private void runOldDiffCacheAsyncAndExportMetrics(
Repository git, ObjectId aId, ObjectId bId, PatchScript expected) { Repository git, ObjectId aId, ObjectId bId, PatchScript expected) {
@SuppressWarnings("unused") @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = Future<?> possiblyIgnoredError =
executor.submit( executor.submit(
() -> { () -> {
try { try {
FileDiffOutput fileDiffOutput = PatchScript patchScript = getPatchScriptWithOldDiffCache(git, aId, bId);
aId == null
? diffOperations.getModifiedFileAgainstParent(
notes.getProjectName(),
bId,
parentNum == -1 ? null : parentNum + 1,
fileName,
diffPrefs.ignoreWhitespace)
: diffOperations.getModifiedFile(
notes.getProjectName(), aId, bId, fileName, diffPrefs.ignoreWhitespace);
PatchScript patchScript = newBuilder().toPatchScriptNew(git, fileDiffOutput);
if (areEqualPatchscripts(patchScript, expected)) { if (areEqualPatchscripts(patchScript, expected)) {
metrics.diffs.increment(metrics.MATCH); metrics.diffs.increment(metrics.MATCH);
} else { } else {
@@ -337,7 +303,7 @@ public class PatchScriptFactory implements Callable<PatchScript> {
"Mismatching diff for change %s, old commit ID: %s, new commit ID: %s, file name: %s.", "Mismatching diff for change %s, old commit ID: %s, new commit ID: %s, file name: %s.",
changeId.toString(), aId, bId, fileName); changeId.toString(), aId, bId, fileName);
} }
} catch (DiffNotAvailableException | IOException e) { } catch (PatchListNotAvailableException | IOException e) {
metrics.diffs.increment(metrics.ERROR); metrics.diffs.increment(metrics.ERROR);
logger.atSevere().atMostEvery(10, TimeUnit.SECONDS).log( logger.atSevere().atMostEvery(10, TimeUnit.SECONDS).log(
String.format( String.format(
@@ -348,6 +314,29 @@ public class PatchScriptFactory implements Callable<PatchScript> {
}); });
} }
private PatchScript getPatchScriptWithOldDiffCache(Repository git, ObjectId aId, ObjectId bId)
throws IOException, PatchListNotAvailableException {
PatchScriptBuilder patchScriptBuilder = newBuilder();
PatchList list = listFor(keyFor(aId, bId, diffPrefs.ignoreWhitespace));
PatchListEntry content = list.get(fileName);
return patchScriptBuilder.toPatchScriptOld(git, list, content);
}
private PatchScript getPatchScriptWithNewDiffCache(Repository git, ObjectId aId, ObjectId bId)
throws IOException, DiffNotAvailableException {
FileDiffOutput fileDiffOutput =
aId == null
? diffOperations.getModifiedFileAgainstParent(
notes.getProjectName(),
bId,
parentNum == -1 ? null : parentNum + 1,
fileName,
diffPrefs.ignoreWhitespace)
: diffOperations.getModifiedFile(
notes.getProjectName(), aId, bId, fileName, diffPrefs.ignoreWhitespace);
return newBuilder().toPatchScriptNew(git, fileDiffOutput);
}
/** /**
* The comparison is not exhaustive but is using the most important fields. Comparing all fields * The comparison is not exhaustive but is using the most important fields. Comparing all fields
* will require some work in {@link PatchScript} to, e.g., convert it to autovalue. This * will require some work in {@link PatchScript} to, e.g., convert it to autovalue. This

View File

@@ -163,8 +163,7 @@ public class SubmitWithStickyApprovalDiff {
latestApprovedPatchsetId, latestApprovedPatchsetId,
currentPatchsetId, currentPatchsetId,
diffPreferencesInfo, diffPreferencesInfo,
currentUser, currentUser);
/* useNewDiffCache= */ false);
PatchScript patchScript = null; PatchScript patchScript = null;
try { try {
patchScript = patchScriptFactory.call(); patchScript = patchScriptFactory.call();

View File

@@ -91,10 +91,6 @@ public class GetDiff implements RestReadView<FileResource> {
@Option(name = "--intraline") @Option(name = "--intraline")
boolean intraline; boolean intraline;
// TODO(ghareeb): This is a temporary parameter for debugging. Please remove.
@Option(name = "--use-new-diff-cache")
boolean useNewDiffCache;
@Inject @Inject
GetDiff( GetDiff(
ProjectCache projectCache, ProjectCache projectCache,
@@ -143,15 +139,13 @@ public class GetDiff implements RestReadView<FileResource> {
} }
psf = psf =
patchScriptFactoryFactory.create( patchScriptFactoryFactory.create(
notes, fileName, basePatchSet.id(), pId, prefs, currentUser.get(), useNewDiffCache); notes, fileName, basePatchSet.id(), pId, prefs, currentUser.get());
} else if (parentNum > 0) { } else if (parentNum > 0) {
psf = psf =
patchScriptFactoryFactory.create( patchScriptFactoryFactory.create(
notes, fileName, parentNum - 1, pId, prefs, currentUser.get(), useNewDiffCache); notes, fileName, parentNum - 1, pId, prefs, currentUser.get());
} else { } else {
psf = psf = patchScriptFactoryFactory.create(notes, fileName, null, pId, prefs, currentUser.get());
patchScriptFactoryFactory.create(
notes, fileName, null, pId, prefs, currentUser.get(), useNewDiffCache);
} }
try { try {

View File

@@ -76,7 +76,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
private static final String FILE_CONTENT2 = "1st line\n2nd line\n3rd line\n"; private static final String FILE_CONTENT2 = "1st line\n2nd line\n3rd line\n";
private boolean intraline; private boolean intraline;
private boolean useNewDiffCache; private boolean useNewDiffCacheListFiles;
private boolean useNewDiffCacheGetDiff; private boolean useNewDiffCacheGetDiff;
private ObjectId commit1; private ObjectId commit1;
@@ -91,9 +91,10 @@ public class RevisionDiffIT extends AbstractDaemonTest {
baseConfig.setString("cache", "diff_intraline", "timeout", "1 minute"); baseConfig.setString("cache", "diff_intraline", "timeout", "1 minute");
intraline = baseConfig.getBoolean(TEST_PARAMETER_MARKER, "intraline", false); intraline = baseConfig.getBoolean(TEST_PARAMETER_MARKER, "intraline", false);
useNewDiffCache = baseConfig.getBoolean("cache", "diff_cache", "useNewDiffCache", false); useNewDiffCacheListFiles =
baseConfig.getBoolean("cache", "diff_cache", "runNewDiffCache_ListFiles", false);
useNewDiffCacheGetDiff = useNewDiffCacheGetDiff =
baseConfig.getBoolean("cache", "diff_cache", "useNewDiffCache_getDiff", false); baseConfig.getBoolean("cache", "diff_cache", "runNewDiffCache_GetDiff", false);
ObjectId headCommit = testRepo.getRepository().resolve("HEAD"); ObjectId headCommit = testRepo.getRepository().resolve("HEAD");
commit1 = commit1 =
@@ -1276,7 +1277,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
public void renamedUnrelatedFileIsIgnored_ForPatchSetDiffWithRebase_WhenEquallyModifiedInBoth() public void renamedUnrelatedFileIsIgnored_ForPatchSetDiffWithRebase_WhenEquallyModifiedInBoth()
throws Exception { throws Exception {
// TODO(ghareeb): fix this test for the new diff cache implementation // TODO(ghareeb): fix this test for the new diff cache implementation
assume().that(useNewDiffCache).isFalse(); assume().that(useNewDiffCacheListFiles).isFalse();
Function<String, String> contentModification = Function<String, String> contentModification =
fileContent -> fileContent.replace("1st line\n", "First line\n"); fileContent -> fileContent.replace("1st line\n", "First line\n");
@@ -1369,7 +1370,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
@Test @Test
public void filesTouchedByPatchSetsAndContainingOnlyRebaseHunksAreIgnored() throws Exception { public void filesTouchedByPatchSetsAndContainingOnlyRebaseHunksAreIgnored() throws Exception {
// TODO(ghareeb): fix this test for the new diff cache implementation // TODO(ghareeb): fix this test for the new diff cache implementation
assume().that(useNewDiffCache).isFalse(); assume().that(useNewDiffCacheListFiles).isFalse();
addModifiedPatchSet( addModifiedPatchSet(
changeId, FILE_NAME, fileContent -> fileContent.replace("Line 50\n", "Line fifty\n")); changeId, FILE_NAME, fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"));
@@ -2751,7 +2752,7 @@ public class RevisionDiffIT extends AbstractDaemonTest {
@Test @Test
public void symlinkConvertedToRegularFileIsIdentifiedAsAdded() throws Exception { public void symlinkConvertedToRegularFileIsIdentifiedAsAdded() throws Exception {
// TODO(ghareeb): fix this test for the new diff cache implementation // TODO(ghareeb): fix this test for the new diff cache implementation
assume().that(useNewDiffCache).isFalse(); assume().that(useNewDiffCacheListFiles).isFalse();
assume().that(useNewDiffCacheGetDiff).isFalse(); assume().that(useNewDiffCacheGetDiff).isFalse();
String target = "file.txt"; String target = "file.txt";

View File

@@ -26,7 +26,7 @@ public class RevisionNewDiffCacheForSingleFileIT extends RevisionDiffIT {
@ConfigSuite.Default @ConfigSuite.Default
public static Config newDiffCacheConfig() { public static Config newDiffCacheConfig() {
Config config = new Config(); Config config = new Config();
config.setBoolean("cache", "diff_cache", "runNewDiffCacheAsync_getDiff", true); config.setBoolean("cache", "diff_cache", "runNewDiffCache_GetDiff", true);
return config; return config;
} }
} }

View File

@@ -18,14 +18,15 @@ import com.google.gerrit.testing.ConfigSuite;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
/** /**
* Runs the {@link RevisionDiffIT} tests with the new diff cache. This is temporary until the new * Runs the {@link RevisionDiffIT} with the list files endpoint using the new diff cache. This is
* diff cache is fully deployed. The new diff cache will become the default in the future. * temporary until the new diff cache is fully deployed. The new diff cache will become the default
* in the future.
*/ */
public class RevisionNewDiffCacheIT extends RevisionDiffIT { public class RevisionNewDiffCacheIT extends RevisionDiffIT {
@ConfigSuite.Default @ConfigSuite.Default
public static Config newDiffCacheConfig() { public static Config newDiffCacheConfig() {
Config config = new Config(); Config config = new Config();
config.setBoolean("cache", "diff_cache", "useNewDiffCache", true); config.setBoolean("cache", "diff_cache", "runNewDiffCache_ListFiles", true);
return config; return config;
} }
} }