[vcs-log] register caches invalidator for log so that Invalidate... action would remove system/vcs-log directory

1. On invalidateCaches create a "corruption.marker" in system/vcs-log.
2. Check existence of this marker before creating log storage and indexes. If marker exists, remove system/vcs-log.
3. Problem arises, however, when system/vcs-log could not be deleted. Nothing can be done here, so just display error balloon, fallback to memory storage and empty indexes. Loading log in background should be disabled in this case.
This commit is contained in:
Julia Beliaeva
2016-10-29 21:33:44 +03:00
parent 494eff81f4
commit 343d9a2ea6
10 changed files with 160 additions and 33 deletions
@@ -25,6 +25,8 @@
<logHighlighterFactory implementation="com.intellij.vcs.log.ui.IndexHighlighter$Factory"/>
<postStartupActivity implementation="com.intellij.vcs.log.impl.VcsProjectLog$InitLogStartupActivity"/>
<cachesInvalidator implementation="com.intellij.vcs.log.impl.VcsLogCachesInvalidator"/>
</extensions>
<actions>
@@ -0,0 +1,59 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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.intellij.vcs.log.data;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcs.log.VcsLogDetailsFilter;
import com.intellij.vcs.log.data.index.VcsLogIndex;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Set;
public class EmptyIndex implements VcsLogIndex {
@Override
public void scheduleIndex(boolean full) {
}
@Override
public boolean isIndexed(int commit) {
return false;
}
@Override
public boolean isIndexed(@NotNull VirtualFile root) {
return false;
}
@Override
public void markForIndexing(int commit, @NotNull VirtualFile root) {
}
@Override
public boolean canFilter(@NotNull List<VcsLogDetailsFilter> filters) {
return false;
}
@NotNull
@Override
public Set<Integer> filter(@NotNull List<VcsLogDetailsFilter> detailsFilters) {
throw new UnsupportedOperationException();
}
@Override
public void markCorrupted() {
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.vcs.log.data;
import com.intellij.ide.caches.CachesInvalidator;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
@@ -35,6 +36,8 @@ import com.intellij.vcs.log.*;
import com.intellij.vcs.log.data.index.VcsLogIndex;
import com.intellij.vcs.log.data.index.VcsLogPersistentIndex;
import com.intellij.vcs.log.impl.FatalErrorConsumer;
import com.intellij.vcs.log.impl.VcsLogCachesInvalidator;
import com.intellij.vcs.log.util.PersistentUtil;
import com.intellij.vcs.log.util.StopWatch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -91,15 +94,30 @@ public class VcsLogData implements Disposable, VcsLogDataProvider {
myUserRegistry = (VcsUserRegistryImpl)ServiceManager.getService(project, VcsUserRegistry.class);
myFatalErrorsConsumer = fatalErrorsConsumer;
myHashMap = createLogHashMap();
VcsLogProgress progress = new VcsLogProgress();
Disposer.register(this, progress);
VcsLogCachesInvalidator invalidator = CachesInvalidator.EP_NAME.findExtension(VcsLogCachesInvalidator.class);
if (invalidator.isValid()) {
myHashMap = createLogHashMap();
myIndex = new VcsLogPersistentIndex(myProject, myHashMap, progress, logProviders, myFatalErrorsConsumer, this);
}
else {
// this is not recoverable
// restart won't help here
// and can not shut down ide because of this
// so use memory storage (probably leading to out of memory at some point) + no index
String message = "Could not delete " + PersistentUtil.LOG_CACHE + "\nDelete it manually and restart IDEA.";
LOG.error(message);
myFatalErrorsConsumer.displayFatalErrorMessage(message);
myHashMap = new InMemoryStorage();
myIndex = new EmptyIndex();
}
myTopCommitsDetailsCache = new TopCommitsCache(myHashMap);
myMiniDetailsGetter = new MiniDetailsGetter(myHashMap, logProviders, myTopCommitsDetailsCache, this);
myDetailsGetter = new CommitDetailsGetter(myHashMap, logProviders, this);
VcsLogProgress progress = new VcsLogProgress();
Disposer.register(this, progress);
myIndex = new VcsLogPersistentIndex(myProject, myHashMap, progress, logProviders, myFatalErrorsConsumer, this);
myRefresher = new VcsLogRefresherImpl(myProject, myHashMap, myLogProviders, myUserRegistry, myIndex, progress, myTopCommitsDetailsCache,
this::fireDataPackChangeEvent, FAILING_EXCEPTION_HANDLER, RECENT_COMMITS_COUNT);
@@ -20,4 +20,6 @@ import org.jetbrains.annotations.Nullable;
public interface FatalErrorConsumer {
void consume(@Nullable Object source, @NotNull Exception exception);
void displayFatalErrorMessage(@NotNull String message);
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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.intellij.vcs.log.impl;
import com.intellij.ide.caches.CachesInvalidator;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.vcs.log.util.PersistentUtil;
public class VcsLogCachesInvalidator extends CachesInvalidator {
public synchronized boolean isValid() {
if (PersistentUtil.getCorruptionMarkerFile().exists()) {
boolean deleted = FileUtil.delete(PersistentUtil.LOG_CACHE);
if (!deleted) {
// if could not delete caches, ensure that corruption marker is still there
FileUtil.createIfDoesntExist(PersistentUtil.getCorruptionMarkerFile());
}
return deleted;
}
return true;
}
@Override
public void invalidateCaches() {
if (PersistentUtil.LOG_CACHE.exists()) {
String[] children = PersistentUtil.LOG_CACHE.list();
if (!ArrayUtil.isEmpty(children)) {
FileUtil.createIfDoesntExist(PersistentUtil.getCorruptionMarkerFile());
}
}
}
}
@@ -240,5 +240,10 @@ public class VcsLogManager implements Disposable {
myLogData.getIndex().markCorrupted();
}
}
@Override
public void displayFatalErrorMessage(@NotNull String message) {
VcsBalloonProblemNotifier.showOverChangesView(myProject, message, MessageType.ERROR);
}
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.vcs.log.impl;
import com.intellij.ide.caches.CachesInvalidator;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
@@ -111,7 +112,10 @@ public class VcsProjectLog {
logManager.scheduleInitialization();
}
else if (PostponableLogRefresher.keepUpToDate()) {
HeavyAwareExecutor.executeOutOfHeavyProcessLater(logManager::scheduleInitialization, 5000);
VcsLogCachesInvalidator invalidator = CachesInvalidator.EP_NAME.findExtension(VcsLogCachesInvalidator.class);
if (invalidator.isValid()) {
HeavyAwareExecutor.executeOutOfHeavyProcessLater(logManager::scheduleInitialization, 5000);
}
}
}
@@ -33,6 +33,7 @@ import java.util.Map;
public class PersistentUtil {
@NotNull public static final File LOG_CACHE = new File(PathManager.getSystemPath(), "vcs-log");
@NotNull private static final String CORRUPTION_MARKER = "corruption.marker";
@NotNull
public static String calcLogId(@NotNull Project project, @NotNull Map<VirtualFile, VcsLogProvider> logProviders) {
@@ -83,4 +84,9 @@ public class PersistentUtil {
new PersistentHashMap<>(storageFile, new IntInlineKeyDescriptor(), externalizer, Page.PAGE_SIZE),
storageFile);
}
@NotNull
public static File getCorruptionMarkerFile() {
return new File(LOG_CACHE, CORRUPTION_MARKER);
}
}
@@ -33,6 +33,7 @@ import com.intellij.vcs.log.graph.GraphCommit;
import com.intellij.vcs.log.impl.*;
import com.intellij.vcs.test.VcsPlatformTest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.*;
@@ -194,7 +195,17 @@ public class VcsLogRefresherTest extends VcsPlatformTest {
}
private VcsLogRefresherImpl createLoader(Consumer<DataPack> dataPackConsumer) {
myLogData = new VcsLogData(myProject, myLogProviders, (source, exception) -> LOG.error(exception));
myLogData = new VcsLogData(myProject, myLogProviders, new FatalErrorHandler() {
@Override
public void consume(@Nullable Object source, @NotNull Exception exception) {
LOG.error(exception);
}
@Override
public void displayFatalErrorMessage(@NotNull String message) {
LOG.error(message);
}
});
Disposer.register(myProject, myLogData);
return new VcsLogRefresherImpl(myProject, myLogData.getHashMap(), myLogProviders, myLogData.getUserRegistry(), myLogData.getIndex(),
new VcsLogProgress(),
@@ -247,31 +247,5 @@ class VisiblePackBuilderTest {
}
}
class EmptyIndex : VcsLogIndex {
override fun isIndexed(root: VirtualFile): Boolean {
return false
}
override fun isIndexed(commit: Int): Boolean {
return false
}
override fun canFilter(filters: MutableList<VcsLogDetailsFilter>): Boolean {
return false
}
override fun scheduleIndex(full: Boolean) {
}
override fun markForIndexing(index: Int, root: VirtualFile) {
}
override fun filter(detailsFilters: MutableList<VcsLogDetailsFilter>): MutableSet<Int> {
throw UnsupportedOperationException()
}
override fun markCorrupted() {
}
}
}