Split intraline differences into their own cache

The bulk of the running time for loading a PatchList entry from the
PatchListCache is actually within the intraline difference code.
Assuming no bugs, running JGit's DiffFormatter and converting the
results is typically sub-second, even for a very large change that
has renames.

By deferring the intraline difference logic until its actually
required to display a particular file allows file lists to load
very quickly, and doesn't penalize users who have disabled the
intraline difference setting in their account preferences.

This also offers a work around for the dreaded "patch of death"
situation.  When a bad file is encountered and it won't load,
users can temporarily switch off intraline difference and open the
file anyway.  Unfortunately the server will still have at least
one worker thread stuck generating the intraline difference cache
record, but at least the change is still viewable.

Change-Id: I61bbd3d068bd9b3da9d1018c0f66969a5b6ff58e
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-11-11 16:54:35 -08:00
parent a3d260a3e1
commit 3ea1d5567c
9 changed files with 371 additions and 169 deletions

View File

@@ -0,0 +1,97 @@
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.patch;
import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.diff.ReplaceEdit;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class IntraLineDiff implements Serializable {
static final long serialVersionUID = IntraLineDiffKey.serialVersionUID;
private List<Edit> edits;
IntraLineDiff(List<Edit> edits) {
this.edits = edits;
}
public List<Edit> getEdits() {
return edits;
}
private void writeObject(final ObjectOutputStream out) throws IOException {
writeVarInt32(out, edits.size());
for (Edit e : edits) {
writeEdit(out, e);
if (e instanceof ReplaceEdit) {
ReplaceEdit r = (ReplaceEdit) e;
writeVarInt32(out, r.getInternalEdits().size());
for (Edit i : r.getInternalEdits()) {
writeEdit(out, i);
}
} else {
writeVarInt32(out, 0);
}
}
}
private void readObject(final ObjectInputStream in) throws IOException {
int editCount = readVarInt32(in);
Edit[] editArray = new Edit[editCount];
for (int i = 0; i < editCount; i++) {
editArray[i] = readEdit(in);
int innerCount = readVarInt32(in);
if (0 < innerCount) {
Edit[] inner = new Edit[innerCount];
for (int j = 0; j < innerCount; j++)
inner[j] = readEdit(in);
editArray[i] = new ReplaceEdit(editArray[i], toList(inner));
}
}
}
private static void writeEdit(OutputStream out, Edit e) throws IOException {
writeVarInt32(out, e.getBeginA());
writeVarInt32(out, e.getEndA());
writeVarInt32(out, e.getBeginB());
writeVarInt32(out, e.getEndB());
}
private static Edit readEdit(InputStream in) throws IOException {
int beginA = readVarInt32(in);
int endA = readVarInt32(in);
int beginB = readVarInt32(in);
int endB = readVarInt32(in);
return new Edit(beginA, endA, beginB, endB);
}
private static List<Edit> toList(Edit[] l) {
return Collections.unmodifiableList(Arrays.asList(l));
}
}