[vcs-log] correctly rebuild user registry on error

VcsUserRegistry is a component independent from log. It should rebuild itself on errors and send rebuild notifications.
Notifications are used to mark index corrupted, since it depends on user registry.
This commit is contained in:
Julia Beliaeva
2018-06-25 18:45:56 +03:00
parent 95d683dbb2
commit 9232b39bff
2 changed files with 59 additions and 15 deletions
@@ -16,9 +16,11 @@
package com.intellij.vcs.log.data
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.util.EventDispatcher
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.Interner
import com.intellij.util.io.*
@@ -29,31 +31,42 @@ import java.io.DataInput
import java.io.DataOutput
import java.io.File
import java.io.IOException
import java.util.*
import java.util.concurrent.atomic.AtomicReference
/**
*
*/
class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, VcsUserRegistry {
private val _persistentEnumerator = AtomicReference<PersistentEnumeratorBase<VcsUser>?>()
private val persistentEnumerator: PersistentEnumeratorBase<VcsUser>?
get() = _persistentEnumerator.get()
private val interner: Interner<VcsUser>
private val mapFile = File(USER_CACHE_APP_DIR, project.locationHash + "." + STORAGE_VERSION)
private val eventDispatcher = EventDispatcher.create<VcsUserRegistryListener>(VcsUserRegistryListener::class.java)
init {
val mapFile = File(USER_CACHE_APP_DIR, project.locationHash + "." + STORAGE_VERSION)
persistentEnumerator = initEnumerator(mapFile)
initEnumerator()
interner = Interner()
}
private fun initEnumerator(mapFile: File): PersistentEnumeratorBase<VcsUser>? {
return try {
IOUtil.openCleanOrResetBroken({
PersistentBTreeEnumerator(mapFile, MyDescriptor(), Page.PAGE_SIZE, null,
STORAGE_VERSION)
}, mapFile)
private fun initEnumerator(): Boolean {
try {
val enumerator = IOUtil.openCleanOrResetBroken({
PersistentBTreeEnumerator(mapFile, MyDescriptor(), Page.PAGE_SIZE, null,
STORAGE_VERSION)
}, mapFile)
val wasSet = _persistentEnumerator.compareAndSet(null, enumerator)
if (!wasSet) {
LOG.error("Could not assign newly opened enumerator")
enumerator?.close()
}
return wasSet
}
catch (e: IOException) {
LOG.warn(e)
null
}
return false
}
override fun createUser(name: String, email: String): VcsUser {
@@ -68,8 +81,8 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V
}
catch (e: IOException) {
LOG.warn(e)
rebuild(e)
}
}
fun addUsers(users: Collection<VcsUser>) {
@@ -84,10 +97,34 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V
}
catch (e: IOException) {
LOG.warn(e)
rebuild(e)
emptySet()
}
}
private fun rebuild(t: Throwable) {
if (persistentEnumerator?.isCorrupted == true) {
_persistentEnumerator.getAndSet(null)?.let { oldEnumerator ->
ApplicationManager.getApplication().executeOnPooledThread {
try {
oldEnumerator.close()
}
catch (_: IOException) {
}
finally {
if (initEnumerator()) {
eventDispatcher.multicaster.onRebuild(t)
}
}
}
}
}
}
fun addRebuildListener(listener: VcsUserRegistryListener, disposable: Disposable) {
eventDispatcher.addListener(listener, disposable)
}
fun flush() {
persistentEnumerator?.force()
}
@@ -140,3 +177,7 @@ class VcsUserRegistryImpl internal constructor(project: Project) : Disposable, V
private const val STORAGE_VERSION = 2
}
}
interface VcsUserRegistryListener : EventListener {
fun onRebuild(t: Throwable)
}
@@ -17,6 +17,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.VcsLogRefresher;
import com.intellij.vcs.log.VcsUserRegistry;
import com.intellij.vcs.log.data.VcsLogData;
import com.intellij.vcs.log.data.VcsLogStorage;
import com.intellij.vcs.log.ui.AbstractVcsLogUi;
@@ -59,13 +60,15 @@ public class VcsLogManager implements Disposable {
myRecreateMainLogHandler = recreateHandler;
Map<VirtualFile, VcsLogProvider> logProviders = findLogProviders(roots, myProject);
myLogData = new VcsLogData(myProject, logProviders, new MyFatalErrorsHandler(), this);
MyFatalErrorsHandler fatalErrorsHandler = new MyFatalErrorsHandler();
myLogData = new VcsLogData(myProject, logProviders, fatalErrorsHandler, this);
myPostponableRefresher = new PostponableLogRefresher(myLogData);
myTabsLogRefresher = new VcsLogTabsWatcher(myProject, myPostponableRefresher);
refreshLogOnVcsEvents(logProviders, myPostponableRefresher, myLogData);
myColorManager = new VcsLogColorManagerImpl(logProviders.keySet());
myLogData.getUserRegistry().addRebuildListener(t -> fatalErrorsHandler.consume(myLogData.getUserRegistry(), t), this);
if (scheduleRefreshImmediately) {
scheduleInitialization();
@@ -130,7 +133,7 @@ public class VcsLogManager implements Disposable {
MultiMap<VcsLogProvider, VirtualFile> providers2roots = MultiMap.create();
logProviders.forEach((key, value) -> providers2roots.putValue(value, key));
for (Map.Entry<VcsLogProvider, Collection<VirtualFile>> entry : providers2roots.entrySet()) {
for (Map.Entry<VcsLogProvider, Collection<VirtualFile>> entry: providers2roots.entrySet()) {
Disposable disposable = entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), refresher);
Disposer.register(disposableParent, disposable);
}
@@ -140,7 +143,7 @@ public class VcsLogManager implements Disposable {
public static Map<VirtualFile, VcsLogProvider> findLogProviders(@NotNull Collection<VcsRoot> roots, @NotNull Project project) {
Map<VirtualFile, VcsLogProvider> logProviders = ContainerUtil.newHashMap();
VcsLogProvider[] allLogProviders = Extensions.getExtensions(VcsLogProvider.LOG_PROVIDER_EP, project);
for (VcsRoot root : roots) {
for (VcsRoot root: roots) {
AbstractVcs vcs = root.getVcs();
VirtualFile path = root.getPath();
if (vcs == null || path == null) {
@@ -148,7 +151,7 @@ public class VcsLogManager implements Disposable {
continue;
}
for (VcsLogProvider provider : allLogProviders) {
for (VcsLogProvider provider: allLogProviders) {
if (provider.getSupportedVcs().equals(vcs.getKeyInstanceMethod())) {
logProviders.put(path, provider);
break;
@@ -207,7 +210,7 @@ public class VcsLogManager implements Disposable {
LOG.error(e);
}
if (source instanceof VcsLogStorage) {
if (source instanceof VcsLogStorage || source instanceof VcsUserRegistry) {
myLogData.getIndex().markCorrupted();
}
}