Version 1.5.5
This commit is contained in:
parent
67c21e382b
commit
ca03ef4791
10
pom.xml
10
pom.xml
@ -6,9 +6,9 @@
|
||||
|
||||
<groupId>it.cavallium</groupId>
|
||||
<artifactId>strangedb</artifactId>
|
||||
<version>1.5.4</version>
|
||||
<version>1.5.5</version>
|
||||
|
||||
<name>strangedb</name>
|
||||
<name>strangedb-java</name>
|
||||
<url>https://git.ignuranza.net/andreacavalli/strangedb</url>
|
||||
|
||||
<properties>
|
||||
@ -43,9 +43,9 @@
|
||||
<version>5.0.0-RC1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.openhft</groupId>
|
||||
<artifactId>zero-allocation-hashing</artifactId>
|
||||
<version>0.8</version>
|
||||
<groupId>it.cavallium</groupId>
|
||||
<artifactId>strangedb-core</artifactId>
|
||||
<version>1.5.5</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
|
||||
<dependency>
|
||||
|
@ -1,44 +0,0 @@
|
||||
package it.cavallium.strangedb;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class BlockInfo {
|
||||
private final long index;
|
||||
private final int size;
|
||||
|
||||
public BlockInfo(long index, int size) {
|
||||
this.index = index;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BlockInfo blockInfo = (BlockInfo) o;
|
||||
return index == blockInfo.index &&
|
||||
size == blockInfo.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(index, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringJoiner(", ", BlockInfo.class.getSimpleName() + "[", "]")
|
||||
.add("index=" + index)
|
||||
.add("size=" + size)
|
||||
.toString();
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package it.cavallium.strangedb;
|
||||
|
||||
public class VariableWrapper<T> {
|
||||
|
||||
public T var;
|
||||
|
||||
public VariableWrapper(T value) {
|
||||
this.var = value;
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import it.cavallium.strangedb.functionalinterfaces.FunctionWithIO;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class Database implements IDatabase, IDatabaseTools {
|
||||
|
||||
private final IDatabaseTools databaseTools;
|
||||
private final DatabaseFileIO fileIO;
|
||||
private final DatabaseBlocksIO blocksIO;
|
||||
private final DatabaseBlocksMetadata blocksMetadata;
|
||||
private final DatabaseReferencesIO referencesIO;
|
||||
private final DatabaseReferencesMetadata referencesMetadata;
|
||||
private final DatabaseObjectsIO objectsIO;
|
||||
private final Path dataFile;
|
||||
private final Path blocksMetaFile;
|
||||
private final Path referencesMetaFile;
|
||||
private EnhancedObject loadedRootObject;
|
||||
private volatile boolean closed;
|
||||
|
||||
public Database(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
||||
if (Files.notExists(dataFile)) {
|
||||
Files.createFile(dataFile);
|
||||
}
|
||||
if (Files.notExists(blocksMetaFile)) {
|
||||
Files.createFile(blocksMetaFile);
|
||||
}
|
||||
if (Files.notExists(referencesMetaFile)) {
|
||||
Files.createFile(referencesMetaFile);
|
||||
}
|
||||
this.dataFile = dataFile;
|
||||
this.blocksMetaFile = blocksMetaFile;
|
||||
this.referencesMetaFile = referencesMetaFile;
|
||||
this.databaseTools = this;
|
||||
this.fileIO = new DatabaseFileIO(dataFile);
|
||||
this.blocksMetadata = new DatabaseBlocksMetadata(blocksMetaFile);
|
||||
this.blocksIO = new DatabaseBlocksIO(fileIO, blocksMetadata);
|
||||
this.referencesMetadata = new DatabaseReferencesMetadata(referencesMetaFile);
|
||||
this.referencesIO = new DatabaseReferencesIO(blocksIO, referencesMetadata);
|
||||
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("The database has been already closed!");
|
||||
}
|
||||
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
||||
this.referencesMetadata.close();
|
||||
this.blocksMetadata.close();
|
||||
this.fileIO.close();
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAndClean() throws IOException {
|
||||
if (!this.closed) {
|
||||
this.close();
|
||||
}
|
||||
Path newDataFile = dataFile.resolveSibling("compressed-data-file.tmp");
|
||||
Path newBlocksFile = blocksMetaFile.resolveSibling("compressed-blocks-file.tmp");
|
||||
Path newReferencesFile = referencesMetaFile.resolveSibling("compressed-references-file.tmp");
|
||||
Path backupDataFile = dataFile.resolveSibling("backup-data.db.bak");
|
||||
Path backupBlocksFile = blocksMetaFile.resolveSibling("backup-blocks.dat.bak");
|
||||
Path backupReferencesFile = referencesMetaFile.resolveSibling("backup-references.dat.bak");
|
||||
Files.copy(dataFile, backupDataFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
Files.copy(blocksMetaFile, backupBlocksFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
Files.copy(referencesMetaFile, backupReferencesFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
Files.move(dataFile, newDataFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(blocksMetaFile, newBlocksFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(referencesMetaFile, newReferencesFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
Database databaseToClean = new Database(newDataFile, newBlocksFile, newReferencesFile);
|
||||
Database newDatabase = new Database(dataFile, blocksMetaFile, referencesMetaFile);
|
||||
|
||||
long referencesCount = databaseToClean.referencesMetadata.getFirstFreeReference();
|
||||
long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount();
|
||||
long writtenReferences = 0;
|
||||
|
||||
for (int referenceID = 0; referenceID < referencesCount; referenceID++) {
|
||||
try {
|
||||
ByteBuffer buffer = databaseToClean.referencesIO.readFromReference(referenceID);
|
||||
newDatabase.referencesIO.writeToReference(referenceID, buffer.limit(), buffer);
|
||||
writtenReferences++;
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences);
|
||||
}
|
||||
}
|
||||
System.out.println("References written: " + writtenReferences + ". Removed " + (blocksCount - writtenReferences) + " blocks. Removed " + (referencesCount - writtenReferences) + " references.");
|
||||
databaseToClean.close();
|
||||
newDatabase.close();
|
||||
Files.deleteIfExists(newDataFile);
|
||||
Files.deleteIfExists(newBlocksFile);
|
||||
Files.deleteIfExists(newReferencesFile);
|
||||
}
|
||||
|
||||
public <T extends EnhancedObject> T loadRoot(Class<T> type, FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
|
||||
if (loadedRootObject != null) {
|
||||
throw new RuntimeException("Root already set!");
|
||||
}
|
||||
T root;
|
||||
if (referencesMetadata.getFirstFreeReference() > 0) {
|
||||
root = objectsIO.loadEnhancedObject(0, type);
|
||||
} else {
|
||||
if (objectsIO.newNullObject() != 0) {
|
||||
throw new IOException("Can't allocate root!");
|
||||
} else {
|
||||
root = ifAbsent.apply(Database.this);
|
||||
objectsIO.setEnhancedObject(0, root);
|
||||
}
|
||||
}
|
||||
loadedRootObject = root;
|
||||
return root;
|
||||
}
|
||||
|
||||
protected void registerClass(Class<?> type, int id) {
|
||||
this.objectsIO.registerClass(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
|
||||
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectsIO getObjectsIO() {
|
||||
return objectsIO;
|
||||
}
|
||||
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import it.cavallium.strangedb.BlockInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
||||
|
||||
public class DatabaseBlocksIO implements IBlocksIO {
|
||||
|
||||
private final DatabaseFileIO fileIO;
|
||||
private final IBlocksMetadata blocksMetadata;
|
||||
|
||||
public DatabaseBlocksIO(DatabaseFileIO fileIO, IBlocksMetadata blocksMetadata) {
|
||||
this.fileIO = fileIO;
|
||||
this.blocksMetadata = blocksMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long newBlock(int size, ByteBuffer data) throws IOException {
|
||||
if (size == 0) {
|
||||
return EMPTY_BLOCK_ID;
|
||||
}
|
||||
long index = fileIO.writeAtEnd(size, data);
|
||||
return blocksMetadata.newBlock(index, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer readBlock(long blockId) throws IOException {
|
||||
if (blockId == EMPTY_BLOCK_ID) {
|
||||
return ByteBuffer.wrap(new byte[0]);
|
||||
}
|
||||
BlockInfo blockInfo = blocksMetadata.getBlockInfo(blockId);
|
||||
return fileIO.readAt(blockInfo.getIndex(), blockInfo.getSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
|
||||
import it.cavallium.strangedb.BlockInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class DatabaseBlocksMetadata implements IBlocksMetadata {
|
||||
public static final BlockInfo ERROR_BLOCK_INFO = new BlockInfo(-2, 0);
|
||||
|
||||
private final AsynchronousFileChannel metaFileChannel;
|
||||
private final int BLOCK_META_BYTES_COUNT = Long.BYTES + Integer.BYTES;
|
||||
private final DatabaseBlocksMetadataCache cache;
|
||||
private long firstFreeBlock;
|
||||
|
||||
public DatabaseBlocksMetadata(Path metaFile) throws IOException {
|
||||
metaFileChannel = AsynchronousFileChannel.open(metaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
firstFreeBlock = metaFileChannel.size() / BLOCK_META_BYTES_COUNT;
|
||||
this.cache = new DatabaseBlocksMetadataCache(this::writeBlockToDisk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockInfo getBlockInfo(long blockId) throws IOException {
|
||||
if (blockId == EMPTY_BLOCK_ID) {
|
||||
return EMPTY_BLOCK_INFO;
|
||||
}
|
||||
BlockInfo blockInfo;
|
||||
if ((blockInfo = cache.get(blockId)) != ERROR_BLOCK_INFO) {
|
||||
return blockInfo;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
|
||||
try {
|
||||
metaFileChannel.read(buffer, blockId * BLOCK_META_BYTES_COUNT).get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IOException(e.getCause());
|
||||
}
|
||||
buffer.flip();
|
||||
long index = buffer.getLong();
|
||||
int size = buffer.getInt();
|
||||
blockInfo = new BlockInfo(index, size);
|
||||
cache.put(blockId, blockInfo);
|
||||
return blockInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long newBlock(long index, int size) throws IOException {
|
||||
if (size == 0) {
|
||||
return EMPTY_BLOCK_ID;
|
||||
}
|
||||
long newBlockId = firstFreeBlock++;
|
||||
BlockInfo blockInfo = new BlockInfo(index, size);
|
||||
cache.put(newBlockId, blockInfo);
|
||||
return newBlockId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cache.close();
|
||||
metaFileChannel.close();
|
||||
}
|
||||
|
||||
private Future<Integer> writeBlockToDisk(long blockId, long index, int size) {
|
||||
ByteBuffer data = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT);
|
||||
data.putLong(index);
|
||||
data.putInt(size);
|
||||
data.flip();
|
||||
return metaFileChannel.write(data, blockId * BLOCK_META_BYTES_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalBlocksCount() {
|
||||
return firstFreeBlock;
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import it.cavallium.strangedb.BlockInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class DatabaseBlocksMetadataCache {
|
||||
|
||||
private static final int GOOD_CACHE_SIZE = 70000;
|
||||
private static final int MAX_CACHE_SIZE = 100000;
|
||||
|
||||
private final Long2ObjectMap<BlockInfo> blocks2Info = new Long2ObjectLinkedOpenHashMap<>(MAX_CACHE_SIZE);
|
||||
private final Object readAccessLock = new Object();
|
||||
private final Object writeAccessLock = new Object();
|
||||
private final DatabaseBlocksMetadataCacheFlusher flusher;
|
||||
private volatile boolean closed;
|
||||
|
||||
public DatabaseBlocksMetadataCache(DatabaseBlocksMetadataCacheFlusher flusher) {
|
||||
this.flusher = flusher;
|
||||
}
|
||||
|
||||
public BlockInfo get(long block) throws IOException {
|
||||
if (closed) throw new IOException("Cache already closed!");
|
||||
synchronized (readAccessLock) {
|
||||
return blocks2Info.getOrDefault(block, DatabaseBlocksMetadata.ERROR_BLOCK_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(long block, BlockInfo blockInfo) throws IOException {
|
||||
if (closed) return;
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
blocks2Info.put(block, blockInfo);
|
||||
}
|
||||
}
|
||||
this.flush();
|
||||
}
|
||||
|
||||
private void flush() throws IOException {
|
||||
if (closed) return;
|
||||
int blocks2InfoSize = blocks2Info.size();
|
||||
if (blocks2InfoSize > MAX_CACHE_SIZE) {
|
||||
synchronized (writeAccessLock) {
|
||||
ObjectIterator<Long2ObjectMap.Entry<BlockInfo>> entriesIterator = blocks2Info.long2ObjectEntrySet().iterator();
|
||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
||||
while (blocks2InfoSize > GOOD_CACHE_SIZE) {
|
||||
Long2ObjectMap.Entry<BlockInfo> entry = entriesIterator.next();
|
||||
BlockInfo blockInfo = entry.getValue();
|
||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), blockInfo.getIndex(), blockInfo.getSize()));
|
||||
entriesIterator.remove();
|
||||
if (entriesToFlush.size() >= 1000) {
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
blocks2InfoSize--;
|
||||
}
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
int blocks2InfoSize = blocks2Info.size();
|
||||
ObjectIterator<Long2ObjectMap.Entry<BlockInfo>> entriesIterator = blocks2Info.long2ObjectEntrySet().iterator();
|
||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
||||
while (blocks2InfoSize > 0) {
|
||||
Long2ObjectMap.Entry<BlockInfo> entry = entriesIterator.next();
|
||||
BlockInfo blockInfo = entry.getValue();
|
||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), blockInfo.getIndex(), blockInfo.getSize()));
|
||||
entriesIterator.remove();
|
||||
if (entriesToFlush.size() >= 1000) {
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
blocks2InfoSize--;
|
||||
}
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAsyncFlush(List<Future<?>> entriesToFlush) throws IOException {
|
||||
try {
|
||||
for (Future<?> entryToFlush : entriesToFlush) {
|
||||
entryToFlush.get();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IOException(e.getCause());
|
||||
} finally {
|
||||
entriesToFlush.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public interface DatabaseBlocksMetadataCacheFlusher {
|
||||
Future<Integer> flush(long key, long value1, int value2) throws IOException;
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class DatabaseFileIO implements IFileIO {
|
||||
|
||||
private final SeekableByteChannel dataFileChannel;
|
||||
private final Object dataAccessLock = new Object();
|
||||
private long firstFreeIndex;
|
||||
|
||||
public DatabaseFileIO(Path dataFile) throws IOException {
|
||||
synchronized (dataAccessLock) {
|
||||
dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
firstFreeIndex = dataFileChannel.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer readAt(long index, int length) throws IOException {
|
||||
ByteBuffer dataBuffer = ByteBuffer.allocate(length);
|
||||
dataFileChannel.position(index).read(dataBuffer);
|
||||
dataBuffer.flip();
|
||||
return dataBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeAt(long index, int length, ByteBuffer data) throws IOException {
|
||||
synchronized (dataAccessLock) {
|
||||
if (data.position() != 0) {
|
||||
throw new IOException("You didn't flip the ByteBuffer!");
|
||||
}
|
||||
if (firstFreeIndex < index + length) {
|
||||
firstFreeIndex = index + length;
|
||||
}
|
||||
dataFileChannel.position(index).write(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long writeAtEnd(int length, ByteBuffer data) throws IOException {
|
||||
synchronized (dataAccessLock) {
|
||||
long index = firstFreeIndex;
|
||||
firstFreeIndex += length;
|
||||
writeAt(index, length, data);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
synchronized (dataAccessLock) {
|
||||
dataFileChannel.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
||||
|
||||
public class DatabaseReferencesIO implements IReferencesIO {
|
||||
|
||||
private final DatabaseBlocksIO blocksIO;
|
||||
private final DatabaseReferencesMetadata referencesMetadata;
|
||||
|
||||
public DatabaseReferencesIO(DatabaseBlocksIO blocksIO, DatabaseReferencesMetadata referencesMetadata) {
|
||||
this.blocksIO = blocksIO;
|
||||
this.referencesMetadata = referencesMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long allocateReference() throws IOException {
|
||||
return referencesMetadata.newReference(EMPTY_BLOCK_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long allocateReference(int size, ByteBuffer data) throws IOException {
|
||||
long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
|
||||
return referencesMetadata.newReference(blockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToReference(long reference, int size, ByteBuffer data) throws IOException {
|
||||
long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data);
|
||||
referencesMetadata.editReference(reference, blockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer readFromReference(long reference) throws IOException {
|
||||
long blockId = referencesMetadata.getReference(reference);
|
||||
return blocksIO.readBlock(blockId);
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousFileChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID;
|
||||
import static it.cavallium.strangedb.database.IBlocksMetadata.ERROR_BLOCK_ID;
|
||||
|
||||
public class DatabaseReferencesMetadata implements IReferencesMetadata {
|
||||
private final AsynchronousFileChannel metaFileChannel;
|
||||
private final int REF_META_BYTES_COUNT = Long.BYTES;
|
||||
private final DatabaseReferencesMetadataCache cache;
|
||||
private long firstFreeReference;
|
||||
|
||||
public DatabaseReferencesMetadata(Path refMetaFile) throws IOException {
|
||||
metaFileChannel = AsynchronousFileChannel.open(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
|
||||
firstFreeReference = metaFileChannel.size() / REF_META_BYTES_COUNT;
|
||||
this.cache = new DatabaseReferencesMetadataCache(this::writeReferenceToDisk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getReference(long reference) throws IOException {
|
||||
if (reference >= firstFreeReference) {
|
||||
return EMPTY_BLOCK_ID;
|
||||
}
|
||||
long block;
|
||||
if ((block = cache.get(reference)) != ERROR_BLOCK_ID) {
|
||||
return block;
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT);
|
||||
try {
|
||||
metaFileChannel.read(buffer, reference * REF_META_BYTES_COUNT).get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IOException(e.getCause());
|
||||
}
|
||||
buffer.flip();
|
||||
block = buffer.getLong();
|
||||
if (buffer.limit() != 0 && block != 0xFFFFFFFFFFFFFFFFL) {
|
||||
cache.put(reference, block);
|
||||
return block;
|
||||
} else {
|
||||
cache.put(reference, EMPTY_BLOCK_ID);
|
||||
return EMPTY_BLOCK_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long newReference(long blockId) throws IOException {
|
||||
long newReference = firstFreeReference++;
|
||||
cache.put(newReference, blockId);
|
||||
return newReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editReference(long reference, long blockId) throws IOException {
|
||||
cache.put(reference, blockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
cache.close();
|
||||
metaFileChannel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstFreeReference() {
|
||||
return firstFreeReference;
|
||||
}
|
||||
|
||||
private Future<Integer> writeReferenceToDisk(long reference, long blockId) {
|
||||
ByteBuffer data = ByteBuffer.allocate(REF_META_BYTES_COUNT);
|
||||
data.putLong(blockId);
|
||||
data.flip();
|
||||
return metaFileChannel.write(data, reference * REF_META_BYTES_COUNT);
|
||||
}
|
||||
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static it.cavallium.strangedb.database.IBlocksMetadata.ERROR_BLOCK_ID;
|
||||
|
||||
public class DatabaseReferencesMetadataCache {
|
||||
|
||||
private static final int GOOD_CACHE_SIZE = 70000;
|
||||
private static final int MAX_CACHE_SIZE = 100000;
|
||||
|
||||
private final Long2LongMap references2Blocks = new Long2LongLinkedOpenHashMap(MAX_CACHE_SIZE);
|
||||
private final Object readAccessLock = new Object();
|
||||
private final Object writeAccessLock = new Object();
|
||||
private final DatabaseReferencesMetadataCacheFlusher flusher;
|
||||
private volatile boolean closed;
|
||||
|
||||
public DatabaseReferencesMetadataCache(DatabaseReferencesMetadataCacheFlusher flusher) {
|
||||
this.flusher = flusher;
|
||||
}
|
||||
|
||||
public long get(long reference) throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
if (closed) throw new IOException("Cache already closed!");
|
||||
return references2Blocks.getOrDefault(reference, ERROR_BLOCK_ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(long reference, long blockId) throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
if (closed) return;
|
||||
references2Blocks.put(reference, blockId);
|
||||
}
|
||||
}
|
||||
this.flush();
|
||||
}
|
||||
|
||||
private void flush() throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
if (closed) return;
|
||||
int references2BlocksSize = references2Blocks.size();
|
||||
if (references2BlocksSize > MAX_CACHE_SIZE) {
|
||||
synchronized (writeAccessLock) {
|
||||
ObjectIterator<Long2LongMap.Entry> entriesIterator = references2Blocks.long2LongEntrySet().iterator();
|
||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
||||
while (references2BlocksSize > GOOD_CACHE_SIZE) {
|
||||
Long2LongMap.Entry entry = entriesIterator.next();
|
||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), entry.getLongValue()));
|
||||
entriesIterator.remove();
|
||||
if (entriesToFlush.size() >= 1000) {
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
references2BlocksSize--;
|
||||
}
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
int references2BlocksSize = references2Blocks.size();
|
||||
ObjectIterator<Long2LongMap.Entry> entriesIterator = references2Blocks.long2LongEntrySet().iterator();
|
||||
List<Future<?>> entriesToFlush = new LinkedList<>();
|
||||
while (references2BlocksSize > 0) {
|
||||
Long2LongMap.Entry entry = entriesIterator.next();
|
||||
entriesToFlush.add(flusher.flush(entry.getLongKey(), entry.getLongValue()));
|
||||
entriesIterator.remove();
|
||||
if (entriesToFlush.size() >= 1000) {
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
references2BlocksSize--;
|
||||
}
|
||||
executeAsyncFlush(entriesToFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAsyncFlush(List<Future<?>> entriesToFlush) throws IOException {
|
||||
synchronized (readAccessLock) {
|
||||
synchronized (writeAccessLock) {
|
||||
try {
|
||||
for (Future<?> entryToFlush : entriesToFlush) {
|
||||
entryToFlush.get();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IOException(e.getCause());
|
||||
} finally {
|
||||
entriesToFlush.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public interface DatabaseReferencesMetadataCacheFlusher {
|
||||
Future<Integer> flush(long key, long value) throws IOException;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface IBlocksIO {
|
||||
/**
|
||||
* Allocate a block
|
||||
* @param size block size
|
||||
* @param data block data
|
||||
* @return the block id
|
||||
*/
|
||||
long newBlock(int size, ByteBuffer data) throws IOException;
|
||||
|
||||
/**
|
||||
* Read a block
|
||||
* @param blockId block id
|
||||
* @return block data
|
||||
*/
|
||||
ByteBuffer readBlock(long blockId) throws IOException;
|
||||
|
||||
/**
|
||||
* Close file
|
||||
*/
|
||||
void close();
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import it.cavallium.strangedb.BlockInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IBlocksMetadata {
|
||||
long EMPTY_BLOCK_ID = -1;
|
||||
long ERROR_BLOCK_ID = -2;
|
||||
BlockInfo EMPTY_BLOCK_INFO = new BlockInfo(0, 0);
|
||||
|
||||
/**
|
||||
* Get block info
|
||||
* @param blockId block id
|
||||
* @return block metadata
|
||||
*/
|
||||
BlockInfo getBlockInfo(long blockId) throws IOException;
|
||||
|
||||
/**
|
||||
* New empty block info
|
||||
* @return block id
|
||||
*/
|
||||
default long newBlock() {
|
||||
return EMPTY_BLOCK_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set block info
|
||||
* @param index block index
|
||||
* @param size block size
|
||||
* @return block id
|
||||
*/
|
||||
long newBlock(long index, int size) throws IOException;
|
||||
|
||||
/**
|
||||
* Set block info
|
||||
* @param blockInfo block info
|
||||
* @return block id
|
||||
*/
|
||||
default long newBlock(BlockInfo blockInfo) throws IOException {
|
||||
return this.newBlock(blockInfo.getIndex(), blockInfo.getSize());
|
||||
}
|
||||
/**
|
||||
* Close file
|
||||
*/
|
||||
void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Get total count of blocks
|
||||
* @return
|
||||
*/
|
||||
long getTotalBlocksCount();
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IDatabase {
|
||||
|
||||
void close() throws IOException;
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
void closeAndClean() throws IOException;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface IFileIO {
|
||||
/**
|
||||
* Read *length* bytes in position *index*
|
||||
* @param index index
|
||||
* @param length length
|
||||
* @return bytes
|
||||
*/
|
||||
ByteBuffer readAt(long index, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* Write *length* bytes in position *index*
|
||||
* @param index index
|
||||
* @param length length
|
||||
* @param data bytes
|
||||
*/
|
||||
void writeAt(long index, int length, ByteBuffer data) throws IOException;
|
||||
|
||||
/**
|
||||
* Write *length* bytes in position *index*
|
||||
* @param length length
|
||||
* @param data bytes
|
||||
* @return index
|
||||
*/
|
||||
long writeAtEnd(int length, ByteBuffer data) throws IOException;
|
||||
|
||||
/**
|
||||
* Close the file
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface IReferencesIO {
|
||||
/**
|
||||
* Allocate a new empty reference
|
||||
* @return the new reference
|
||||
*/
|
||||
long allocateReference() throws IOException;
|
||||
|
||||
/**
|
||||
* Allocate a new reference with that data
|
||||
* @param size data size
|
||||
* @param data bytes
|
||||
* @return the new reference
|
||||
*/
|
||||
long allocateReference(int size, ByteBuffer data) throws IOException;
|
||||
|
||||
/**
|
||||
* Write some data to the reference
|
||||
* @param reference reference
|
||||
* @param size data size
|
||||
* @param data bytes
|
||||
*/
|
||||
void writeToReference(long reference, int size, ByteBuffer data) throws IOException;
|
||||
|
||||
/**
|
||||
* Read data from the reference
|
||||
* @param reference reference
|
||||
* @return bytes
|
||||
*/
|
||||
ByteBuffer readFromReference(long reference) throws IOException;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IReferencesMetadata {
|
||||
/**
|
||||
* Get block of reference
|
||||
* @param reference reference
|
||||
* @return block id
|
||||
*/
|
||||
long getReference(long reference) throws IOException;
|
||||
|
||||
/**
|
||||
* Allocate a block for a new reference
|
||||
* @param blockId block id
|
||||
* @return reference
|
||||
*/
|
||||
long newReference(long blockId) throws IOException;
|
||||
|
||||
/**
|
||||
* Change reference size
|
||||
* @param reference reference
|
||||
* @param blockId block id
|
||||
*/
|
||||
void editReference(long reference, long blockId) throws IOException;
|
||||
|
||||
/**
|
||||
* Close file
|
||||
*/
|
||||
void close() throws IOException;
|
||||
|
||||
long getFirstFreeReference();
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
|
||||
public interface Long2LongConsumer {
|
||||
void accept(long a, long b);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package it.cavallium.strangedb.functionalinterfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConsumerWithIO<T> {
|
||||
|
||||
void accept(T t) throws IOException;
|
||||
|
||||
default ConsumerWithIO<T> andThen(ConsumerWithIO<? super T> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return (T t) -> { accept(t); after.accept(t); };
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package it.cavallium.strangedb.functionalinterfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a function that accepts one argument and produces a result.
|
||||
*
|
||||
* <p>This is a <a href="package-summary.html">functional interface</a>
|
||||
* whose functional method is {@link #apply(Object)}.
|
||||
*
|
||||
* @param <T> the type of the input to the function
|
||||
* @param <R> the type of the result of the function
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FunctionWithIO<T, R> {
|
||||
|
||||
/**
|
||||
* Applies this function to the given argument.
|
||||
*
|
||||
* @param t the function argument
|
||||
* @return the function result
|
||||
*/
|
||||
R apply(T t) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a composed function that first applies the {@code before}
|
||||
* function to its input, and then applies this function to the result.
|
||||
* If evaluation of either function throws an exception, it is relayed to
|
||||
* the caller of the composed function.
|
||||
*
|
||||
* @param <V> the type of input to the {@code before} function, and to the
|
||||
* composed function
|
||||
* @param before the function to apply before this function is applied
|
||||
* @return a composed function that first applies the {@code before}
|
||||
* function and then applies this function
|
||||
* @throws NullPointerException if before is null
|
||||
*
|
||||
* @see #andThen(FunctionWithIO)
|
||||
*/
|
||||
default <V> FunctionWithIO<V, R> compose(FunctionWithIO<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return (V v) -> apply(before.apply(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a composed function that first applies this function to
|
||||
* its input, and then applies the {@code after} function to the result.
|
||||
* If evaluation of either function throws an exception, it is relayed to
|
||||
* the caller of the composed function.
|
||||
*
|
||||
* @param <V> the type of output of the {@code after} function, and of the
|
||||
* composed function
|
||||
* @param after the function to apply after this function is applied
|
||||
* @return a composed function that first applies this function and then
|
||||
* applies the {@code after} function
|
||||
* @throws NullPointerException if after is null
|
||||
*
|
||||
* @see #compose(FunctionWithIO)
|
||||
*/
|
||||
default <V> FunctionWithIO<T, V> andThen(FunctionWithIO<? super R, ? extends V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return (T t) -> after.apply(apply(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that always returns its input argument.
|
||||
*
|
||||
* @param <T> the type of the input and output objects to the function
|
||||
* @return a function that always returns its input argument
|
||||
*/
|
||||
static <T> FunctionWithIO<T, T> identity() {
|
||||
return t -> t;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
package it.cavallium.strangedb.functionalinterfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RunnableWithIO {
|
||||
/**
|
||||
* When an object implementing interface <code>Runnable</code> is used
|
||||
* to create a thread, starting the thread causes the object's
|
||||
* <code>run</code> method to be called in that separately executing
|
||||
* thread.
|
||||
* <p>
|
||||
* The general contract of the method <code>run</code> is that it may
|
||||
* take any action whatsoever.
|
||||
*
|
||||
* @see java.lang.Thread#run()
|
||||
*/
|
||||
public abstract void run() throws IOException;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package it.cavallium.strangedb.functionalinterfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SupplierWithIO<T> {
|
||||
public T getWithIO() throws IOException;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
public enum DbDataType {
|
||||
ENHANCED_OBJECT,
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
public enum DbPrimitiveType {
|
||||
BOOLEAN,
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,4 +1,4 @@
|
||||
package it.cavallium.strangedb.annotations;
|
||||
package it.cavallium.strangedb.java.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
@ -1,8 +1,8 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.annotations.*;
|
||||
import it.cavallium.strangedb.java.annotations.*;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
@ -1,9 +1,9 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import it.cavallium.strangedb.EnhancedObjectUpgrader;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObjectUpgrader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
@ -0,0 +1,62 @@
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.database.DatabaseCore;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.functionalinterfaces.FunctionWithIO;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class DatabaseJava extends DatabaseCore implements IDatabaseTools {
|
||||
private final IDatabaseTools databaseTools;
|
||||
private final DatabaseObjectsIO objectsIO;
|
||||
private EnhancedObject loadedRootObject;
|
||||
|
||||
public DatabaseJava(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
||||
super(dataFile, blocksMetaFile, referencesMetaFile);
|
||||
this.databaseTools = this;
|
||||
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (this.closed) {
|
||||
throw new IOException("The database has been already closed!");
|
||||
}
|
||||
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
||||
}
|
||||
|
||||
public <T extends EnhancedObject> T loadRoot(Class<T> type, FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
|
||||
if (loadedRootObject != null) {
|
||||
throw new RuntimeException("Root already set!");
|
||||
}
|
||||
T root;
|
||||
if (referencesMetadata.getFirstFreeReference() > 0) {
|
||||
root = objectsIO.loadEnhancedObject(0, type);
|
||||
} else {
|
||||
if (objectsIO.newNullObject() != 0) {
|
||||
throw new IOException("Can't allocate root!");
|
||||
} else {
|
||||
root = ifAbsent.apply(DatabaseJava.this);
|
||||
objectsIO.setEnhancedObject(0, root);
|
||||
}
|
||||
}
|
||||
loadedRootObject = root;
|
||||
return root;
|
||||
}
|
||||
|
||||
protected void registerClass(Class<?> type, int id) {
|
||||
this.objectsIO.registerClass(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
|
||||
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IObjectsIO getObjectsIO() {
|
||||
return objectsIO;
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import it.cavallium.strangedb.annotations.*;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObjectFullInfo;
|
||||
import it.cavallium.strangedb.database.references.DatabaseReferencesIO;
|
||||
import it.cavallium.strangedb.java.annotations.*;
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
||||
@ -12,7 +15,6 @@ import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
@ -1,6 +1,6 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IObjectsIO;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.database;
|
||||
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
|
@ -1,14 +1,13 @@
|
||||
package it.cavallium.strangedb;
|
||||
package it.cavallium.strangedb.java.objects;
|
||||
|
||||
import it.cavallium.strangedb.database.EnhancedObjectFullInfo;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import it.cavallium.strangedb.annotations.DbClass;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
||||
import it.cavallium.strangedb.java.annotations.DbClass;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
@ -1,7 +1,7 @@
|
||||
package it.cavallium.strangedb.database;
|
||||
package it.cavallium.strangedb.java.objects;
|
||||
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@ -33,15 +33,15 @@ public class EnhancedObjectFullInfo {
|
||||
this.loadedPropertyValues = loadedPropertyValues;
|
||||
}
|
||||
|
||||
int getVersion() {
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
long[] getFieldReferences() {
|
||||
public long[] getFieldReferences() {
|
||||
return fieldReferences;
|
||||
}
|
||||
|
||||
DbDataType[] getFieldTypes() {
|
||||
public DbDataType[] getFieldTypes() {
|
||||
return fieldTypes;
|
||||
}
|
||||
|
||||
@ -61,15 +61,15 @@ public class EnhancedObjectFullInfo {
|
||||
return primitiveFields;
|
||||
}
|
||||
|
||||
long[] getPropertyReferences() {
|
||||
public long[] getPropertyReferences() {
|
||||
return propertyReferences;
|
||||
}
|
||||
|
||||
DbDataType[] getPropertyTypes() {
|
||||
public DbDataType[] getPropertyTypes() {
|
||||
return propertyTypes;
|
||||
}
|
||||
|
||||
Object[] getLoadedPropertyValues() {
|
||||
public Object[] getLoadedPropertyValues() {
|
||||
return loadedPropertyValues;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package it.cavallium.strangedb;
|
||||
package it.cavallium.strangedb.java.objects;
|
||||
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
@ -1,10 +1,10 @@
|
||||
package it.cavallium.strangedb.lists;
|
||||
package it.cavallium.strangedb.java.objects.lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -1,9 +1,9 @@
|
||||
package it.cavallium.strangedb.lists;
|
||||
package it.cavallium.strangedb.java.objects.lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -1,8 +1,8 @@
|
||||
package it.cavallium.strangedb.lists;
|
||||
package it.cavallium.strangedb.java.objects.lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.StringJoiner;
|
@ -3,12 +3,12 @@ package it.cavallium.strangedb.tests;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
|
||||
import it.cavallium.strangedb.utils.NTestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -1,9 +1,10 @@
|
||||
package it.cavallium.strangedb.tests;
|
||||
|
||||
import it.cavallium.strangedb.database.DatabaseCore;
|
||||
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import it.cavallium.strangedb.database.Database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -16,14 +17,14 @@ public class EnhancedClassUpdate {
|
||||
private Path path1;
|
||||
private Path path2;
|
||||
private Path path3;
|
||||
private Database db;
|
||||
private DatabaseJava db;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
path1 = Files.createTempFile("db-tests-", ".db");
|
||||
path2 = Files.createTempFile("db-tests-", ".db");
|
||||
path3 = Files.createTempFile("db-tests-", ".db");
|
||||
db = new Database(path1, path2, path3);
|
||||
db = new DatabaseJava(path1, path2, path3);
|
||||
OldClass root = db.loadRoot(OldClass.class, OldClass::new);
|
||||
root.field1 = "Abc";
|
||||
root.field2 = 12;
|
||||
@ -33,7 +34,7 @@ public class EnhancedClassUpdate {
|
||||
|
||||
@Test
|
||||
public void shouldUpdateClass() throws IOException {
|
||||
db = new Database(path1, path2, path3);
|
||||
db = new DatabaseJava(path1, path2, path3);
|
||||
V2Class root = db.loadRoot(V2Class.class, V2Class::new);
|
||||
assertEquals(root.field4, "Abc");
|
||||
assertEquals(root.field2, 12);
|
||||
|
@ -1,11 +1,11 @@
|
||||
package it.cavallium.strangedb.tests;
|
||||
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.annotations.DbPropertySetter;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertyGetter;
|
||||
import it.cavallium.strangedb.java.annotations.DbPropertySetter;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.utils.NTestUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -1,11 +1,11 @@
|
||||
package it.cavallium.strangedb.tests;
|
||||
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
package it.cavallium.strangedb.tests;
|
||||
|
||||
import it.cavallium.strangedb.annotations.*;
|
||||
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
||||
import it.cavallium.strangedb.lists.EnhancedObjectStrandeDbList;
|
||||
import it.cavallium.strangedb.lists.ObjectStrandeDbList;
|
||||
import it.cavallium.strangedb.java.annotations.*;
|
||||
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||
import it.cavallium.strangedb.java.objects.lists.EnhancedObjectStrandeDbList;
|
||||
import it.cavallium.strangedb.java.objects.lists.ObjectStrandeDbList;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.cavallium.strangedb.database.Database;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.database.DatabaseCore;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.VariableWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -23,7 +24,7 @@ public class Performance {
|
||||
private static Path dbDataFile;
|
||||
private static Path dbBlocksFile;
|
||||
private static Path dbReferencesFile;
|
||||
private static Database db;
|
||||
private static DatabaseJava db;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -48,8 +49,8 @@ public class Performance {
|
||||
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
|
||||
System.out.println("Test name Total Time | Time at 1 Time at 10 Time at 100 Time at 1K Time at 10K");
|
||||
System.out.println("-------------------------------------------------------+-----------------------------------------------------------------");
|
||||
testS("Database creation", 3000, Performance::deleteDb, Performance::generateDb, () -> {});
|
||||
testS("Database root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new), () -> {});
|
||||
testS("DatabaseCore creation", 3000, Performance::deleteDb, Performance::generateDb, () -> {});
|
||||
testS("DatabaseCore root creation", 3000, Performance::regenDb, () -> db.loadRoot(PreloadedListContainer.class, PreloadedListContainer::new), () -> {});
|
||||
final VariableWrapper<PreloadedListContainer> preloadedListContainer = new VariableWrapper<>(null);
|
||||
final VariableWrapper<SimpleEnhancedObject> simpleEnhancedObjectContainer = new VariableWrapper<>(null);
|
||||
testS("ObjectStrandeDbList<Int> creation", 3000, () -> {
|
||||
@ -267,7 +268,7 @@ public class Performance {
|
||||
dbDataFile = Files.createFile(rootDirectory.resolve("db_data.dat"));
|
||||
dbBlocksFile = Files.createFile(rootDirectory.resolve("db_blocks.dat"));
|
||||
dbReferencesFile = Files.createFile(rootDirectory.resolve("db_references.dat"));
|
||||
db = new Database(dbDataFile, dbBlocksFile, dbReferencesFile);
|
||||
db = new DatabaseJava(dbDataFile, dbBlocksFile, dbReferencesFile);
|
||||
}
|
||||
|
||||
public static void deleteDb() throws IOException {
|
||||
|
@ -1,13 +1,13 @@
|
||||
package it.cavallium.strangedb.tests;
|
||||
|
||||
import it.cavallium.strangedb.EnhancedObjectUpgrader;
|
||||
import it.cavallium.strangedb.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.annotations.DbField;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.annotations.DbClass;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObjectUpgrader;
|
||||
import it.cavallium.strangedb.java.annotations.DbDataType;
|
||||
import it.cavallium.strangedb.java.annotations.DbField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.annotations.DbClass;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
package it.cavallium.strangedb.utils;
|
||||
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.database.Database;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveField;
|
||||
import it.cavallium.strangedb.java.annotations.DbPrimitiveType;
|
||||
import it.cavallium.strangedb.database.DatabaseCore;
|
||||
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -16,7 +17,7 @@ public class NSimplestClass extends EnhancedObject {
|
||||
|
||||
}
|
||||
|
||||
public NSimplestClass(Database database) throws IOException {
|
||||
public NSimplestClass(DatabaseJava database) throws IOException {
|
||||
super(database);
|
||||
field1 = true;
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package it.cavallium.strangedb.utils;
|
||||
|
||||
import it.cavallium.strangedb.annotations.*;
|
||||
import it.cavallium.strangedb.database.DatabaseCore;
|
||||
import it.cavallium.strangedb.functionalinterfaces.RunnableWithIO;
|
||||
import it.cavallium.strangedb.java.annotations.*;
|
||||
import it.cavallium.strangedb.java.database.DatabaseJava;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.cavallium.strangedb.database.Database;
|
||||
import it.cavallium.strangedb.EnhancedObject;
|
||||
import it.cavallium.strangedb.database.IDatabaseTools;
|
||||
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
||||
import it.cavallium.strangedb.java.database.IDatabaseTools;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -23,7 +24,7 @@ public class NTestUtils {
|
||||
|
||||
public static class WrappedDb {
|
||||
|
||||
private Database db;
|
||||
private DatabaseJava db;
|
||||
private Path tempDir;
|
||||
private RunnableWithIO r;
|
||||
|
||||
@ -46,8 +47,8 @@ public class NTestUtils {
|
||||
return this;
|
||||
}
|
||||
|
||||
private Database openDatabase() throws IOException {
|
||||
return new Database(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("blocks.dat")), tempDir.resolve(Paths.get("references.dat")));
|
||||
private DatabaseJava openDatabase() throws IOException {
|
||||
return new DatabaseJava(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("blocks.dat")), tempDir.resolve(Paths.get("references.dat")));
|
||||
}
|
||||
|
||||
public void delete() throws IOException {
|
||||
@ -55,7 +56,7 @@ public class NTestUtils {
|
||||
deleteDir(tempDir);
|
||||
}
|
||||
|
||||
public Database get() {
|
||||
public DatabaseJava get() {
|
||||
return db;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user