From 343d9a2ea6f43398f95a5ff71b1d08120f77ab1c Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Fri, 14 Oct 2016 18:40:52 +0300 Subject: [PATCH] [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. --- .../vcs-log/impl/src/META-INF/vcs-log.xml | 2 + .../com/intellij/vcs/log/data/EmptyIndex.java | 59 +++++++++++++++++++ .../com/intellij/vcs/log/data/VcsLogData.java | 28 +++++++-- .../vcs/log/impl/FatalErrorConsumer.java | 2 + .../vcs/log/impl/VcsLogCachesInvalidator.java | 46 +++++++++++++++ .../intellij/vcs/log/impl/VcsLogManager.java | 5 ++ .../intellij/vcs/log/impl/VcsProjectLog.java | 6 +- .../intellij/vcs/log/util/PersistentUtil.java | 6 ++ .../vcs/log/data/VcsLogRefresherTest.java | 13 +++- .../vcs/log/data/VisiblePackBuilderTest.kt | 26 -------- 10 files changed, 160 insertions(+), 33 deletions(-) create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyIndex.java create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogCachesInvalidator.java diff --git a/platform/vcs-log/impl/src/META-INF/vcs-log.xml b/platform/vcs-log/impl/src/META-INF/vcs-log.xml index 4f44100f5b9e..960c14f95090 100644 --- a/platform/vcs-log/impl/src/META-INF/vcs-log.xml +++ b/platform/vcs-log/impl/src/META-INF/vcs-log.xml @@ -25,6 +25,8 @@ + + diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyIndex.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyIndex.java new file mode 100644 index 000000000000..a83d4e16310b --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyIndex.java @@ -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 filters) { + return false; + } + + @NotNull + @Override + public Set filter(@NotNull List detailsFilters) { + throw new UnsupportedOperationException(); + } + + @Override + public void markCorrupted() { + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java index 16af2331ad5d..be451c68b38b 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogData.java @@ -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); diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/FatalErrorConsumer.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/FatalErrorConsumer.java index 566ec3d80c1c..0573ed544ba6 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/FatalErrorConsumer.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/FatalErrorConsumer.java @@ -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); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogCachesInvalidator.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogCachesInvalidator.java new file mode 100644 index 000000000000..e0260bbf485e --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogCachesInvalidator.java @@ -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()); + } + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java index 84f4b4bbf5a9..cb685aea87f3 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java @@ -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); + } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsProjectLog.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsProjectLog.java index 3664dd8b9033..390b7cc1b0a0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsProjectLog.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsProjectLog.java @@ -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); + } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java index 407c77cce7c6..b39928c3b663 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/util/PersistentUtil.java @@ -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 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); + } } diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java index da9fe03e983b..d438daa36f4d 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java @@ -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 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(), diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VisiblePackBuilderTest.kt b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VisiblePackBuilderTest.kt index 172b294d2b55..44071cfd9fbc 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VisiblePackBuilderTest.kt +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VisiblePackBuilderTest.kt @@ -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): Boolean { - return false - } - - override fun scheduleIndex(full: Boolean) { - } - - override fun markForIndexing(index: Int, root: VirtualFile) { - } - - override fun filter(detailsFilters: MutableList): MutableSet { - throw UnsupportedOperationException() - } - - override fun markCorrupted() { - } - } }