strangedb/src/main/java/org/warp/jcwdb/JCWDatabase.java

245 lines
6.9 KiB
Java
Raw Normal View History

2018-11-19 15:16:12 +01:00
package org.warp.jcwdb;
import java.io.IOException;
2018-11-27 10:26:01 +01:00
import java.lang.ref.WeakReference;
2018-11-19 15:16:12 +01:00
import java.nio.file.Path;
2018-11-22 23:31:41 +01:00
import java.util.ArrayList;
2018-12-05 02:39:41 +01:00
import java.util.Iterator;
2018-12-11 23:00:51 +01:00
import java.util.function.Consumer;
2018-11-27 10:26:01 +01:00
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
2018-11-27 17:47:19 +01:00
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
2018-12-04 23:57:49 +01:00
import it.unimi.dsi.fastutil.longs.LongArrayList;
2018-12-05 02:39:41 +01:00
import it.unimi.dsi.fastutil.objects.ObjectIterator;
2018-11-19 15:16:12 +01:00
2018-11-27 17:47:19 +01:00
public class JCWDatabase implements AutoCloseable, Cleanable {
2018-12-06 12:30:04 +01:00
public final static long MAX_LOADED_REFERENCES = 1000;
public final static long MAX_LOADED_INDICES = 10000;
2018-11-27 17:47:19 +01:00
private final TypesManager typesManager;
private final MixedIndexDatabase indices;
private final Cleaner databaseCleaner;
2018-12-05 02:39:41 +01:00
private final EntryReferenceTools entryReferenceTools = new EntryReferenceTools();
2018-11-27 10:26:01 +01:00
private final Long2ObjectMap<WeakReference<EntryReference<?>>> references;
2018-11-22 23:31:41 +01:00
private volatile boolean closed;
private final Object closeLock = new Object();
private final Object indicesAccessLock = new Object();
private final Object referencesAccessLock = new Object();
2018-11-19 15:16:12 +01:00
public JCWDatabase(Path dataFile, Path metadataFile) throws IOException {
2018-11-21 01:02:25 +01:00
this.typesManager = new TypesManager(this);
2018-11-20 18:39:48 +01:00
this.indices = new MixedIndexDatabase(typesManager, dataFile, metadataFile);
2018-11-27 10:26:01 +01:00
this.references = new Long2ObjectLinkedOpenHashMap<>();
2018-11-22 23:31:41 +01:00
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
JCWDatabase.this.close();
} catch (Exception e) {
e.printStackTrace();
}
}));
2018-11-27 17:47:19 +01:00
this.databaseCleaner = new Cleaner(this);
this.databaseCleaner.start();
2018-11-20 18:39:48 +01:00
}
2018-11-21 01:02:25 +01:00
2018-11-22 23:31:41 +01:00
public <T> EntryReference<LightList<T>> getRoot() throws IOException {
2018-12-04 23:57:49 +01:00
return getRoot(Object.class).cast();
}
public <T> EntryReference<LightList<T>> getRoot(Class<T> clazz) throws IOException {
2018-11-22 23:31:41 +01:00
checkClosed();
if (exists(0)) {
2018-11-21 01:02:25 +01:00
return get(0);
2018-11-22 23:31:41 +01:00
} else {
2018-12-11 23:00:51 +01:00
LightList<T> newRoot = new LightList<T>(this, new LongArrayList());
2018-11-22 23:31:41 +01:00
return set(0, newRoot);
2018-11-21 01:02:25 +01:00
}
}
public <T> EntryReference<T> get(long index) throws IOException {
2018-11-22 23:31:41 +01:00
checkClosed();
synchronized (referencesAccessLock) {
2018-11-27 10:26:01 +01:00
WeakReference<EntryReference<?>> refRef = this.references.getOrDefault(index, null);
EntryReference<T> ref;
if (refRef == null || (ref = (EntryReference<T>) refRef.get()) == null) {
2018-11-22 23:31:41 +01:00
int type;
2018-12-11 23:00:51 +01:00
long hash;
2018-11-22 23:31:41 +01:00
synchronized (indicesAccessLock) {
type = this.indices.getType(index);
2018-12-11 23:00:51 +01:00
hash = this.indices.getHash(index);
2018-11-22 23:31:41 +01:00
}
DBTypeParser<T> typeParser = this.typesManager.get(type);
2018-12-11 23:00:51 +01:00
ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser);
2018-12-05 02:39:41 +01:00
refRef = new WeakReference<>(ref);
2018-11-27 10:26:01 +01:00
this.references.put(index, refRef);
2018-11-22 23:31:41 +01:00
}
return ref;
2018-11-21 01:02:25 +01:00
}
}
2018-11-22 23:31:41 +01:00
protected <T> EntryReference<T> add(T value) throws IOException {
checkClosed();
synchronized (referencesAccessLock) {
EntryReference<T> ref;
2018-11-22 23:31:41 +01:00
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
long index;
2018-12-11 23:00:51 +01:00
long hash;
2018-11-22 23:31:41 +01:00
synchronized (indicesAccessLock) {
index = indices.add(typeParser.getWriter(value));
2018-12-11 23:00:51 +01:00
hash = indices.getHash(index);
2018-11-22 23:31:41 +01:00
}
2018-12-11 23:00:51 +01:00
ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser, value);
2018-12-05 02:39:41 +01:00
this.references.put(index, new WeakReference<>(ref));
2018-11-21 01:02:25 +01:00
return ref;
2018-11-20 18:39:48 +01:00
}
2018-11-22 23:31:41 +01:00
}
protected boolean exists(long index) {
checkClosed();
synchronized (referencesAccessLock) {
synchronized (indicesAccessLock) {
return this.references.containsKey(index) || this.indices.has(index);
}
}
}
protected <T> EntryReference<T> set(long index, T value) throws IOException {
checkClosed();
synchronized (referencesAccessLock) {
EntryReference<T> ref;
if (exists(index)) {
ref = get(index);
2018-12-11 23:00:51 +01:00
ref.setValue(value);
2018-11-22 23:31:41 +01:00
return ref;
} else {
2018-12-11 23:00:51 +01:00
@SuppressWarnings("unchecked")
2018-11-22 23:31:41 +01:00
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
2018-12-11 23:00:51 +01:00
long hash;
2018-11-22 23:31:41 +01:00
synchronized (indicesAccessLock) {
2018-12-11 23:00:51 +01:00
IndexDetails returnedDetails = indices.set(index, typeParser.getWriter(value));
hash = returnedDetails.getHash();
2018-11-22 23:31:41 +01:00
}
2018-12-11 23:00:51 +01:00
ref = new EntryReference<>(entryReferenceTools, index, hash, typeParser);
2018-11-27 10:26:01 +01:00
this.references.put(index, new WeakReference<EntryReference<?>>(ref));
2018-11-22 23:31:41 +01:00
return ref;
}
}
2018-11-20 18:39:48 +01:00
}
2018-12-06 12:30:04 +01:00
public boolean isOpen() {
return !closed;
}
2018-11-21 01:02:25 +01:00
@Override
public void close() throws IOException {
2018-11-22 23:31:41 +01:00
if (closed) {
return;
}
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
2018-12-05 02:39:41 +01:00
this.databaseCleaner.stop();
2018-11-22 23:31:41 +01:00
synchronized (referencesAccessLock) {
2018-12-05 02:39:41 +01:00
ObjectIterator<WeakReference<EntryReference<?>>> iterator = references.values().iterator();
while (iterator.hasNext()) {
WeakReference<EntryReference<?>> referenceRef = iterator.next();
2018-11-27 10:26:01 +01:00
EntryReference<?> reference = referenceRef.get();
if (reference != null) {
reference.close();
2018-12-05 02:39:41 +01:00
iterator.remove();
2018-11-27 10:26:01 +01:00
}
2018-12-05 02:39:41 +01:00
2018-11-22 23:31:41 +01:00
}
}
synchronized (indicesAccessLock) {
this.indices.close();
}
System.out.println("Database closed.");
}
private void checkClosed() {
if (closed) {
throw new RuntimeException("Index Manager is closed.");
2018-11-21 01:02:25 +01:00
}
2018-11-19 15:16:12 +01:00
}
2018-11-27 17:47:19 +01:00
@Override
public long clean() {
2018-12-05 02:39:41 +01:00
long removedItems = cleanEmptyReferences()
2018-11-27 17:47:19 +01:00
+ cleanExtraReferences()
+ indices.clean();
2018-12-05 02:39:41 +01:00
return removedItems;
2018-11-27 17:47:19 +01:00
}
private long cleanEmptyReferences() {
long removed = 0;
synchronized(referencesAccessLock) {
2018-12-05 02:39:41 +01:00
ObjectIterator<Entry<WeakReference<EntryReference<?>>>> iterator = references.long2ObjectEntrySet().iterator();
while (iterator.hasNext()) {
Entry<WeakReference<EntryReference<?>>> entry = iterator.next();
2018-11-27 17:47:19 +01:00
if (entry.getValue().get() == null) {
2018-12-05 02:39:41 +01:00
iterator.remove();
2018-11-27 17:47:19 +01:00
removed++;
}
}
}
return removed;
}
private long cleanExtraReferences() {
long removedReferences = 0;
synchronized(referencesAccessLock) {
if (references.size() > MAX_LOADED_REFERENCES) {
long count = 0;
2018-12-05 02:39:41 +01:00
ObjectIterator<Entry<WeakReference<EntryReference<?>>>> iterator = references.long2ObjectEntrySet().iterator();
while (iterator.hasNext()) {
Entry<WeakReference<EntryReference<?>>> entry = iterator.next();
2018-11-27 17:47:19 +01:00
if (count > MAX_LOADED_REFERENCES * 3l / 2l) {
2018-12-11 23:00:51 +01:00
WeakReference<EntryReference<?>> weakRef = entry.getValue();
EntryReference<?> ref = weakRef.get();
if (ref != null) {
try {
ref.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2018-12-05 02:39:41 +01:00
iterator.remove();
2018-11-27 17:47:19 +01:00
removedReferences++;
} else {
count++;
}
}
}
}
return removedReferences;
}
2018-12-05 02:39:41 +01:00
public class EntryReferenceTools {
private EntryReferenceTools() {
}
public <T> T read(long index, DBReader<T> reader) throws IOException {
return indices.get(index, reader);
}
2018-12-11 23:00:51 +01:00
public <T> IndexDetails write(long index, DBDataOutput<T> writer) throws IOException {
return indices.set(index, writer);
2018-12-05 02:39:41 +01:00
}
}
2018-12-04 23:57:49 +01:00
@SuppressWarnings("unchecked")
protected <T> long calculateHash(T o) {
return ((DBTypeParser<T>) typesManager.get(o.getClass())).calculateHash(o);
}
2018-11-19 15:16:12 +01:00
}