-
Notifications
You must be signed in to change notification settings - Fork 78
ДЗ№4. Тяпуев Дмитрий. Магистратура. Политех #202
base: main
Are you sure you want to change the base?
Changes from 7 commits
e176c7d
26f5bb7
5a89728
b6e47b3
269b43f
6ecdff5
c162794
ca1356b
6280634
8244af7
e23dcff
d4d384a
43569ac
89533ca
0fca460
ef2c3b0
57e1652
9957684
9e8f1b3
5f758a6
aa7dfa7
30d534a
b750479
0dec53e
2e5a3d4
d614b11
3899bc4
fd682b5
e7e25ca
52230fd
1767600
06bb1b9
028150c
273a787
6b80d94
5603493
c8b8aad
d7da6f4
d759686
e7570ab
e6de16c
35c51db
71ede3a
25a2985
b5487db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package ru.vk.itmo.tyapuevdmitrij; | ||
|
||
public class FilesException extends RuntimeException { | ||
public FilesException(String message, Throwable cause) { | ||
super(message, cause); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,14 +9,14 @@ | |
import java.lang.foreign.MemorySegment; | ||
import java.lang.foreign.ValueLayout; | ||
import java.nio.file.Path; | ||
import java.util.Collections; | ||
import java.util.Comparator; | ||
import java.util.Iterator; | ||
import java.util.concurrent.ConcurrentNavigableMap; | ||
import java.util.concurrent.ConcurrentSkipListMap; | ||
|
||
public class InMemoryDao implements Dao<MemorySegment, Entry<MemorySegment>> { | ||
|
||
private static final Comparator<MemorySegment> memorySegmentComparator = (segment1, segment2) -> { | ||
public class MemorySegmentDao implements Dao<MemorySegment, Entry<MemorySegment>> { | ||
private static final Comparator<MemorySegment> MEMORY_SEGMENT_COMPARATOR = (segment1, segment2) -> { | ||
long offset = segment1.mismatch(segment2); | ||
if (offset == -1) { | ||
return 0; | ||
|
@@ -30,29 +30,29 @@ public class InMemoryDao implements Dao<MemorySegment, Entry<MemorySegment>> { | |
return segment1.get(ValueLayout.JAVA_BYTE, offset) - segment2.get(ValueLayout.JAVA_BYTE, offset); | ||
}; | ||
private final ConcurrentNavigableMap<MemorySegment, Entry<MemorySegment>> memTable = | ||
new ConcurrentSkipListMap<>(memorySegmentComparator); | ||
new ConcurrentSkipListMap<>(MEMORY_SEGMENT_COMPARATOR); | ||
|
||
private final Arena readArena; | ||
private final Path ssTablePath; | ||
private long ssTablesEntryQuantity; | ||
private boolean compacted; | ||
private final Storage storage; | ||
|
||
public InMemoryDao() { | ||
public MemorySegmentDao() { | ||
ssTablePath = null; | ||
readArena = null; | ||
storage = null; | ||
} | ||
|
||
public InMemoryDao(Config config) { | ||
public MemorySegmentDao(Config config) { | ||
ssTablePath = config.basePath(); | ||
readArena = Arena.ofShared(); | ||
storage = new Storage(ssTablePath, readArena); | ||
} | ||
|
||
@Override | ||
public Iterator<Entry<MemorySegment>> get(MemorySegment from, MemorySegment to) { | ||
return storage.range(getMemTableIterator(from, to), from, to, memorySegmentComparator); | ||
return storage.range(getMemTableIterator(from, to), from, to, MEMORY_SEGMENT_COMPARATOR); | ||
} | ||
|
||
private Iterator<Entry<MemorySegment>> getMemTableIterator(MemorySegment from, MemorySegment to) { | ||
|
@@ -71,13 +71,25 @@ private Iterator<Entry<MemorySegment>> getMemTableIterator(MemorySegment from, M | |
@Override | ||
public Entry<MemorySegment> get(MemorySegment key) { | ||
Entry<MemorySegment> value = memTable.get(key); | ||
if (memTable.containsKey(key) && value.value() == null) { | ||
if (value != null && value.value() == null) { | ||
return null; | ||
} | ||
if (value != null || storage.ssTables == null) { | ||
return value; | ||
} | ||
return storage.getSsTableDataByKey(key, memorySegmentComparator); | ||
Iterator<Entry<MemorySegment>> iterator = storage.range(Collections.emptyIterator(), | ||
key, | ||
null, | ||
MEMORY_SEGMENT_COMPARATOR); | ||
|
||
if (!iterator.hasNext()) { | ||
return null; | ||
} | ||
Entry<MemorySegment> next = iterator.next(); | ||
if (MEMORY_SEGMENT_COMPARATOR.compare(next.key(), key) == 0) { | ||
return next; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
|
@@ -91,27 +103,47 @@ public void compact() throws IOException { | |
return; | ||
} | ||
Iterator<Entry<MemorySegment>> dataIterator = get(null, null); | ||
Arena writeArena = Arena.ofConfined(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Лучше создавать под try-with-resources. Иначе, в случае исключения между созданием и закрытием, будет потеря памяти There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Исправил |
||
MemorySegment buffer = NmapBuffer.getWriteBufferToSsTable(getCompactionTableByteSize(), | ||
ssTablePath, | ||
storage.ssTablesQuantity); | ||
storage.ssTablesQuantity, | ||
writeArena, | ||
true); | ||
long bufferByteSize = buffer.byteSize(); | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, bufferByteSize - Long.BYTES, ssTablesEntryQuantity); | ||
long[] offsets = new long[2]; | ||
offsets[1] = bufferByteSize - Long.BYTES - ssTablesEntryQuantity * 2L * Long.BYTES; | ||
long dataOffset = 0; | ||
long indexOffset = bufferByteSize - Long.BYTES - ssTablesEntryQuantity * 2L * Long.BYTES; | ||
while (dataIterator.hasNext()) { | ||
storage.writeEntryAndIndexesToCompactionTable(buffer, dataIterator.next(), offsets); | ||
Entry<MemorySegment> entry = dataIterator.next(); | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, indexOffset, dataOffset); | ||
indexOffset += Long.BYTES; | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, dataOffset, entry.key().byteSize()); | ||
dataOffset += Long.BYTES; | ||
MemorySegment.copy(entry.key(), 0, buffer, dataOffset, entry.key().byteSize()); | ||
dataOffset += entry.key().byteSize(); | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, indexOffset, dataOffset); | ||
indexOffset += Long.BYTES; | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, dataOffset, entry.value().byteSize()); | ||
dataOffset += Long.BYTES; | ||
MemorySegment.copy(entry.value(), 0, buffer, dataOffset, entry.value().byteSize()); | ||
dataOffset += entry.value().byteSize(); | ||
} | ||
StorageHelper.deleteOldSsTables(ssTablePath, storage.ssTablesQuantity); | ||
if (writeArena.scope().isAlive()) { | ||
writeArena.close(); | ||
} | ||
StorageHelper.deleteOldSsTables(ssTablePath); | ||
StorageHelper.renameCompactedSsTable(ssTablePath); | ||
compacted = true; | ||
} | ||
|
||
@Override | ||
public void close() throws IOException { | ||
if (compacted) { | ||
readArena.close(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. думаю, проверка на isAlive должна быть выше всех. Иначе вы не проверяете на isAlive, а просто закрываете арену There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Исправил |
||
return; | ||
} | ||
if (memTable.isEmpty()) { | ||
readArena.close(); | ||
return; | ||
} | ||
if (!readArena.scope().isAlive()) { | ||
|
@@ -135,5 +167,4 @@ private long getCompactionTableByteSize() { | |
ssTablesEntryQuantity = countEntry; | ||
return compactionTableByteSize + countEntry * 4L * Long.BYTES + Long.BYTES; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,10 +33,16 @@ static MemorySegment getReadBufferFromSsTable(Path ssTablePath, Arena readArena) | |
|
||
static MemorySegment getWriteBufferToSsTable(Long writeBytes, | ||
typuichik123 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Path ssTablePath, | ||
int ssTablesQuantity) throws IOException { | ||
int ssTablesQuantity, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Сейчас метод принимает два проблемных аргумента: ssTablesQuantity и compactionFlag. Первый аргумент лишний потому, что не используется в половине кейсов использования. А второй аргумент этому способствует) Как насчет того, чтоб передавать в метод уже готовый Path? Тогда необходимости в тех двух аргументах отпадет, а сам метод будет выполнять единственную задачу - мапинг файла There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. исправил There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Сейчас метод принимает два проблемных аргумента: ssTablesQuantity и compactionFlag. Первый аргумент лишний потому, что не используется в половине кейсов использования. А второй аргумент этому способствует) Как насчет того, чтоб передавать в метод уже готовый Path? Тогда необходимости в тех двух аргументах отпадет, а сам метод будет выполнять единственную задачу - мапинг файла There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. У меня логика построена так, что при создании sstable учитывается их количество. Поэтому надо знать- компактим мы или просто пишем. Изначально у меня для этого было 2 метода, но я решил так оптимизировать. Да и codeclimate не позволял иметь много кода в одном файле, так что пришлось разделять логику по классам. |
||
Arena writeArena, | ||
boolean compactionFlag) throws IOException { | ||
MemorySegment buffer; | ||
Path path = ssTablePath.resolve(Storage.SS_TABLE_FILE_NAME + ssTablesQuantity); | ||
Arena writeArena = Arena.ofConfined(); | ||
Path path; | ||
if (compactionFlag) { | ||
path = ssTablePath.resolve(StorageHelper.COMPACTED_FILE_NAME); | ||
} else { | ||
path = ssTablePath.resolve(StorageHelper.SS_TABLE_FILE_NAME + ssTablesQuantity); | ||
} | ||
try (FileChannel channel = FileChannel.open(path, EnumSet.of(StandardOpenOption.READ, | ||
StandardOpenOption.WRITE, | ||
StandardOpenOption.CREATE, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import ru.vk.itmo.BaseEntry; | ||
import ru.vk.itmo.Entry; | ||
|
||
import java.io.IOException; | ||
import java.lang.foreign.Arena; | ||
import java.lang.foreign.MemorySegment; | ||
|
@@ -14,51 +15,39 @@ | |
import java.util.NoSuchElementException; | ||
|
||
public class Storage { | ||
protected List<MemorySegment> ssTables; | ||
protected static long memTableEntriesSize; | ||
protected static final String SS_TABLE_FILE_NAME = "ssTable"; | ||
private final StorageHelper storageHelper; | ||
protected final int ssTablesQuantity; | ||
protected List<MemorySegment> ssTables; | ||
|
||
public Storage(Path ssTablePath, Arena readArena) { | ||
storageHelper = new StorageHelper(); | ||
ssTablesQuantity = StorageHelper.findSsTablesQuantity(ssTablePath); | ||
ssTables = new ArrayList<>(ssTablesQuantity); | ||
if (ssTablesQuantity != 0) { | ||
for (int i = 0; i < ssTablesQuantity; i++) { | ||
Path path = ssTablePath.resolve(SS_TABLE_FILE_NAME + i); | ||
ssTables.add(NmapBuffer.getReadBufferFromSsTable(path, readArena)); | ||
} | ||
for (int i = 0; i < ssTablesQuantity; i++) { | ||
Path path = ssTablePath.resolve(StorageHelper.SS_TABLE_FILE_NAME + i); | ||
ssTables.add(NmapBuffer.getReadBufferFromSsTable(path, readArena)); | ||
} | ||
} | ||
|
||
public void writeEntryAndIndexesToCompactionTable(MemorySegment buffer, | ||
Entry<MemorySegment> entry, long... offsets) { | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, offsets[1], offsets[0]); | ||
offsets[1] += Long.BYTES; | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, offsets[0], entry.key().byteSize()); | ||
offsets[0] += Long.BYTES; | ||
MemorySegment.copy(entry.key(), 0, buffer, offsets[0], entry.key().byteSize()); | ||
offsets[0] += entry.key().byteSize(); | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, offsets[1], offsets[0]); | ||
offsets[1] += Long.BYTES; | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, offsets[0], entry.value().byteSize()); | ||
offsets[0] += Long.BYTES; | ||
MemorySegment.copy(entry.value(), 0, buffer, offsets[0], entry.value().byteSize()); | ||
offsets[0] += entry.value().byteSize(); | ||
} | ||
|
||
public void save(Iterable<Entry<MemorySegment>> memTableEntries, Path ssTablePath) throws IOException { | ||
MemorySegment buffer = NmapBuffer.getWriteBufferToSsTable(StorageHelper.getSsTableDataByteSize(memTableEntries), | ||
Arena writeArena = Arena.ofConfined(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. try-with-resources There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Исправил |
||
MemorySegment buffer = NmapBuffer.getWriteBufferToSsTable(storageHelper.getSsTableDataByteSize(memTableEntries), | ||
ssTablePath, | ||
ssTablesQuantity); | ||
ssTablesQuantity, | ||
writeArena, | ||
false); | ||
writeMemTableDataToFile(buffer, memTableEntries); | ||
if (writeArena.scope().isAlive()) { | ||
writeArena.close(); | ||
} | ||
} | ||
|
||
private void writeMemTableDataToFile(MemorySegment buffer, Iterable<Entry<MemorySegment>> memTableEntries) { | ||
long offset = 0; | ||
long bufferByteSize = buffer.byteSize(); | ||
long writeIndexPosition = bufferByteSize - memTableEntriesSize * 2L * Long.BYTES - Long.BYTES; | ||
long writeIndexPosition = bufferByteSize - storageHelper.memTableEntriesCount * 2L * Long.BYTES - Long.BYTES; | ||
//write to the end of file size of memTable | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, bufferByteSize - Long.BYTES, memTableEntriesSize); | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, bufferByteSize - Long.BYTES, storageHelper.memTableEntriesCount); | ||
for (Entry<MemorySegment> entry : memTableEntries) { | ||
buffer.set(ValueLayout.JAVA_LONG_UNALIGNED, offset, entry.key().byteSize()); | ||
// write keyByteSizeOffsetPosition to the end of buffer | ||
|
@@ -134,35 +123,6 @@ private MemorySegment getValueByOffset(MemorySegment ssTable, long offset) { | |
return ssTable.asSlice(valueOffset, valueByteSize); | ||
} | ||
|
||
public Entry<MemorySegment> getSsTableDataByKey(MemorySegment key, | ||
Comparator<MemorySegment> memorySegmentComparator) { | ||
for (int i = ssTables.size() - 1; i > -1; i--) { | ||
MemorySegment ssTable = ssTables.get(i); | ||
long memTableSize = ssTable.get(ValueLayout.JAVA_LONG_UNALIGNED, ssTable.byteSize() - Long.BYTES); | ||
long left = 0; | ||
long right = memTableSize - 1L; | ||
long lastKeyIndexOffset = ssTable.byteSize() - 3 * Long.BYTES; | ||
while (left <= right) { | ||
long mid = (right + left) >>> 1; | ||
long midOffset = lastKeyIndexOffset - (memTableSize - 1L) * Long.BYTES * 2L + mid * 2L * Long.BYTES; | ||
MemorySegment readKey = getKeyByOffset(ssTable, midOffset); | ||
int res = memorySegmentComparator.compare(readKey, key); | ||
if (res == 0) { | ||
MemorySegment value = getValueByOffset(ssTable, midOffset + Long.BYTES); | ||
if (value == null) { | ||
return null; | ||
} | ||
return new BaseEntry<>(key, value); | ||
} else if (res > 0) { | ||
right = mid - 1; | ||
} else { | ||
left = mid + 1; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public Iterator<Entry<MemorySegment>> range( | ||
Iterator<Entry<MemorySegment>> firstIterator, | ||
MemorySegment from, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не рассматривается еще один кейс: если количество таблиц == 1, а memTable is empty, то компактить тоже нет смысла
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Исправил