index pack: attach/detach index per project

GitOrigin-RevId: 786c4d03af1b17f532197b0a926c20ae40d1c5c8
This commit is contained in:
Dmitry Batkovich
2019-12-23 15:07:19 +00:00
committed by intellij-monorepo-bot
parent 2e05d1ba18
commit 0b7732a047
3 changed files with 158 additions and 78 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.indexing;
import com.intellij.openapi.command.impl.DummyProject;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
@@ -195,9 +196,12 @@ public class IndexPackTest extends TestCase {
try (UncompressedZipFileSystem fs = new UncompressedZipFileSystem(pack.toPath(), new UncompressedZipFileSystemProvider())) {
PlatformTestUtil.startPerformanceTest("read", 6000, () -> {
ReadOnlyIndexPack<String, String, String, ?> indexPack = new ReadOnlyIndexPack<>(generateIndexNames(packSize)
.map(name -> createStringLengthIndex(fs.getPath(name, "index"), true))
.collect(Collectors.toList()));
ReadOnlyIndexPack<String, String, String, InvertedIndex<String, String, String>> indexPack
= new ReadOnlyIndexPack<>(path -> createStringLengthIndex(path, false));
generateIndexNames(packSize).map(name -> fs.getPath(name, "index")).forEach(path -> {
indexPack.attach(path, DummyProject.getInstance());
});
LongAdder recordCount = new LongAdder();
for (int key : keys.toArray()) {
@@ -0,0 +1,151 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.indexing.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.util.SmartList;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.indexing.InvertedIndex;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
public class ReadOnlyIndexPack<K, V, Input, Index extends InvertedIndex<K, V, Input>> implements InvertedIndex<K, V, Input> {
private static final Logger LOG = Logger.getInstance(ReadOnlyIndexPack.class);
private final Function<Path, Index> myIndexGenerator;
private final BidirectionalMap<Path, Index> myLoadedIndexes = new BidirectionalMap<>();
private final MultiMap<Project, Index> myProject2Index = new MultiMap<>();
private final MultiMap<Index, Project> myIndex2Project = new MultiMap<>();
private final ReentrantReadWriteLock myPackStructureAccessLock = new ReentrantReadWriteLock();
public ReadOnlyIndexPack(@NotNull Function<Path, Index> generator) {
myIndexGenerator = generator;
}
public void attach(@NotNull Path indexPath, @NotNull Project project) {
ReentrantReadWriteLock.WriteLock writeLock = myPackStructureAccessLock.writeLock();
writeLock.lock();
try {
Index index = myLoadedIndexes.computeIfAbsent(indexPath, myIndexGenerator);
LOG.assertTrue(!myIndex2Project.get(index).contains(project));
LOG.assertTrue(!myProject2Index.get(project).contains(index));
} finally {
writeLock.unlock();
}
}
public void detachIndex(@NotNull Path indexPath, @NotNull Project project) {
ReentrantReadWriteLock.WriteLock writeLock = myPackStructureAccessLock.writeLock();
writeLock.lock();
try {
Index index = myLoadedIndexes.get(indexPath);
LOG.assertTrue(index != null);
LOG.assertTrue(myIndex2Project.remove(index, project));
tryCleanIndexData(index);
} finally {
writeLock.unlock();
}
}
public void detachProject(@NotNull Project project) {
ReentrantReadWriteLock.WriteLock writeLock = myPackStructureAccessLock.writeLock();
writeLock.lock();
try {
Collection<Index> removedIndexes = myProject2Index.remove(project);
if (removedIndexes != null) {
for (Index index : removedIndexes) {
tryCleanIndexData(index);
}
}
} finally {
writeLock.unlock();
}
}
private void tryCleanIndexData(@NotNull Index index) {
assert myPackStructureAccessLock.writeLock().isHeldByCurrentThread();
if (myIndex2Project.get(index).isEmpty()) {
myLoadedIndexes.removeValue(index);
try {
index.dispose();
} catch (Exception e) {
LOG.error(e);
}
}
}
@NotNull
@Override
public ValueContainer<V> getData(@NotNull K k) throws StorageException {
List<ValueContainer<V>> result = new SmartList<>();
ReentrantReadWriteLock.ReadLock readLock = myPackStructureAccessLock.readLock();
readLock.lock();
try {
for (InvertedIndex<K, V, Input> index : myLoadedIndexes.values()) {
ValueContainer<V> currentData = index.getData(k);
if (currentData.size() != 0) {
result.add(currentData);
}
}
return result.isEmpty() ? new ValueContainerImpl<>() : new MergedValueContainer<>(result);
} finally {
readLock.unlock();
}
}
@NotNull
@Override
public Computable<Boolean> update(int inputId, @Nullable Input content) {
throw new UnsupportedOperationException("index pack is read-only");
}
@Override
public void flush() throws StorageException {
ReentrantReadWriteLock.ReadLock readLock = myPackStructureAccessLock.readLock();
readLock.lock();
try {
List<StorageException> exceptions = new SmartList<>();
for (InvertedIndex<K, V, Input> index : myLoadedIndexes.values()) {
try {
index.flush();
} catch (StorageException e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
} finally {
readLock.unlock();
}
}
@Override
public void clear() {
throw new UnsupportedOperationException("index pack is read-only");
}
@Override
public void dispose() {
ReentrantReadWriteLock.WriteLock writeLock = myPackStructureAccessLock.writeLock();
writeLock.lock();
try {
for (Project project : myProject2Index.keySet()) {
detachProject(project);
}
} finally {
writeLock.unlock();
}
}
}
@@ -1,75 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.util.indexing.impl;
import com.intellij.openapi.util.Computable;
import com.intellij.util.SmartList;
import com.intellij.util.indexing.InvertedIndex;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class ReadOnlyIndexPack<K, V, Input, Index extends InvertedIndex<K, V, Input>> implements InvertedIndex<K, V, Input> {
@NotNull
private final List<Index> myIndexes;
public ReadOnlyIndexPack(@NotNull List<Index> indexes) {myIndexes = indexes;}
@NotNull
@Override
public ValueContainer<V> getData(@NotNull K k) throws StorageException {
List<ValueContainer<V>> result = new SmartList<>();
for (InvertedIndex<K, V, Input> index : myIndexes) {
ValueContainer<V> currentData = index.getData(k);
if (currentData.size() != 0) {
result.add(currentData);
}
}
return result.isEmpty() ? new ValueContainerImpl<>() : new MergedValueContainer<>(result);
}
@NotNull
@Override
public Computable<Boolean> update(int inputId, @Nullable Input content) {
throw new UnsupportedOperationException("index pack is read-only");
}
@Override
public void flush() throws StorageException {
List<StorageException> exceptions = new SmartList<>();
for (InvertedIndex<K, V, Input> index : myIndexes) {
try {
index.flush();
}
catch (StorageException e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
}
@Override
public void clear() throws StorageException {
throw new UnsupportedOperationException("index pack is read-only");
}
@Override
public void dispose() {
List<RuntimeException> exceptions = new SmartList<>();
for (InvertedIndex<K, V, Input> index : myIndexes) {
try {
index.dispose();
}
catch (RuntimeException e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
}
}