diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServerImpl.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServerImpl.java new file mode 100644 index 000000000..14f577467 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServerImpl.java @@ -0,0 +1,160 @@ +package ru.vk.itmo.test.viktorkorotkikh; + +import one.nio.http.HttpServer; +import one.nio.http.HttpServerConfig; +import one.nio.http.HttpSession; +import one.nio.http.Path; +import one.nio.http.Request; +import one.nio.http.RequestMethod; +import one.nio.http.Response; +import one.nio.net.Socket; +import one.nio.server.AcceptorConfig; +import one.nio.server.RejectedSessionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.vk.itmo.ServiceConfig; +import ru.vk.itmo.dao.BaseEntry; +import ru.vk.itmo.dao.Dao; +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.LSMDaoOutOfMemoryException; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.TooManyFlushesException; +import ru.vk.itmo.test.viktorkorotkikh.http.LSMConstantResponse; +import ru.vk.itmo.test.viktorkorotkikh.http.LSMCustomSession; +import ru.vk.itmo.test.viktorkorotkikh.http.LSMServerResponseWithMemorySegment; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.foreign.MemorySegment; +import java.nio.charset.StandardCharsets; + +import static one.nio.http.Request.METHOD_DELETE; +import static one.nio.http.Request.METHOD_GET; +import static one.nio.http.Request.METHOD_PUT; + +public class LSMServerImpl extends HttpServer { + private static final Logger log = LoggerFactory.getLogger(LSMServerImpl.class); + private final Dao> dao; + + public LSMServerImpl(ServiceConfig serviceConfig, Dao> dao) throws IOException { + super(createServerConfig(serviceConfig)); + this.dao = dao; + } + + private static HttpServerConfig createServerConfig(ServiceConfig serviceConfig) { + HttpServerConfig serverConfig = new HttpServerConfig(); + serverConfig.acceptors = new AcceptorConfig[]{createAcceptorConfig(serviceConfig.selfPort())}; + serverConfig.closeSessions = true; + return serverConfig; + } + + private static AcceptorConfig createAcceptorConfig(int port) { + AcceptorConfig acceptorConfig = new AcceptorConfig(); + acceptorConfig.port = port; + acceptorConfig.reusePort = true; + return acceptorConfig; + } + + @Override + public void handleRequest(Request request, HttpSession session) throws IOException { + try { + super.handleRequest(request, session); + } catch (Exception e) { + log.error("Unexpected error occurred: ", e); + session.sendResponse(LSMConstantResponse.serviceUnavailable(request)); + } + } + + @Path("/v0/entity") + public void handleEntityRequest(Request request, HttpSession session) throws IOException { + // validate id parameter + String id = request.getParameter("id="); + if (id == null || id.isEmpty()) { + session.sendResponse(LSMConstantResponse.badRequest(request)); + return; + } + + Response response = switch (request.getMethod()) { + case METHOD_GET -> handleGetEntity(request, id); + case METHOD_PUT -> handlePutEntity(request, id); + case METHOD_DELETE -> handleDeleteEntity(request, id); + default -> LSMConstantResponse.methodNotAllowed(request); + }; + session.sendResponse(response); + } + + private Response handleGetEntity(final Request request, final String id) { + final Entry entry; + try { + entry = dao.get(fromString(id)); + } catch (UncheckedIOException e) { + // sstable get method throws UncheckedIOException + return LSMConstantResponse.serviceUnavailable(request); + } + if (entry == null || entry.value() == null) { + return LSMConstantResponse.notFound(request); + } + + return new LSMServerResponseWithMemorySegment(Response.OK, entry.value()); + } + + private Response handlePutEntity(final Request request, final String id) { + if (request.getBody() == null) { + return LSMConstantResponse.badRequest(request); + } + + Entry newEntry = new BaseEntry<>( + fromString(id), + MemorySegment.ofArray(request.getBody()) + ); + try { + dao.upsert(newEntry); + } catch (LSMDaoOutOfMemoryException e) { + // when entry is too big to be putted into memtable + return LSMConstantResponse.entityTooLarge(request); + } catch (TooManyFlushesException e) { + // when one memory table is in the process of being flushed, and the second is already full + return LSMConstantResponse.tooManyRequests(request); + } + + return LSMConstantResponse.created(request); + } + + private Response handleDeleteEntity(final Request request, final String id) { + final Entry newEntry = new BaseEntry<>( + fromString(id), + null + ); + try { + dao.upsert(newEntry); + } catch (LSMDaoOutOfMemoryException e) { + // when entry is too big to be putted into memtable + return LSMConstantResponse.entityTooLarge(request); + } catch (TooManyFlushesException e) { + // when one memory table is in the process of being flushed, and the second is already full + return LSMConstantResponse.tooManyRequests(request); + } + + return LSMConstantResponse.accepted(request); + } + + @Path("/v0/compact") + @RequestMethod(value = {METHOD_GET}) + public Response handleCompact(Request request) throws IOException { + dao.compact(); + return LSMConstantResponse.ok(request); + } + + private static MemorySegment fromString(String data) { + return data == null ? null : MemorySegment.ofArray(data.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void handleDefault(Request request, HttpSession session) throws IOException { + session.sendResponse(LSMConstantResponse.badRequest(request)); + } + + @Override + public HttpSession createSession(Socket socket) throws RejectedSessionException { + return new LSMCustomSession(socket, this); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServiceImpl.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServiceImpl.java new file mode 100644 index 000000000..e7fbf3716 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/LSMServiceImpl.java @@ -0,0 +1,107 @@ +package ru.vk.itmo.test.viktorkorotkikh; + +import ru.vk.itmo.Service; +import ru.vk.itmo.ServiceConfig; +import ru.vk.itmo.dao.Config; +import ru.vk.itmo.dao.Dao; +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.ServiceFactory; +import ru.vk.itmo.test.viktorkorotkikh.dao.LSMDaoImpl; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.foreign.MemorySegment; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class LSMServiceImpl implements Service { + private static final long FLUSH_THRESHOLD = 1 << 20; // 1 MB + private final ServiceConfig serviceConfig; + private LSMServerImpl httpServer; + private boolean isRunning; + private Dao> dao; + + public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { + Path tmpDir = Files.createTempDirectory("dao"); + tmpDir.toFile().deleteOnExit(); + + ServiceConfig serviceConfig = new ServiceConfig( + 8080, + "http://localhost:8080", + List.of("http://localhost:8080"), + tmpDir + ); + LSMServiceImpl lsmService = new LSMServiceImpl(serviceConfig); + + lsmService.start().get(); + } + + public LSMServiceImpl(ServiceConfig serviceConfig) throws IOException { + this.serviceConfig = serviceConfig; + } + + private static LSMServerImpl createServer( + ServiceConfig serviceConfig, + Dao> dao + ) throws IOException { + return new LSMServerImpl(serviceConfig, dao); + } + + private static Dao> createLSMDao(Path workingDir) { + Config daoConfig = new Config( + workingDir, + FLUSH_THRESHOLD + ); + return new LSMDaoImpl(daoConfig); + } + + private static void closeLSMDao(Dao> dao) { + if (dao == null) return; + try { + dao.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public synchronized CompletableFuture start() throws IOException { + if (isRunning) return CompletableFuture.completedFuture(null); + dao = createLSMDao(serviceConfig.workingDir()); + + httpServer = createServer(serviceConfig, dao); + httpServer.start(); + + isRunning = true; + return CompletableFuture.completedFuture(null); + } + + @Override + public synchronized CompletableFuture stop() throws IOException { + if (!isRunning) return CompletableFuture.completedFuture(null); + httpServer.stop(); + httpServer = null; + + closeLSMDao(dao); + dao = null; + + isRunning = false; + return CompletableFuture.completedFuture(null); + } + + @ServiceFactory(stage = 1) + public static class LSMServiceFactoryImpl implements ServiceFactory.Factory { + @Override + public Service create(ServiceConfig config) { + try { + return new LSMServiceImpl(config); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/EntriesMetadata.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/EntriesMetadata.java new file mode 100644 index 000000000..899c76c52 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/EntriesMetadata.java @@ -0,0 +1,5 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +public record EntriesMetadata(int count, long entriesDataSize) { + +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMDaoImpl.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMDaoImpl.java new file mode 100644 index 000000000..1438475ab --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMDaoImpl.java @@ -0,0 +1,228 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import ru.vk.itmo.dao.Config; +import ru.vk.itmo.dao.Dao; +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.BackgroundExecutionException; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.CompactionException; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.FlushingException; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.LSMDaoCreationException; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.TooManyFlushesException; +import ru.vk.itmo.test.viktorkorotkikh.dao.sstable.SSTable; +import ru.vk.itmo.test.viktorkorotkikh.dao.sstable.SSTableUtils; + +import java.io.IOException; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class LSMDaoImpl implements Dao> { + + private volatile MemTable memTable; + + private volatile MemTable flushingMemTable; + + private volatile Future flushFuture; + + private volatile Future compactionFuture; + + private volatile List ssTables; + private Arena ssTablesArena; + + private final Config config; + private final ExecutorService bgExecutor = Executors.newSingleThreadExecutor(); + + private final ReadWriteLock upsertLock = new ReentrantReadWriteLock(); + + private final AtomicBoolean closed = new AtomicBoolean(false); + + public LSMDaoImpl(Config config) { + this.config = config; + this.memTable = new MemTable(config.flushThresholdBytes()); + this.flushingMemTable = new MemTable(-1); + try { + this.ssTablesArena = Arena.ofShared(); + this.ssTables = SSTable.load(ssTablesArena, config); + } catch (IOException e) { + ssTablesArena.close(); + throw new LSMDaoCreationException(e); + } + } + + @Override + public Iterator> get(MemorySegment from, MemorySegment to) { + return mergeIterator(from, to); + } + + private MergeIterator.MergeIteratorWithTombstoneFilter mergeIterator(MemorySegment from, MemorySegment to) { + List ssTableIterators = SSTableUtils.ssTableIterators(ssTables, from, to); + return MergeIterator.create( + memTable.iterator(from, to, 0), + flushingMemTable.iterator(from, to, 1), + ssTableIterators + ); + } + + @Override + public Entry get(MemorySegment key) { + Entry fromMemTable = memTable.get(key); + if (fromMemTable != null) { + return fromMemTable.value() == null ? null : fromMemTable; + } + Entry fromFlushingMemTable = flushingMemTable.get(key); + if (fromFlushingMemTable != null) { + return fromFlushingMemTable.value() == null ? null : fromFlushingMemTable; + } + return getFromDisk(key); + } + + private Entry getFromDisk(MemorySegment key) { + for (int i = ssTables.size() - 1; i >= 0; i--) { // reverse order because last sstable has the highest priority + SSTable ssTable = ssTables.get(i); + Entry fromDisk = ssTable.get(key); + if (fromDisk != null) { + return fromDisk.value() == null ? null : fromDisk; + } + } + return null; + } + + @Override + public void upsert(Entry entry) { + upsertLock.readLock().lock(); + try { + if (!memTable.upsert(entry)) { // no overflow + return; + } + } finally { + upsertLock.readLock().unlock(); + } + tryToFlush(false); + } + + @Override + public void compact() throws IOException { + if (SSTable.isCompacted(ssTables)) { + return; + } + + compactionFuture = bgExecutor.submit(this::compactInBackground); + } + + private void compactInBackground() { + try { + SSTable.compact( + MergeIterator.createThroughSSTables( + SSTableUtils.ssTableIterators(ssTables, null, null) + ), + config + ); + ssTables = SSTable.load(ssTablesArena, config); + } catch (IOException e) { + throw new CompactionException(e); + } + } + + @Override + public void flush() throws IOException { + tryToFlush(true); + } + + private void tryToFlush(boolean tolerateToBackgroundFlushing) { + upsertLock.writeLock().lock(); + try { + if (flushingMemTable.isEmpty()) { + prepareFlush(); + } else { + if (tolerateToBackgroundFlushing) { + return; + } else { + throw new TooManyFlushesException(); + } + } + } finally { + upsertLock.writeLock().unlock(); + } + flushFuture = runFlushInBackground(); + } + + private void prepareFlush() { + flushingMemTable = memTable; + memTable = new MemTable(config.flushThresholdBytes()); + } + + private Future runFlushInBackground() { + return bgExecutor.submit(() -> { + try { + flush(flushingMemTable, ssTables.size(), ssTablesArena); + } catch (IOException e) { + throw new FlushingException(e); + } + }); + } + + private void flush( + MemTable memTable, + int fileIndex, + Arena ssTablesArena + ) throws IOException { + if (memTable.isEmpty()) return; + + SSTable.save(memTable, fileIndex, config); + + SSTable flushed = SSTable.loadOne(ssTablesArena, false, config, fileIndex); + final List newSSTables = new ArrayList<>(ssTables.size() + 1); + newSSTables.add(flushed); + newSSTables.addAll(ssTables); + + ssTables = newSSTables; + flushingMemTable = new MemTable(-1); + } + + private void await(Future future) { + try { + future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + throw new BackgroundExecutionException(e); + } + } + + @Override + public void close() throws IOException { + if (closed.getAndSet(true)) { + return; // already closed + } + bgExecutor.shutdown(); + try { + if (flushFuture != null) { + await(flushFuture); + } + if (compactionFuture != null) { + await(compactionFuture); + } + bgExecutor.awaitTermination(1, TimeUnit.MINUTES); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + if (ssTablesArena.scope().isAlive()) { + ssTablesArena.close(); + } + if (!memTable.isEmpty()) { + SSTable.save(memTable, ssTables.size(), config); + } + + memTable = new MemTable(-1); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMPointerIterator.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMPointerIterator.java new file mode 100644 index 000000000..df1479380 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/LSMPointerIterator.java @@ -0,0 +1,96 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import ru.vk.itmo.dao.Entry; + +import java.lang.foreign.MemorySegment; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public abstract class LSMPointerIterator implements Iterator> { + + public abstract int getPriority(); + + protected abstract MemorySegment getPointerKeySrc(); + + protected abstract long getPointerKeySrcOffset(); + + protected abstract long getPointerKeySrcSize(); + + public abstract boolean isPointerOnTombstone(); + + public abstract void shift(); + + public abstract long getPointerSize(); + + public int compareByPointers(LSMPointerIterator otherIterator) { + return MemorySegmentComparator.INSTANCE.compare( + getPointerKeySrc(), + getPointerKeySrcOffset(), + getPointerKeySrcOffset() + getPointerKeySrcSize(), + otherIterator.getPointerKeySrc(), + otherIterator.getPointerKeySrcOffset(), + otherIterator.getPointerKeySrcOffset() + otherIterator.getPointerKeySrcSize() + ); + } + + public int compareByPointersWithPriority(LSMPointerIterator otherIterator) { + int keyComparison = compareByPointers(otherIterator); + if (keyComparison == 0) { + return Integer.compare(otherIterator.getPriority(), getPriority()); + } + return keyComparison; + } + + public static final class Empty extends LSMPointerIterator { + private final int priority; + + public Empty(int priority) { + this.priority = priority; + } + + @Override + public int getPriority() { + return priority; + } + + @Override + protected MemorySegment getPointerKeySrc() { + return null; + } + + @Override + protected long getPointerKeySrcOffset() { + return -1; + } + + @Override + protected long getPointerKeySrcSize() { + return -1; + } + + @Override + public boolean isPointerOnTombstone() { + return false; + } + + @Override + public void shift() { + throw new NoSuchElementException(); + } + + @Override + public long getPointerSize() { + return -1; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Entry next() { + throw new NoSuchElementException(); + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemTable.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemTable.java new file mode 100644 index 000000000..7a0f7e461 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemTable.java @@ -0,0 +1,147 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.exceptions.LSMDaoOutOfMemoryException; + +import java.lang.foreign.MemorySegment; +import java.util.Collection; +import java.util.Iterator; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicLong; + +public class MemTable { + private final NavigableMap> storage; + + private final long flushThresholdBytes; + + private final AtomicLong memTableByteSize = new AtomicLong(); + + public MemTable(long flushThresholdBytes) { + this.flushThresholdBytes = flushThresholdBytes; + this.storage = createNewMemTable(); + } + + private static NavigableMap> createNewMemTable() { + return new ConcurrentSkipListMap<>(MemorySegmentComparator.INSTANCE); + } + + private Iterator> storageIterator(MemorySegment from, MemorySegment to) { + if (from == null && to == null) { + return storage.sequencedValues().iterator(); + } + + if (from == null) { + return storage.headMap(to).sequencedValues().iterator(); + } + + if (to == null) { + return storage.tailMap(from).sequencedValues().iterator(); + } + + return storage.subMap(from, to).sequencedValues().iterator(); + } + + public MemTableIterator iterator(MemorySegment from, MemorySegment to, int priorityReduction) { + return new MemTableIterator(storageIterator(from, to), priorityReduction); + } + + public Entry get(MemorySegment key) { + return storage.get(key); + } + + public Collection> values() { + return storage.values(); + } + + public boolean upsert(Entry entry) { + long newEntrySize = Utils.getEntrySize(entry); + if (memTableByteSize.addAndGet(newEntrySize) - newEntrySize >= flushThresholdBytes) { + memTableByteSize.addAndGet(-newEntrySize); + throw new LSMDaoOutOfMemoryException(); + } + Entry previous = storage.put(entry.key(), entry); + if (previous != null) { + // entry already was in memTable, so we need to subtract size of previous entry + memTableByteSize.addAndGet(-Utils.getEntrySize(previous)); + } + return memTableByteSize.get() >= flushThresholdBytes; + } + + public boolean isEmpty() { + return memTableByteSize.get() == 0L; + } + + public long getByteSize() { + return memTableByteSize.get(); + } + + public static final class MemTableIterator extends LSMPointerIterator { + private final Iterator> iterator; + private Entry current; + + private final int priority; + + private MemTableIterator(Iterator> storageIterator, int priorityReduction) { + this.iterator = storageIterator; + if (iterator.hasNext()) { + current = iterator.next(); + } + this.priority = Integer.MAX_VALUE - priorityReduction; + } + + @Override + public int getPriority() { + return priority; + } + + @Override + protected MemorySegment getPointerKeySrc() { + return current.key(); + } + + @Override + protected long getPointerKeySrcOffset() { + return 0; + } + + @Override + public boolean isPointerOnTombstone() { + return current.value() == null; + } + + @Override + public void shift() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + current = iterator.hasNext() ? iterator.next() : null; + } + + @Override + public long getPointerSize() { + return Utils.getEntrySize(current); + } + + @Override + protected long getPointerKeySrcSize() { + return current.key().byteSize(); + } + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry entry = current; + current = iterator.hasNext() ? iterator.next() : null; + return entry; + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemorySegmentComparator.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemorySegmentComparator.java new file mode 100644 index 000000000..94455e941 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MemorySegmentComparator.java @@ -0,0 +1,64 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.util.Comparator; + +public class MemorySegmentComparator implements Comparator { + + public static final MemorySegmentComparator INSTANCE = new MemorySegmentComparator(); + + @Override + public int compare(MemorySegment o1, MemorySegment o2) { + // range of 0 (inclusive) up to the size (in bytes) of the smaller memory segment (exclusive). + long mismatch = o1.mismatch(o2); + if (mismatch == -1) { // equals + return 0; + } + + if (mismatch == o1.byteSize()) { // o1 is smaller memory segment + return -1; + } + + if (mismatch == o2.byteSize()) { // o2 is smaller memory segment + return 1; + } + + return o1.get(ValueLayout.JAVA_BYTE, mismatch) - o2.get(ValueLayout.JAVA_BYTE, mismatch); + } + + public int compare( + MemorySegment srcSegment, + long srcFromOffset, + long srcToOffset, + MemorySegment dstSegment, + long dstFromOffset, + long dstToOffset + ) { + long mismatch = MemorySegment + .mismatch(srcSegment, srcFromOffset, srcToOffset, dstSegment, dstFromOffset, dstToOffset); + if (mismatch == -1) { // equals + return 0; + } + + if (mismatch == srcToOffset - srcFromOffset) { // keyFromSrcMemorySegment is smaller memory segment + return -1; + } + + if (mismatch == dstSegment.byteSize()) { // keyFromDstMemorySegment is smaller memory segment + return 1; + } + byte o1 = srcSegment.get(ValueLayout.JAVA_BYTE, srcFromOffset + mismatch); + byte o2 = dstSegment.get(ValueLayout.JAVA_BYTE, dstFromOffset + mismatch); + return o1 - o2; + } + + public int compare( + MemorySegment srcSegment, + long srcFromOffset, + long srcToOffset, + MemorySegment dstSegment + ) { + return compare(srcSegment, srcFromOffset, srcToOffset, dstSegment, 0, dstSegment.byteSize()); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MergeIterator.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MergeIterator.java new file mode 100644 index 000000000..af5ad5841 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/MergeIterator.java @@ -0,0 +1,172 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import ru.vk.itmo.dao.Entry; + +import java.lang.foreign.MemorySegment; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; + +public final class MergeIterator implements Iterator> { + private final PriorityQueue lsmPointerIterators; + + public static MergeIteratorWithTombstoneFilter create( + LSMPointerIterator memTableIterator, + LSMPointerIterator flushingMemTableIterator, + List ssTableIterators + ) { + return new MergeIteratorWithTombstoneFilter( + new MergeIterator(memTableIterator, flushingMemTableIterator, ssTableIterators) + ); + } + + public static MergeIteratorWithTombstoneFilter createThroughSSTables( + List ssTableIterators + ) { + return new MergeIteratorWithTombstoneFilter(new MergeIterator(ssTableIterators)); + } + + private MergeIterator( + LSMPointerIterator memTableIterator, + LSMPointerIterator flushingMemTableIterator, + List ssTableIterators + ) { + this.lsmPointerIterators = new PriorityQueue<>( + ssTableIterators.size() + 2, + LSMPointerIterator::compareByPointersWithPriority + ); + if (memTableIterator.hasNext()) { + lsmPointerIterators.add(memTableIterator); + } + if (flushingMemTableIterator.hasNext()) { + lsmPointerIterators.add(flushingMemTableIterator); + } + for (LSMPointerIterator iterator : ssTableIterators) { + if (iterator.hasNext()) { + lsmPointerIterators.add(iterator); + } + } + } + + private MergeIterator(List ssTableIterators) { + this.lsmPointerIterators = new PriorityQueue<>( + ssTableIterators.size(), + LSMPointerIterator::compareByPointersWithPriority + ); + for (LSMPointerIterator iterator : ssTableIterators) { + if (iterator.hasNext()) { + lsmPointerIterators.add(iterator); + } + } + } + + private boolean isOnTombstone() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return lsmPointerIterators.peek().isPointerOnTombstone(); + } + + @Override + public boolean hasNext() { + return !lsmPointerIterators.isEmpty(); + } + + private LSMPointerIterator shiftIterators() { + LSMPointerIterator lsmPointerIterator = lsmPointerIterators.remove(); + LSMPointerIterator nextIterator; + while ((nextIterator = lsmPointerIterators.peek()) != null) { + int keyComparison = lsmPointerIterator.compareByPointers(nextIterator); + if (keyComparison != 0) { + break; + } + lsmPointerIterators.remove(); + nextIterator.shift(); + if (nextIterator.hasNext()) { + lsmPointerIterators.add(nextIterator); + } + } + return lsmPointerIterator; + } + + private void shift() { + LSMPointerIterator lsmPointerIterator = shiftIterators(); + lsmPointerIterator.shift(); + if (lsmPointerIterator.hasNext()) { + lsmPointerIterators.add(lsmPointerIterator); + } + } + + private long getPointerSizeAndShift() { + LSMPointerIterator lsmPointerIterator = shiftIterators(); + long pointerSize = lsmPointerIterator.getPointerSize(); + lsmPointerIterator.shift(); + if (lsmPointerIterator.hasNext()) { + lsmPointerIterators.add(lsmPointerIterator); + } + return pointerSize; + } + + @Override + public Entry next() { + LSMPointerIterator lsmPointerIterator = shiftIterators(); + Entry next = lsmPointerIterator.next(); + if (lsmPointerIterator.hasNext()) { + lsmPointerIterators.add(lsmPointerIterator); + } + return next; + } + + public static final class MergeIteratorWithTombstoneFilter implements Iterator> { + + private final MergeIterator mergeIterator; + private boolean haveNext; + + private MergeIteratorWithTombstoneFilter(MergeIterator mergeIterator) { + this.mergeIterator = mergeIterator; + } + + @Override + public boolean hasNext() { + if (haveNext) { + return true; + } + + while (mergeIterator.hasNext()) { + if (!mergeIterator.isOnTombstone()) { + haveNext = true; + return true; + } + mergeIterator.shift(); + } + + return false; + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry next = mergeIterator.next(); + haveNext = false; + return next; + } + + public long getPointerSizeAndShift() { + haveNext = false; + return mergeIterator.getPointerSizeAndShift(); + } + + public EntriesMetadata countEntities() { + int count = 0; + long entriesSize = 0; + while (hasNext()) { + entriesSize += getPointerSizeAndShift(); + count++; + } + return new EntriesMetadata(count, entriesSize); + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/Utils.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/Utils.java new file mode 100644 index 000000000..20e0bd983 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/Utils.java @@ -0,0 +1,18 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao; + +import ru.vk.itmo.dao.Entry; + +import java.lang.foreign.MemorySegment; + +public final class Utils { + + private Utils() { + } + + public static long getEntrySize(Entry entry) { + if (entry.value() == null) { + return Long.BYTES + entry.key().byteSize() + Long.BYTES; + } + return Long.BYTES + entry.key().byteSize() + Long.BYTES + entry.value().byteSize(); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/BackgroundExecutionException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/BackgroundExecutionException.java new file mode 100644 index 000000000..def7b30ee --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/BackgroundExecutionException.java @@ -0,0 +1,7 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class BackgroundExecutionException extends RuntimeException { + public BackgroundExecutionException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/CompactionException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/CompactionException.java new file mode 100644 index 000000000..36cb70206 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/CompactionException.java @@ -0,0 +1,7 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class CompactionException extends RuntimeException { + public CompactionException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/FlushingException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/FlushingException.java new file mode 100644 index 000000000..5c2b2d20e --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/FlushingException.java @@ -0,0 +1,7 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class FlushingException extends RuntimeException { + public FlushingException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoCreationException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoCreationException.java new file mode 100644 index 000000000..6f2b2ea1b --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoCreationException.java @@ -0,0 +1,7 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class LSMDaoCreationException extends RuntimeException { + public LSMDaoCreationException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoOutOfMemoryException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoOutOfMemoryException.java new file mode 100644 index 000000000..6e78ddf82 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/LSMDaoOutOfMemoryException.java @@ -0,0 +1,7 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class LSMDaoOutOfMemoryException extends RuntimeException { + public LSMDaoOutOfMemoryException() { + super("LSMDao memory tables is full. Please wait for background flushing to complete."); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/TooManyFlushesException.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/TooManyFlushesException.java new file mode 100644 index 000000000..b4ca6b50c --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/exceptions/TooManyFlushesException.java @@ -0,0 +1,4 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.exceptions; + +public class TooManyFlushesException extends RuntimeException { +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/ByteArraySegment.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/ByteArraySegment.java new file mode 100644 index 000000000..a7cc81f38 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/ByteArraySegment.java @@ -0,0 +1,65 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.io; + +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.nio.ByteBuffer; + +/** + * Growable buffer with {@link ByteBuffer} and {@link MemorySegment} interface. + * + * @author incubos + */ +public final class ByteArraySegment { + private byte[] array; + private final boolean resizable; + private MemorySegment segment; + + public ByteArraySegment(final int capacity) { + this(capacity, true); + } + + public ByteArraySegment(final int capacity, final boolean resizable) { + this.array = new byte[capacity]; + this.resizable = resizable; + this.segment = MemorySegment.ofArray(array); + } + + public void withArray(final ArrayConsumer consumer) throws IOException { + consumer.process(array); + } + + public T withArrayReturn(final ArrayProcessor consumer) throws IOException { + return consumer.process(array); + } + + public MemorySegment segment() { + return segment; + } + + public void ensureCapacity(final long size) { + if (!resizable) { + throw new IllegalStateException("ByteArraySegment is not resizable"); + } + if (size > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Too big!"); + } + + final int capacity = (int) size; + if (array.length >= capacity) { + return; + } + + // Grow to the nearest bigger power of 2 + final int newSize = Integer.highestOneBit(capacity) << 1; + array = new byte[newSize]; + segment = MemorySegment.ofArray(array); + } + + public interface ArrayConsumer { + void process(byte[] array) throws IOException; + } + + public interface ArrayProcessor { + T process(byte[] array) throws IOException; + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/AbstractSSTableReader.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/AbstractSSTableReader.java new file mode 100644 index 000000000..7ac13a79d --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/AbstractSSTableReader.java @@ -0,0 +1,89 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.io.read; + +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.LSMPointerIterator; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +/** + * Base class for implementing the sstable reader. + * + * @author vitekkor + * @see BaseSSTableReader + */ +public abstract class AbstractSSTableReader { + protected final MemorySegment mappedSSTable; + protected final MemorySegment mappedIndexFile; + protected final int index; + + protected AbstractSSTableReader( + MemorySegment mappedSSTable, + MemorySegment mappedIndexFile, + int index + ) { + this.mappedSSTable = mappedSSTable; + this.mappedIndexFile = mappedIndexFile; + this.index = index; + } + + /** + * Returns entry by key. + * @param key entry`s key + * @return entry + * @throws IOException if an I/O error occurs. + */ + public Entry get(MemorySegment key) throws IOException { + try { + long entryOffset = getEntryOffset(key, SearchOption.EQ); + if (entryOffset == -1) { + return null; + } + return getByIndex(entryOffset); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Returns ordered iterator of entries with keys between from (inclusive) and to (exclusive). + * @param from lower bound of range (inclusive) + * @param to upper bound of range (exclusive) + * @return entries [from;to) + * @throws IOException if an I/O error occurs. + */ + public abstract LSMPointerIterator iterator(MemorySegment from, MemorySegment to) throws IOException; + + /** + * Returns entry by index. + * @param index entry`s index from index file. + * @return entry + * @throws IOException if an I/O error occurs. + */ + protected abstract Entry getByIndex(long index) throws IOException; + + /** + * Returns entry's index by key. + * @param key entry`s key + * @param searchOption Entity search parameter. {@link SearchOption#EQ} find equal by key, + * {@link SearchOption#GTE} find greater than or equal to, + * {@link SearchOption#LT} strictly less than + * @return offset (index) + * @throws IOException if an I/O error occurs. + */ + protected abstract long getEntryOffset(MemorySegment key, SearchOption searchOption) throws IOException; + + /** + * SSTable has no tombstones. + * @return false if sstable has tombstones. + */ + public boolean hasNoTombstones() { + return mappedIndexFile.get(ValueLayout.JAVA_BOOLEAN, 0); + } + + public enum SearchOption { + EQ, GTE, LT + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/BaseSSTableReader.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/BaseSSTableReader.java new file mode 100644 index 000000000..5c83955f4 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/read/BaseSSTableReader.java @@ -0,0 +1,204 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.io.read; + +import ru.vk.itmo.dao.BaseEntry; +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.LSMPointerIterator; +import ru.vk.itmo.test.viktorkorotkikh.dao.MemorySegmentComparator; +import ru.vk.itmo.test.viktorkorotkikh.dao.Utils; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.util.NoSuchElementException; + +/** + * SSTable reader without compression. + * + * @author vitekkor + */ +public class BaseSSTableReader extends AbstractSSTableReader { + private static final long METADATA_SIZE = Long.BYTES + 1L; + + private static final long ENTRY_METADATA_SIZE = Long.BYTES; + + public BaseSSTableReader( + MemorySegment mappedSSTable, + MemorySegment mappedIndexFile, + int index + ) { + super(mappedSSTable, mappedIndexFile, index); + } + + private long getMinKeySizeOffset() { + return mappedIndexFile.get(ValueLayout.JAVA_LONG_UNALIGNED, METADATA_SIZE); + } + + private long getMaxKeySizeOffset() { + long entriesSize = getEntriesSize(); + return mappedIndexFile.get( + ValueLayout.JAVA_LONG_UNALIGNED, + METADATA_SIZE + (entriesSize - 1) * ENTRY_METADATA_SIZE + ); + } + + @Override + public LSMPointerIterator iterator(MemorySegment from, MemorySegment to) { + long fromPosition = getMinKeySizeOffset(); + long toPosition = getMaxKeySizeOffset(); + if (from != null) { + fromPosition = getEntryOffset(from, SearchOption.GTE); + if (fromPosition == -1) { + return new LSMPointerIterator.Empty(index); + } + } + if (to != null) { + toPosition = getEntryOffset(to, SearchOption.LT); + if (toPosition == -1) { + return new LSMPointerIterator.Empty(index); + } + } + + return new BaseSSTableIterator(fromPosition, toPosition); + } + + @Override + protected Entry getByIndex(long index) { + long keySize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, index); + MemorySegment savedKey = mappedSSTable.asSlice(index + Long.BYTES, keySize); + + long valueOffset = index + Long.BYTES + keySize; + long valueSize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, valueOffset); + if (valueSize == -1) { + return new BaseEntry<>(savedKey, null); + } + return new BaseEntry<>(savedKey, mappedSSTable.asSlice(valueOffset + Long.BYTES, valueSize)); + } + + private long getEntriesSize() { + return mappedIndexFile.get(ValueLayout.JAVA_LONG_UNALIGNED, 1); + } + + @Override + protected long getEntryOffset(MemorySegment key, SearchOption searchOption) { + // binary search + long entriesSize = getEntriesSize(); + long left = 0; + long right = entriesSize - 1; + while (left <= right) { + long mid = (right + left) / 2; + long keySizeOffset = mappedIndexFile.get( + ValueLayout.JAVA_LONG_UNALIGNED, + METADATA_SIZE + mid * ENTRY_METADATA_SIZE + ); + long keySize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, keySizeOffset); + long keyOffset = keySizeOffset + Long.BYTES; + int keyComparison = MemorySegmentComparator.INSTANCE.compare( + mappedSSTable, keyOffset, + keyOffset + keySize, + key + ); + if (keyComparison < 0) { + left = mid + 1; + } else if (keyComparison > 0) { + right = mid - 1; + } else { + return switch (searchOption) { + case EQ, GTE -> keySizeOffset; + case LT -> keySizeOffset - METADATA_SIZE; + }; + } + } + + return switch (searchOption) { + case EQ -> -1; + case GTE -> { + if (left == entriesSize) { + yield -1; + } else { + yield mappedIndexFile.get( + ValueLayout.JAVA_LONG_UNALIGNED, + METADATA_SIZE + left * ENTRY_METADATA_SIZE + ); + } + } + case LT -> { + if (right == -1) { + yield -1; + } else { + yield mappedIndexFile.get( + ValueLayout.JAVA_LONG_UNALIGNED, + METADATA_SIZE + right * ENTRY_METADATA_SIZE + ); + } + } + }; + } + + public final class BaseSSTableIterator extends LSMPointerIterator { + private long fromPosition; + private final long toPosition; + + private BaseSSTableIterator(long fromPosition, long toPosition) { + this.fromPosition = fromPosition; + this.toPosition = toPosition; + } + + @Override + public int getPriority() { + return index; + } + + @Override + public MemorySegment getPointerKeySrc() { + return mappedSSTable; + } + + @Override + public long getPointerKeySrcOffset() { + return fromPosition + Long.BYTES; + } + + @Override + public boolean isPointerOnTombstone() { + long keySize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, fromPosition); + long valueOffset = fromPosition + Long.BYTES + keySize; + long valueSize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, valueOffset); + return valueSize == -1; + } + + @Override + public void shift() { + fromPosition += getPointerSize(); + } + + @Override + public long getPointerSize() { + long keySize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, fromPosition); + long valueOffset = fromPosition + Long.BYTES + keySize; + long valueSize = mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, valueOffset); + if (valueSize == -1) { + return Long.BYTES + keySize + Long.BYTES; + } + return Long.BYTES + keySize + Long.BYTES + valueSize; + } + + @Override + public long getPointerKeySrcSize() { + return mappedSSTable.get(ValueLayout.JAVA_LONG_UNALIGNED, fromPosition); + } + + @Override + public boolean hasNext() { + return fromPosition <= toPosition; + } + + @Override + public Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Entry entry = getByIndex(fromPosition); + fromPosition += Utils.getEntrySize(entry); + return entry; + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/AbstractSSTableWriter.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/AbstractSSTableWriter.java new file mode 100644 index 000000000..22da0b6aa --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/AbstractSSTableWriter.java @@ -0,0 +1,175 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.io.write; + +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.io.ByteArraySegment; +import ru.vk.itmo.test.viktorkorotkikh.dao.sstable.SSTableUtils; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.Iterator; + +/** + * Base class for implementing the sstable writer. + * + * @author vitekkor + * @see BaseSSTableWriter + */ +public abstract class AbstractSSTableWriter { + private static final int BUFFER_SIZE = 64 * 1024; + protected final ByteArraySegment longBuffer = new ByteArraySegment(Long.BYTES); + protected final ByteArraySegment blobBuffer; + + private static final long INDEX_METADATA_SIZE = Long.BYTES + 1L; // entries size + hasNoTombstones flag + + protected AbstractSSTableWriter() { + blobBuffer = new ByteArraySegment(512); + } + + /** + * Writes entries to SSTable files including data, index, and compression information. + * + * @param isCompacted A boolean flag indicating whether the SSTable is compacted. + * @param entries Iterator over the entries to be written. + * @param baseDir The base directory for storing SSTable files. + * @param fileIndex The index of the SSTable file. + * @throws IOException if an I/O error occurs during writing. + */ + public void write( + boolean isCompacted, + Iterator> entries, + final Path baseDir, + final int fileIndex + ) throws IOException { + // Write to temporary files + final Path tempIndexName = SSTableUtils.tempIndexName(isCompacted, baseDir, fileIndex); + final Path tempDataName = SSTableUtils.tempDataName(isCompacted, baseDir, fileIndex); + + // Delete temporary files to eliminate tails + Files.deleteIfExists(tempIndexName); + Files.deleteIfExists(tempDataName); + + // Iterate in a single pass! + // Will write through FileChannel despite extra memory copying and + // no buffering (which may be implemented later). + // Looking forward to MemorySegment facilities in FileChannel! + try ( + OutputStream index = + new BufferedOutputStream( + new FileOutputStream( + tempIndexName.toFile()), + BUFFER_SIZE); + OutputStream data = + new BufferedOutputStream( + new FileOutputStream( + tempDataName.toFile()), + BUFFER_SIZE)) { + index.write(new byte[(int) INDEX_METADATA_SIZE]); // write 0, fill in the data later + + int entriesSize = 0; + boolean hasNoTombstones = true; + + while (entries.hasNext()) { + // Then write the entry + final Entry entry = entries.next(); + hasNoTombstones &= entry.value() != null; + writeEntry(entry, data, index); + entriesSize++; + } + + index.flush(); + + // map the index and compression info files for updating metadata + try (Arena arena = Arena.ofConfined(); + FileChannel indexFileChannel = FileChannel.open( + tempIndexName, + StandardOpenOption.READ, + StandardOpenOption.WRITE + ) + ) { + MemorySegment mappedIndexFile = indexFileChannel.map( + FileChannel.MapMode.READ_WRITE, + 0L, + INDEX_METADATA_SIZE, + arena + ); + // write final index information and compression info metadata. + writeIndexInfo(mappedIndexFile, entriesSize, hasNoTombstones); + // force changes to the files to be written to the storage device. + mappedIndexFile.force(); + } + } + + renameTmpFiles(isCompacted, baseDir, fileIndex, tempIndexName, tempDataName); + } + + /** + * Rename the temporary files to their final names, making the SSTable available for use. + * + * @param isCompacted A boolean flag indicating whether the SSTable is compacted. + * @param baseDir The base directory for storing SSTable files. + * @param fileIndex The index of the SSTable file. + * @param tempIndexName A temporary index file path. + * @param tempDataName A temporary data file path. + * @throws IOException if an I/O error occurs during writing. + */ + private static void renameTmpFiles( + boolean isCompacted, + Path baseDir, + int fileIndex, + Path tempIndexName, + Path tempDataName + ) throws IOException { + // Publish files atomically + // FIRST index, LAST data + final Path indexName = SSTableUtils.indexName(isCompacted, baseDir, fileIndex); + Files.move( + tempIndexName, + indexName, + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING + ); + final Path dataName = SSTableUtils.dataName(isCompacted, baseDir, fileIndex); + Files.move( + tempDataName, + dataName, + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING + ); + } + + /** + * Write final index information and compression info metadata. + * + * @param mappedIndexFile A mapped index file. + * @param entriesSize Count of entries. + * @param hasNoTombstones hasNoTombstones flag + */ + protected abstract void writeIndexInfo( + MemorySegment mappedIndexFile, + int entriesSize, + boolean hasNoTombstones + ); + + /** + * Writes entry into os stream. Also writes additional information into compressionInfoStream and indexStream. + * + * @param entry Entry to write. + * @param os Data output stream. + * @param indexStream Index output stream. + * @throws IOException if an I/O error occurs during writing. + */ + protected abstract void writeEntry( + final Entry entry, + final OutputStream os, + final OutputStream indexStream + ) throws IOException; +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/BaseSSTableWriter.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/BaseSSTableWriter.java new file mode 100644 index 000000000..d1b3bb0eb --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/io/write/BaseSSTableWriter.java @@ -0,0 +1,75 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.io.write; + +import ru.vk.itmo.dao.Entry; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +/** + * Writer without compression. Instead of blocks, normal data is written: key1Size|key1|value1Size|value1 + * + * @author vitekkor + */ +public final class BaseSSTableWriter extends AbstractSSTableWriter { + private long entryOffset; + + @Override + protected void writeIndexInfo( + MemorySegment mappedIndexFile, + int entriesSize, + boolean hasNoTombstones + ) { + mappedIndexFile.set(ValueLayout.JAVA_BOOLEAN, 0, hasNoTombstones); + mappedIndexFile.set(ValueLayout.JAVA_LONG_UNALIGNED, 1, entriesSize); + } + + @Override + protected void writeEntry( + final Entry entry, + final OutputStream os, + final OutputStream indexStream + ) throws IOException { + final MemorySegment key = entry.key(); + final MemorySegment value = entry.value(); + long result = 0L; + + // Key size + writeLong(key.byteSize(), os); + result += Long.BYTES; + + // Key + writeSegment(key, os); + result += key.byteSize(); + + // Value size and possibly value + if (value == null) { + // Tombstone + writeLong(-1, os); + result += Long.BYTES; + } else { + // Value length + writeLong(value.byteSize(), os); + result += Long.BYTES; + + // Value + writeSegment(value, os); + result += value.byteSize(); + } + writeLong(entryOffset, indexStream); + entryOffset += result; + } + + private void writeLong(final long value, final OutputStream os) throws IOException { + longBuffer.segment().set(ValueLayout.JAVA_LONG_UNALIGNED, 0, value); + longBuffer.withArray(os::write); + } + + private void writeSegment(final MemorySegment value, final OutputStream os) throws IOException { + final long size = value.byteSize(); + blobBuffer.ensureCapacity(size); + MemorySegment.copy(value, 0L, blobBuffer.segment(), 0L, size); + blobBuffer.withArray(array -> os.write(array, 0, (int) size)); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTable.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTable.java new file mode 100644 index 000000000..9eb67dba5 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTable.java @@ -0,0 +1,239 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.sstable; + +import ru.vk.itmo.dao.Config; +import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.viktorkorotkikh.dao.LSMPointerIterator; +import ru.vk.itmo.test.viktorkorotkikh.dao.MemTable; +import ru.vk.itmo.test.viktorkorotkikh.dao.MergeIterator; +import ru.vk.itmo.test.viktorkorotkikh.dao.io.read.AbstractSSTableReader; +import ru.vk.itmo.test.viktorkorotkikh.dao.io.read.BaseSSTableReader; +import ru.vk.itmo.test.viktorkorotkikh.dao.io.write.AbstractSSTableWriter; +import ru.vk.itmo.test.viktorkorotkikh.dao.io.write.BaseSSTableWriter; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public final class SSTable { + + private final boolean hasNoTombstones; + private final AbstractSSTableReader reader; + + private SSTable(AbstractSSTableReader reader) { + this.hasNoTombstones = reader.hasNoTombstones(); + this.reader = reader; + } + + public static List load(Arena arena, Config config) throws IOException { + Path basePath = config.basePath(); + if (checkIfCompactedExists(config)) { + finalizeCompaction(basePath); + } + + Path indexTmp = basePath.resolve(SSTableUtils.INDEX_FILE_NAME + SSTableUtils.TMP_FILE_EXTENSION); + Path indexFile = basePath.resolve(SSTableUtils.INDEX_FILE_NAME); + + if (!Files.exists(indexFile)) { + if (Files.exists(indexTmp)) { + Files.move(indexTmp, indexFile, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } else { + if (!Files.exists(basePath)) { + Files.createDirectory(basePath); + } + Files.createFile(indexFile); + } + } + + List existedFiles = Files.readAllLines(indexFile, StandardCharsets.UTF_8); + List ssTables = new ArrayList<>(existedFiles.size()); + for (int i = 0; i < existedFiles.size(); i++) { + ssTables.add(loadOne(arena, existedFiles.get(i).startsWith(SSTableUtils.COMPACTED_PREFIX), config, i)); + } + + return ssTables; + } + + public static SSTable loadOne(Arena arena, boolean isCompacted, Config config, int index) throws IOException { + try ( + FileChannel ssTableFileChannel = FileChannel.open( + SSTableUtils.dataName(isCompacted, config.basePath(), index), + StandardOpenOption.READ + ); + FileChannel indexFileChannel = FileChannel.open( + SSTableUtils.indexName(isCompacted, config.basePath(), index), + StandardOpenOption.READ + ) + ) { + MemorySegment mappedSSTable = ssTableFileChannel.map( + FileChannel.MapMode.READ_ONLY, + 0, + ssTableFileChannel.size(), + arena + ); + MemorySegment mappedIndexFile = indexFileChannel.map( + FileChannel.MapMode.READ_ONLY, + 0, + indexFileChannel.size(), + arena + ); + AbstractSSTableReader reader = new BaseSSTableReader( + mappedSSTable, + mappedIndexFile, + index + ); + return new SSTable(reader); + } + } + + public static boolean isCompacted(List ssTables) { + return ssTables.isEmpty() || (ssTables.size() == 1 && ssTables.getFirst().hasNoTombstones); + } + + public LSMPointerIterator iterator(MemorySegment from, MemorySegment to) throws IOException { + return reader.iterator(from, to); + } + + public static void save(MemTable memTable, int fileIndex, Config config) throws IOException { + final Path indexTmp = config.basePath() + .resolve(SSTableUtils.INDEX_FILE_NAME + SSTableUtils.TMP_FILE_EXTENSION); + final Path indexFile = config.basePath() + .resolve(SSTableUtils.INDEX_FILE_NAME); + + try { + Files.createFile(indexFile); + } catch (FileAlreadyExistsException ignored) { + // it is ok, actually it is normal state + } + + AbstractSSTableWriter writer = getWriter(); + writer.write(false, memTable.values().iterator(), config.basePath(), fileIndex); + + String newFileName = SSTableUtils.dataName(false, config.basePath(), fileIndex) + .getFileName().toString(); + + List existedFiles = Files.readAllLines(indexFile, StandardCharsets.UTF_8); + List list = new ArrayList<>(existedFiles.size() + 1); + list.addAll(existedFiles); + list.add(newFileName); + Files.write( + indexTmp, + list, + StandardOpenOption.WRITE, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ); + + Files.deleteIfExists(indexFile); + + Files.move(indexTmp, indexFile, StandardCopyOption.ATOMIC_MOVE); + } + + private static AbstractSSTableWriter getWriter() { + return new BaseSSTableWriter(); + } + + public static void compact( + MergeIterator.MergeIteratorWithTombstoneFilter data, + Config config + ) throws IOException { + AbstractSSTableWriter writer = getWriter(); + writer.write(true, data, config.basePath(), 0); + finalizeCompaction(config.basePath()); + } + + private static Path getCompactedFilePath(Path basePath) { + return SSTableUtils.dataName(true, basePath, 0); + } + + private static boolean checkIfCompactedExists(Config config) { + Path compacted = getCompactedFilePath(config.basePath()); + if (!Files.exists(compacted)) { + return false; + } + try (Arena arena = Arena.ofConfined()) { + return !loadOne(arena, true, config, 0).hasNoTombstones; + } catch (IOException ignored) { + return false; + } + } + + private static void finalizeCompaction(Path storagePath) throws IOException { + try (Stream stream = + Files.find( + storagePath, + 1, + (path, ignored) -> { + String fileName = path.getFileName().toString(); + return fileName.startsWith(SSTableUtils.FILE_NAME) + && ( + fileName.endsWith(SSTableUtils.FILE_EXTENSION) + || fileName.endsWith(SSTableUtils.SSTABLE_INDEX_EXTENSION) + || fileName.endsWith(SSTableUtils.COMPRESSION_INFO_EXTENSION) + ); + })) { + stream.forEach(p -> { + try { + Files.delete(p); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + Path indexTmp = storagePath.resolve(SSTableUtils.INDEX_FILE_NAME + SSTableUtils.TMP_FILE_EXTENSION); + Path indexFile = storagePath.resolve(SSTableUtils.INDEX_FILE_NAME); + + Files.deleteIfExists(indexFile); + Files.deleteIfExists(indexTmp); + + Path compactionFile = getCompactedFilePath(storagePath); + boolean noData = Files.size(compactionFile) == 0; + String newFile = SSTableUtils.dataName(false, storagePath, 0).getFileName().toString(); + + Files.write( + indexTmp, + noData ? Collections.emptyList() : Collections.singleton(newFile), + StandardOpenOption.WRITE, + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ); + + Files.move(indexTmp, indexFile, StandardCopyOption.ATOMIC_MOVE); + Path sstableIndexFile = SSTableUtils.indexName(true, storagePath, 0); + if (noData) { + Files.delete(compactionFile); + Files.delete(sstableIndexFile); + } else { + Files.move( + compactionFile, + storagePath.resolve(newFile), + StandardCopyOption.ATOMIC_MOVE + ); + Files.move( + sstableIndexFile, + SSTableUtils.indexName(false, storagePath, 0), + StandardCopyOption.ATOMIC_MOVE + ); + } + } + + public Entry get(MemorySegment key) { + try { + return reader.get(key); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTableUtils.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTableUtils.java new file mode 100644 index 000000000..5947d7400 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/dao/sstable/SSTableUtils.java @@ -0,0 +1,86 @@ +package ru.vk.itmo.test.viktorkorotkikh.dao.sstable; + +import ru.vk.itmo.test.viktorkorotkikh.dao.LSMPointerIterator; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.foreign.MemorySegment; +import java.nio.file.Path; +import java.util.List; + +public final class SSTableUtils { + static final String FILE_NAME = "sstable"; + + static final String INDEX_FILE_NAME = "index.idx"; + + static final String FILE_EXTENSION = ".db"; + + static final String SSTABLE_INDEX_EXTENSION = ".index"; + + static final String COMPACTED_PREFIX = "_compacted_"; + + static final String COMPRESSION_INFO_EXTENSION = ".compressionInfo"; + + static final String TMP_FILE_EXTENSION = ".tmp"; + + private SSTableUtils() { + } + + public static Path indexName( + final boolean isCompacted, + final Path baseDir, + final int fileIndex + ) { + return isCompacted + ? baseDir.resolve(COMPACTED_PREFIX + FILE_NAME + fileIndex + SSTABLE_INDEX_EXTENSION) + : baseDir.resolve(FILE_NAME + fileIndex + SSTABLE_INDEX_EXTENSION); + } + + public static Path dataName( + final boolean isCompacted, + final Path baseDir, + final int fileIndex + ) { + return isCompacted + ? baseDir.resolve(COMPACTED_PREFIX + FILE_NAME + fileIndex + FILE_EXTENSION) + : baseDir.resolve(FILE_NAME + fileIndex + FILE_EXTENSION); + } + + public static Path tempIndexName( + final boolean isCompacted, + final Path baseDir, + final int fileIndex + ) { + if (isCompacted) { + return baseDir.resolve( + COMPACTED_PREFIX + FILE_NAME + fileIndex + SSTABLE_INDEX_EXTENSION + TMP_FILE_EXTENSION + ); + } else { + return baseDir.resolve(FILE_NAME + fileIndex + SSTABLE_INDEX_EXTENSION + TMP_FILE_EXTENSION); + } + } + + public static Path tempDataName( + final boolean isCompacted, + final Path baseDir, + final int fileIndex + ) { + return isCompacted + ? baseDir.resolve(COMPACTED_PREFIX + FILE_NAME + fileIndex + FILE_EXTENSION + TMP_FILE_EXTENSION) + : baseDir.resolve(FILE_NAME + fileIndex + FILE_EXTENSION + TMP_FILE_EXTENSION); + } + + public static List ssTableIterators( + List ssTables, + MemorySegment from, + MemorySegment to + ) { + return ssTables.stream().map(ssTable -> { + try { + return ssTable.iterator(from, to); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).toList(); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMConstantResponse.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMConstantResponse.java new file mode 100644 index 000000000..ebccd9f3c --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMConstantResponse.java @@ -0,0 +1,102 @@ +package ru.vk.itmo.test.viktorkorotkikh.http; + +import one.nio.http.Request; +import one.nio.http.Response; + +public final class LSMConstantResponse { + private static final String CONNECTION_CLOSE_HEADER = "Connection: close"; + public static final String TOO_MANY_REQUESTS = "429 Too Many Requests"; + public static final Response BAD_REQUEST_CLOSE = new Response(Response.BAD_REQUEST, Response.EMPTY); + public static final Response CREATED_CLOSE = new Response(Response.CREATED, Response.EMPTY); + public static final Response ACCEPTED_CLOSE = new Response(Response.ACCEPTED, Response.EMPTY); + public static final Response NOT_FOUND_CLOSE = new Response(Response.NOT_FOUND, Response.EMPTY); + public static final Response METHOD_NOT_ALLOWED_CLOSE = new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY); + public static final Response OK_CLOSE = new Response(Response.OK, Response.EMPTY); + public static final Response TOO_MANY_REQUESTS_CLOSE = new Response(TOO_MANY_REQUESTS, Response.EMPTY); + public static final Response ENTITY_TOO_LARGE_CLOSE = + new Response(Response.REQUEST_ENTITY_TOO_LARGE, Response.EMPTY); + public static final Response SERVICE_UNAVAILABLE_CLOSE = + new Response(Response.SERVICE_UNAVAILABLE, Response.EMPTY); + + private static final String CONNECTION_KEEP_ALIVE_HEADER = "Connection: Keep-Alive"; + public static final Response BAD_REQUEST_KEEP_ALIVE = new Response(Response.BAD_REQUEST, Response.EMPTY); + public static final Response CREATED_KEEP_ALIVE = new Response(Response.CREATED, Response.EMPTY); + public static final Response ACCEPTED_KEEP_ALIVE = new Response(Response.ACCEPTED, Response.EMPTY); + public static final Response NOT_FOUND_KEEP_ALIVE = new Response(Response.NOT_FOUND, Response.EMPTY); + public static final Response METHOD_NOT_ALLOWED_KEEP_ALIVE + = new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY); + public static final Response OK_KEEP_ALIVE = new Response(Response.OK, Response.EMPTY); + public static final Response TOO_MANY_REQUESTS_KEEP_ALIVE = new Response(TOO_MANY_REQUESTS, Response.EMPTY); + public static final Response ENTITY_TOO_LARGE_KEEP_ALIVE = + new Response(Response.REQUEST_ENTITY_TOO_LARGE, Response.EMPTY); + public static final Response SERVICE_UNAVAILABLE_KEEP_ALIVE + = new Response(Response.SERVICE_UNAVAILABLE, Response.EMPTY); + + static { + BAD_REQUEST_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + CREATED_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + ACCEPTED_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + NOT_FOUND_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + METHOD_NOT_ALLOWED_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + OK_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + TOO_MANY_REQUESTS_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + ENTITY_TOO_LARGE_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + SERVICE_UNAVAILABLE_CLOSE.addHeader(CONNECTION_CLOSE_HEADER); + + BAD_REQUEST_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + CREATED_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + ACCEPTED_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + NOT_FOUND_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + METHOD_NOT_ALLOWED_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + OK_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + TOO_MANY_REQUESTS_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + ENTITY_TOO_LARGE_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + SERVICE_UNAVAILABLE_KEEP_ALIVE.addHeader(CONNECTION_KEEP_ALIVE_HEADER); + } + + public static Response ok(final Request request) { + return keepAlive(request) ? OK_KEEP_ALIVE : OK_CLOSE; + } + + public static Response badRequest(final Request request) { + return keepAlive(request) ? BAD_REQUEST_KEEP_ALIVE : BAD_REQUEST_CLOSE; + } + + public static Response created(final Request request) { + return keepAlive(request) ? CREATED_KEEP_ALIVE : CREATED_CLOSE; + } + + public static Response accepted(final Request request) { + return keepAlive(request) ? ACCEPTED_KEEP_ALIVE : ACCEPTED_CLOSE; + } + + public static Response notFound(final Request request) { + return keepAlive(request) ? NOT_FOUND_KEEP_ALIVE : NOT_FOUND_CLOSE; + } + + public static Response methodNotAllowed(final Request request) { + return keepAlive(request) ? METHOD_NOT_ALLOWED_KEEP_ALIVE : METHOD_NOT_ALLOWED_CLOSE; + } + + public static Response tooManyRequests(Request request) { + return keepAlive(request) ? TOO_MANY_REQUESTS_KEEP_ALIVE : TOO_MANY_REQUESTS_CLOSE; + } + + public static Response entityTooLarge(Request request) { + return keepAlive(request) ? ENTITY_TOO_LARGE_KEEP_ALIVE : ENTITY_TOO_LARGE_CLOSE; + } + + public static Response serviceUnavailable(Request request) { + return keepAlive(request) ? SERVICE_UNAVAILABLE_KEEP_ALIVE : SERVICE_UNAVAILABLE_CLOSE; + } + + public static boolean keepAlive(final Request request) { + final String connection = request.getHeader("Connection:"); + return request.isHttp11() + ? !"close".equalsIgnoreCase(connection) + : "Keep-Alive".equalsIgnoreCase(connection); + } + + private LSMConstantResponse() { + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMCustomSession.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMCustomSession.java new file mode 100644 index 000000000..f78aa600a --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMCustomSession.java @@ -0,0 +1,41 @@ +package ru.vk.itmo.test.viktorkorotkikh.http; + +import one.nio.http.HttpServer; +import one.nio.http.HttpSession; +import one.nio.http.Request; +import one.nio.http.Response; +import one.nio.net.Socket; + +import java.io.IOException; + +public class LSMCustomSession extends HttpSession { + public LSMCustomSession(Socket socket, HttpServer server) { + super(socket, server); + } + + @Override + public synchronized void sendResponse(Response response) throws IOException { + Request handling = this.handling; + if (handling == null) { + throw new IOException("Out of order response"); + } + + server.incRequestsProcessed(); + + boolean keepAlive = LSMConstantResponse.keepAlive(handling); + + writeResponse(response, handling.getMethod() != Request.METHOD_HEAD); + if (!keepAlive) scheduleClose(); + + this.handling = pipeline.pollFirst(); + handling = this.handling; + + if (handling != null) { + if (handling == FIN) { + scheduleClose(); + } else { + server.handleRequest(handling, this); + } + } + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMServerResponseWithMemorySegment.java b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMServerResponseWithMemorySegment.java new file mode 100644 index 000000000..68404968f --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/http/LSMServerResponseWithMemorySegment.java @@ -0,0 +1,63 @@ +package ru.vk.itmo.test.viktorkorotkikh.http; + +import one.nio.http.Response; +import one.nio.util.ByteArrayBuilder; +import one.nio.util.Utf8; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +public class LSMServerResponseWithMemorySegment extends Response { + private static final byte[] HTTP11_HEADER = Utf8.toBytes("HTTP/1.1 "); + private static final int PROTOCOL_HEADER_LENGTH = 11; + private final MemorySegment memorySegmentBody; + + public LSMServerResponseWithMemorySegment(String resultCode, MemorySegment body) { + super(resultCode); + this.memorySegmentBody = body; + addHeader("Content-Length: " + memorySegmentBody.byteSize()); + } + + @Override + public byte[] getBody() { + return memorySegmentBody.toArray(ValueLayout.JAVA_BYTE); + } + + @Override + public void setBody(byte[] body) { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] toBytes(boolean includeBody) { + int estimatedSize = PROTOCOL_HEADER_LENGTH + getHeaderCount() * 2; + for (int i = 0; i < getHeaderCount(); i++) { + estimatedSize += getHeaders()[i].length(); + } + if (includeBody) { + estimatedSize += (int) memorySegmentBody.byteSize(); + } + + ByteArrayBuilder builder = new ByteArrayBuilder(estimatedSize); + builder.append(HTTP11_HEADER); + for (int i = 0; i < getHeaderCount(); i++) { + builder.append(getHeaders()[i]).append('\r').append('\n'); + } + builder.append('\r').append('\n'); + if (includeBody) { + writeBody(memorySegmentBody, builder); + } + return builder.buffer(); + } + + private static void writeBody(MemorySegment memorySegmentBody, ByteArrayBuilder builder) { + MemorySegment.copy( + memorySegmentBody, + ValueLayout.JAVA_BYTE, + 0L, + builder.buffer(), + builder.length(), + (int) memorySegmentBody.byteSize() + ); + } +} diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction-histogram.png new file mode 100644 index 000000000..49428c0d0 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction.txt new file mode 100644 index 000000000..f0dae230e --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-after-compaction.txt @@ -0,0 +1,118 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 0.988ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 0.99ms 533.21us 2.70ms 59.54% + Req/Sec 4.15k 318.84 5.11k 38.04% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 0.99ms + 75.000% 1.42ms + 90.000% 1.75ms + 99.000% 1.95ms + 99.900% 1.98ms + 99.990% 2.06ms + 99.999% 2.33ms +100.000% 2.70ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.010 0.000000 8 1.00 + 0.231 0.100000 20069 1.11 + 0.449 0.200000 40024 1.25 + 0.669 0.300000 60025 1.43 + 0.877 0.400000 79998 1.67 + 0.988 0.500000 100092 2.00 + 1.043 0.550000 110041 2.22 + 1.100 0.600000 120090 2.50 + 1.200 0.650000 129996 2.86 + 1.308 0.700000 140026 3.33 + 1.418 0.750000 149988 4.00 + 1.473 0.775000 155002 4.44 + 1.527 0.800000 159985 5.00 + 1.581 0.825000 164995 5.71 + 1.637 0.850000 170062 6.67 + 1.692 0.875000 175041 8.00 + 1.719 0.887500 177547 8.89 + 1.745 0.900000 180003 10.00 + 1.772 0.912500 182541 11.43 + 1.799 0.925000 185054 13.33 + 1.826 0.937500 187541 16.00 + 1.840 0.943750 188799 17.78 + 1.853 0.950000 189996 20.00 + 1.868 0.956250 191322 22.86 + 1.881 0.962500 192490 26.67 + 1.895 0.968750 193747 32.00 + 1.902 0.971875 194373 35.56 + 1.910 0.975000 195050 40.00 + 1.917 0.978125 195676 45.71 + 1.924 0.981250 196317 53.33 + 1.931 0.984375 196864 64.00 + 1.936 0.985938 197219 71.11 + 1.940 0.987500 197489 80.00 + 1.945 0.989062 197821 91.43 + 1.950 0.990625 198152 106.67 + 1.955 0.992188 198481 128.00 + 1.957 0.992969 198601 142.22 + 1.960 0.993750 198775 160.00 + 1.963 0.994531 198920 182.86 + 1.966 0.995313 199082 213.33 + 1.969 0.996094 199246 256.00 + 1.970 0.996484 199295 284.44 + 1.972 0.996875 199381 320.00 + 1.974 0.997266 199461 365.71 + 1.976 0.997656 199552 426.67 + 1.978 0.998047 199615 512.00 + 1.979 0.998242 199648 568.89 + 1.980 0.998437 199682 640.00 + 1.981 0.998633 199716 731.43 + 1.983 0.998828 199773 853.33 + 1.984 0.999023 199789 1024.00 + 1.985 0.999121 199808 1137.78 + 1.986 0.999219 199827 1280.00 + 1.987 0.999316 199844 1462.86 + 1.989 0.999414 199868 1706.67 + 1.991 0.999512 199884 2048.00 + 1.993 0.999561 199896 2275.56 + 1.994 0.999609 199901 2560.00 + 1.996 0.999658 199911 2925.71 + 2.004 0.999707 199921 3413.33 + 2.010 0.999756 199933 4096.00 + 2.012 0.999780 199936 4551.11 + 2.017 0.999805 199941 5120.00 + 2.025 0.999829 199946 5851.43 + 2.031 0.999854 199950 6826.67 + 2.043 0.999878 199955 8192.00 + 2.053 0.999890 199958 9102.22 + 2.057 0.999902 199960 10240.00 + 2.061 0.999915 199962 11702.86 + 2.079 0.999927 199965 13653.33 + 2.091 0.999939 199967 16384.00 + 2.097 0.999945 199969 18204.44 + 2.099 0.999951 199970 20480.00 + 2.105 0.999957 199971 23405.71 + 2.135 0.999963 199972 27306.67 + 2.137 0.999969 199974 32768.00 + 2.137 0.999973 199974 36408.89 + 2.147 0.999976 199975 40960.00 + 2.147 0.999979 199975 46811.43 + 2.257 0.999982 199976 54613.33 + 2.257 0.999985 199976 65536.00 + 2.331 0.999986 199977 72817.78 + 2.331 0.999988 199977 81920.00 + 2.331 0.999989 199977 93622.86 + 2.567 0.999991 199978 109226.67 + 2.567 0.999992 199978 131072.00 + 2.567 0.999993 199978 145635.56 + 2.567 0.999994 199978 163840.00 + 2.567 0.999995 199978 187245.71 + 2.703 0.999995 199979 218453.33 + 2.703 1.000000 199979 inf +#[Mean = 0.989, StdDeviation = 0.533] +#[Max = 2.702, Total count = 199979] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 239994 requests in 1.00m, 174.28MB read + Non-2xx or 3xx responses: 176585 +Requests/sec: 3999.87 +Transfer/sec: 2.90MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-histogram.png new file mode 100644 index 000000000..0aa679fd8 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s.txt new file mode 100644 index 000000000..2b6df0a36 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-4000-60s.txt @@ -0,0 +1,117 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.012ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 2.07ms 5.13ms 50.46ms 94.61% + Req/Sec 4.22k 351.34 5.67k 70.28% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 1.01ms + 75.000% 1.45ms + 90.000% 1.98ms + 99.000% 31.09ms + 99.900% 49.41ms + 99.990% 50.24ms + 99.999% 50.40ms +100.000% 50.49ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.010 0.000000 1 1.00 + 0.331 0.100000 20041 1.11 + 0.521 0.200000 40074 1.25 + 0.703 0.300000 60018 1.43 + 0.872 0.400000 79968 1.67 + 1.015 0.500000 99902 2.00 + 1.079 0.550000 109902 2.22 + 1.143 0.600000 120022 2.50 + 1.216 0.650000 129888 2.86 + 1.312 0.700000 139863 3.33 + 1.450 0.750000 149891 4.00 + 1.527 0.775000 154850 4.44 + 1.608 0.800000 159864 5.00 + 1.691 0.825000 164843 5.71 + 1.780 0.850000 169870 6.67 + 1.872 0.875000 174859 8.00 + 1.922 0.887500 177341 8.89 + 1.978 0.900000 179827 10.00 + 2.051 0.912500 182327 11.43 + 2.265 0.925000 184815 13.33 + 3.799 0.937500 187300 16.00 + 5.755 0.943750 188550 17.78 + 9.487 0.950000 189798 20.00 + 11.071 0.956250 191049 22.86 + 12.927 0.962500 192298 26.67 + 13.999 0.968750 193543 32.00 + 14.615 0.971875 194170 35.56 + 16.543 0.975000 194793 40.00 + 17.519 0.978125 195423 45.71 + 18.991 0.981250 196043 53.33 + 20.895 0.984375 196665 64.00 + 23.119 0.985938 196977 71.11 + 27.903 0.987500 197295 80.00 + 29.551 0.989062 197602 91.43 + 31.807 0.990625 197915 106.67 + 33.631 0.992188 198232 128.00 + 34.079 0.992969 198390 142.22 + 37.631 0.993750 198539 160.00 + 40.575 0.994531 198694 182.86 + 43.807 0.995313 198852 213.33 + 46.335 0.996094 199006 256.00 + 46.719 0.996484 199085 284.44 + 47.135 0.996875 199168 320.00 + 47.647 0.997266 199242 365.71 + 47.935 0.997656 199321 426.67 + 48.255 0.998047 199396 512.00 + 48.447 0.998242 199440 568.89 + 48.607 0.998437 199475 640.00 + 48.863 0.998633 199517 731.43 + 49.215 0.998828 199556 853.33 + 49.439 0.999023 199591 1024.00 + 49.599 0.999121 199613 1137.78 + 49.727 0.999219 199635 1280.00 + 49.823 0.999316 199654 1462.86 + 49.887 0.999414 199676 1706.67 + 49.951 0.999512 199691 2048.00 + 49.983 0.999561 199705 2275.56 + 50.015 0.999609 199711 2560.00 + 50.079 0.999658 199722 2925.71 + 50.111 0.999707 199731 3413.33 + 50.143 0.999756 199745 4096.00 + 50.143 0.999780 199745 4551.11 + 50.175 0.999805 199752 5120.00 + 50.175 0.999829 199752 5851.43 + 50.207 0.999854 199760 6826.67 + 50.239 0.999878 199766 8192.00 + 50.239 0.999890 199766 9102.22 + 50.271 0.999902 199771 10240.00 + 50.271 0.999915 199771 11702.86 + 50.303 0.999927 199775 13653.33 + 50.303 0.999939 199775 16384.00 + 50.335 0.999945 199779 18204.44 + 50.335 0.999951 199779 20480.00 + 50.335 0.999957 199779 23405.71 + 50.335 0.999963 199779 27306.67 + 50.367 0.999969 199782 32768.00 + 50.367 0.999973 199782 36408.89 + 50.367 0.999976 199782 40960.00 + 50.367 0.999979 199782 46811.43 + 50.399 0.999982 199784 54613.33 + 50.399 0.999985 199784 65536.00 + 50.399 0.999986 199784 72817.78 + 50.399 0.999988 199784 81920.00 + 50.399 0.999989 199784 93622.86 + 50.431 0.999991 199785 109226.67 + 50.431 0.999992 199785 131072.00 + 50.431 0.999993 199785 145635.56 + 50.431 0.999994 199785 163840.00 + 50.431 0.999995 199785 187245.71 + 50.495 0.999995 199786 218453.33 + 50.495 1.000000 199786 inf +#[Mean = 2.069, StdDeviation = 5.125] +#[Max = 50.464, Total count = 199786] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 239800 requests in 1.00m, 616.69MB read +Requests/sec: 3996.67 +Transfer/sec: 10.28MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-alloc-general.png new file mode 100644 index 000000000..eb166c7b2 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-general.png new file mode 100644 index 000000000..71e1bc49e Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-get.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-get.png new file mode 100644 index 000000000..541e1324b Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-after-compact-cpu-get.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-alloc-general.png new file mode 100644 index 000000000..7b1c276bd Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-cpu-general.png new file mode 100644 index 000000000..a1fec43ad Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/GET-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-histogram.png new file mode 100644 index 000000000..869f5ef6e Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot-histogram.png new file mode 100644 index 000000000..81cfdb3ee Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot.txt new file mode 100644 index 000000000..f8f7a0655 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s-hot.txt @@ -0,0 +1,119 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 0.704ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 717.79us 392.04us 7.77ms 70.28% + Req/Sec 5.80k 345.26 6.78k 67.53% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 698.00us + 75.000% 0.99ms + 90.000% 1.16ms + 99.000% 1.28ms + 99.900% 5.22ms + 99.990% 7.49ms + 99.999% 7.76ms +100.000% 7.78ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.149 0.000000 2 1.00 + 0.270 0.100000 27717 1.11 + 0.375 0.200000 55254 1.25 + 0.480 0.300000 82523 1.43 + 0.588 0.400000 110182 1.67 + 0.698 0.500000 137689 2.00 + 0.753 0.550000 151360 2.22 + 0.810 0.600000 165181 2.50 + 0.867 0.650000 178787 2.86 + 0.925 0.700000 192485 3.33 + 0.985 0.750000 206323 4.00 + 1.015 0.775000 213182 4.44 + 1.045 0.800000 220058 5.00 + 1.075 0.825000 226995 5.71 + 1.104 0.850000 233770 6.67 + 1.134 0.875000 240813 8.00 + 1.148 0.887500 244125 8.89 + 1.162 0.900000 247529 10.00 + 1.177 0.912500 251151 11.43 + 1.190 0.925000 254456 13.33 + 1.204 0.937500 257902 16.00 + 1.211 0.943750 259673 17.78 + 1.218 0.950000 261408 20.00 + 1.225 0.956250 263134 22.86 + 1.231 0.962500 264675 26.67 + 1.239 0.968750 266572 32.00 + 1.242 0.971875 267264 35.56 + 1.246 0.975000 268125 40.00 + 1.250 0.978125 269018 45.71 + 1.255 0.981250 269969 53.33 + 1.260 0.984375 270819 64.00 + 1.263 0.985938 271212 71.11 + 1.267 0.987500 271618 80.00 + 1.272 0.989062 272024 91.43 + 1.280 0.990625 272413 106.67 + 1.310 0.992188 272832 128.00 + 1.346 0.992969 273043 142.22 + 1.390 0.993750 273262 160.00 + 1.449 0.994531 273471 182.86 + 1.530 0.995313 273687 213.33 + 1.715 0.996094 273900 256.00 + 1.873 0.996484 274008 284.44 + 2.027 0.996875 274115 320.00 + 2.299 0.997266 274223 365.71 + 2.655 0.997656 274330 426.67 + 3.239 0.998047 274437 512.00 + 3.755 0.998242 274491 568.89 + 4.195 0.998437 274546 640.00 + 4.467 0.998633 274600 731.43 + 4.719 0.998828 274652 853.33 + 5.283 0.999023 274707 1024.00 + 5.507 0.999121 274733 1137.78 + 5.715 0.999219 274760 1280.00 + 5.943 0.999316 274787 1462.86 + 6.127 0.999414 274813 1706.67 + 6.343 0.999512 274840 2048.00 + 6.515 0.999561 274854 2275.56 + 6.687 0.999609 274867 2560.00 + 6.851 0.999658 274881 2925.71 + 6.983 0.999707 274895 3413.33 + 7.099 0.999756 274907 4096.00 + 7.159 0.999780 274914 4551.11 + 7.235 0.999805 274921 5120.00 + 7.331 0.999829 274929 5851.43 + 7.379 0.999854 274934 6826.67 + 7.451 0.999878 274941 8192.00 + 7.483 0.999890 274945 9102.22 + 7.495 0.999902 274948 10240.00 + 7.527 0.999915 274952 11702.86 + 7.551 0.999927 274954 13653.33 + 7.607 0.999939 274958 16384.00 + 7.631 0.999945 274960 18204.44 + 7.635 0.999951 274961 20480.00 + 7.675 0.999957 274963 23405.71 + 7.691 0.999963 274964 27306.67 + 7.711 0.999969 274966 32768.00 + 7.731 0.999973 274968 36408.89 + 7.731 0.999976 274968 40960.00 + 7.735 0.999979 274969 46811.43 + 7.735 0.999982 274969 54613.33 + 7.743 0.999985 274970 65536.00 + 7.759 0.999986 274972 72817.78 + 7.759 0.999988 274972 81920.00 + 7.759 0.999989 274972 93622.86 + 7.759 0.999991 274972 109226.67 + 7.759 0.999992 274972 131072.00 + 7.771 0.999993 274973 145635.56 + 7.771 0.999994 274973 163840.00 + 7.771 0.999995 274973 187245.71 + 7.771 0.999995 274973 218453.33 + 7.771 0.999996 274973 262144.00 + 7.775 0.999997 274974 291271.11 + 7.775 1.000000 274974 inf +#[Mean = 0.718, StdDeviation = 0.392] +#[Max = 7.772, Total count = 274974] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 329993 requests in 1.00m, 21.09MB read +Requests/sec: 5499.91 +Transfer/sec: 359.86KB \ No newline at end of file diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s.txt new file mode 100644 index 000000000..4b56be232 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-5500-60s.txt @@ -0,0 +1,111 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 2.817ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 811.40us 1.18ms 22.29ms 98.71% + Req/Sec 5.81k 395.83 6.78k 58.03% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 695.00us + 75.000% 0.99ms + 90.000% 1.18ms + 99.000% 4.35ms + 99.900% 19.49ms + 99.990% 22.14ms + 99.999% 22.29ms +100.000% 22.30ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.147 0.000000 1 1.00 + 0.269 0.100000 27560 1.11 + 0.371 0.200000 55150 1.25 + 0.475 0.300000 82566 1.43 + 0.583 0.400000 110211 1.67 + 0.695 0.500000 137527 2.00 + 0.754 0.550000 151423 2.22 + 0.812 0.600000 164979 2.50 + 0.872 0.650000 178818 2.86 + 0.933 0.700000 192659 3.33 + 0.994 0.750000 206445 4.00 + 1.024 0.775000 213252 4.44 + 1.055 0.800000 220121 5.00 + 1.085 0.825000 226867 5.71 + 1.116 0.850000 233887 6.67 + 1.146 0.875000 240602 8.00 + 1.161 0.887500 244136 8.89 + 1.176 0.900000 247513 10.00 + 1.191 0.912500 251070 11.43 + 1.205 0.925000 254507 13.33 + 1.218 0.937500 257781 16.00 + 1.225 0.943750 259501 17.78 + 1.232 0.950000 261266 20.00 + 1.239 0.956250 262982 22.86 + 1.247 0.962500 264873 26.67 + 1.254 0.968750 266421 32.00 + 1.259 0.971875 267369 35.56 + 1.264 0.975000 268213 40.00 + 1.271 0.978125 269042 45.71 + 1.285 0.981250 269839 53.33 + 1.443 0.984375 270671 64.00 + 1.657 0.985938 271099 71.11 + 2.241 0.987500 271528 80.00 + 3.555 0.989062 271958 91.43 + 4.827 0.990625 272388 106.67 + 6.299 0.992188 272818 128.00 + 7.063 0.992969 273033 142.22 + 7.723 0.993750 273247 160.00 + 8.519 0.994531 273462 182.86 + 9.415 0.995313 273678 213.33 + 10.623 0.996094 273892 256.00 + 11.503 0.996484 273999 284.44 + 12.695 0.996875 274106 320.00 + 13.991 0.997266 274217 365.71 + 15.207 0.997656 274322 426.67 + 16.463 0.998047 274428 512.00 + 17.039 0.998242 274482 568.89 + 17.695 0.998437 274537 640.00 + 18.287 0.998633 274590 731.43 + 18.975 0.998828 274645 853.33 + 19.583 0.999023 274697 1024.00 + 19.919 0.999121 274724 1137.78 + 20.191 0.999219 274752 1280.00 + 20.479 0.999316 274778 1462.86 + 20.783 0.999414 274804 1706.67 + 21.039 0.999512 274831 2048.00 + 21.167 0.999561 274845 2275.56 + 21.359 0.999609 274859 2560.00 + 21.535 0.999658 274872 2925.71 + 21.679 0.999707 274885 3413.33 + 21.823 0.999756 274898 4096.00 + 21.903 0.999780 274906 4551.11 + 21.967 0.999805 274912 5120.00 + 22.015 0.999829 274919 5851.43 + 22.063 0.999854 274925 6826.67 + 22.111 0.999878 274933 8192.00 + 22.127 0.999890 274935 9102.22 + 22.143 0.999902 274939 10240.00 + 22.159 0.999915 274943 11702.86 + 22.175 0.999927 274947 13653.33 + 22.207 0.999939 274949 16384.00 + 22.223 0.999945 274952 18204.44 + 22.223 0.999951 274952 20480.00 + 22.239 0.999957 274957 23405.71 + 22.239 0.999963 274957 27306.67 + 22.239 0.999969 274957 32768.00 + 22.255 0.999973 274958 36408.89 + 22.271 0.999976 274960 40960.00 + 22.271 0.999979 274960 46811.43 + 22.271 0.999982 274960 54613.33 + 22.287 0.999985 274962 65536.00 + 22.287 0.999986 274962 72817.78 + 22.287 0.999988 274962 81920.00 + 22.303 0.999989 274965 93622.86 + 22.303 1.000000 274965 inf +#[Mean = 0.811, StdDeviation = 1.184] +#[Max = 22.288, Total count = 274965] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 329987 requests in 1.00m, 21.08MB read +Requests/sec: 5499.91 +Transfer/sec: 359.86KB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-general.png new file mode 100644 index 000000000..f4ae448ae Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-handle.png new file mode 100644 index 000000000..8218f4539 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-save-sstable.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-save-sstable.png new file mode 100644 index 000000000..d0ada9bea Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-alloc-save-sstable.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-general.png new file mode 100644 index 000000000..9c8dcb3d7 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-handle.png new file mode 100644 index 000000000..591ca3e1d Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-jit.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-jit.png new file mode 100644 index 000000000..9abe211f2 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-jit.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-save-sstable.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-save-sstable.png new file mode 100644 index 000000000..a38b7b0db Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-cpu-save-sstable.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-alloc-general.png new file mode 100644 index 000000000..9aab59b15 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-cpu-general.png new file mode 100644 index 000000000..5ad54f728 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/PUT-hot-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/report.md b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/report.md new file mode 100644 index 000000000..e9baa4b2f --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1/report.md @@ -0,0 +1,193 @@ +# Stage 1 + +- [PUT](#PUT) + * [Latency](#latency) + * [CPU profiling](#cpu-profiling) + * [Memory allocations](#memory-allocations) +- [PUT hot](#PUT-hot) + * [Latency](#latency-1) + * [CPU profiling](#cpu-profiling-1) + * [Memory allocations](#memory-allocations-1) +- [GET (после PUT)](#Get-после-PUT) + * [Latency](#latency-2) + * [CPU profiling](#cpu-profiling-2) + * [Memory allocations](#memory-allocations-2) +- [GET (после compaction)](#get-после-compaction) + * [Latency](#latency-3) + * [CPU profiling](#cpu-profiling-3) + * [Memory allocations](#memory-allocations-3) + + +## PUT + +### Latency + +Экспериментальным путём было выявлено, что стабильной нагрузкой ниже точки разладки является 5500 запросов в секунду. + +Результаты замера ([PUT-5500-60s.txt](PUT-5500-60s.txt)): +``` + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 695.00us + 75.000% 0.99ms + 90.000% 1.18ms + 99.000% 4.35ms + 99.900% 19.49ms + 99.990% 22.14ms + 99.999% 22.29ms +100.000% 22.30ms +``` + +![PUT-5500-60s-histogram.png](PUT-5500-60s-histogram.png) + +На графике хорошо заметно, что, во-первых, есть заметный рост задержки около 98%, и, во-вторых, гигантский рост после 99%. +Это связано с прогревом JVM (на cpu профиле будет видно, что большое количество процессорного времени заняла jit компиляция). + +### Cpu profiling + +![PUT-cpu-profiling](PUT-cpu-general.png) + +На графике профилирования cpu видно, что немалую часть времени заняла jit компиляция - примерно 25% всего процессорного времени. + +При этом большую часть времени обработки запроса занимает запись ответа - почти 30% процессорного времени: +![PUT-cpu-profiling-handle-request](PUT-cpu-handle.png) + +Также часть времени заняла запись sstable на диск - 12%: +![PUT-cpu-profiling-save-sstable](PUT-cpu-save-sstable.png) + +### Memory allocations + +![PUT-alloc-profiling](PUT-alloc-general.png) + +Больше всего памяти выделяется при приёме запроса - аллокация буффера для чтения запроса. + +Также видно, что именно чтение тела запроса занимает больше памяти, чем все остальные параметры +(заголовки, параметры и т.д.). + +![PUT-alloc-handle](PUT-alloc-handle.png) + +При обработке запроса память занимает преобразование строки параметра id в MemorySegment (выделение byte[]), +а также преобразование тела запроса в MemorySegment. + +![PUT-alloc-save-sstable](PUT-alloc-save-sstable.png) + +При сохранении sstable выделяется заметное количество памяти при чтении индекс файла (readAllLines), +а также при инициализации BufferedOutputStream для записи в файл. + +Повторное чтение индекс файла можно заменить на постоянно хранящийся в памяти список sstable, +который заполняется при инициализации dao и просто модифицируется. + +## Put (hot) + +Проведём повторное тестирование и посмотрим, как будет вести себя система с уже прогретой jvm. + +### Latency + +[PUT-5500-60s-hot.txt](PUT-5500-60s-hot.txt): +``` + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 698.00us + 75.000% 0.99ms + 90.000% 1.16ms + 99.000% 1.28ms + 99.900% 5.22ms + 99.990% 7.49ms + 99.999% 7.76ms +100.000% 7.78ms +``` + +![PUT-5500-60s-hot-histogram.png](PUT-5500-60s-hot-histogram.png) + +В целом график похож на график теста с холодной jvm, но задержки для 99.9+ перцентиля в 3 раза меньше. + +### Cpu profiling + +То, что jvm была прогрета подтверждает и график профилирования CPU - jit компиляция заняла лишь ~5% вместо 25%: + +![PUT-hot-cpu-general.png](PUT-hot-cpu-general.png) + +### Memory allocations + +С точки зрения аллокаций ничего не поменялось: + +![PUT-hot-alloc-general.png](PUT-hot-alloc-general.png) + +## Get (после PUT) + +### Latency + +GET запросы отправлялись на прогретой jvm после PUT запросов. + +Экспериментальным путём было выявлено, что стабильной нагрузкой ниже точки разладки является 4000 запросов в секунду. + +Результаты замера ([GET-4000-60s](GET-4000-60s.txt)): + +``` +Latency Distribution (HdrHistogram - Recorded Latency) +50.000% 1.01ms +75.000% 1.45ms +90.000% 1.98ms +99.000% 31.09ms +99.900% 49.41ms +99.990% 50.24ms +99.999% 50.40ms +100.000% 50.49ms +``` + +![GET-4000-60s-histogram.png](GET-4000-60s-histogram.png) + +На графике хорошо заметно, что серьёзные "проблемы" начинаются после 90%. Но после 99.9% выходят на плато. + +### Cpu profiling + +![GET-cpu-profiling](GET-cpu-general.png) + +На графике профилирования cpu видно, что большую часть времени занимают операции сравнения MemorySegment - 24% времени. +Операции записи ответа в сокет занимают всего 2.7%. + +### Memory allocations + +![GET-alloc-profiling](GET-alloc-general.png) + +Немалое количество памяти выделяется при отправке ответа на запрос - 45% от общего количества. + +Парсинг самого запроса потребляет лишь 3%. + +Куда большее количество памяти требуется при поиске Entity - 48%. +Это происходит из-за чтения Entity с диска и копирования его значения в byte[] (так как Response в качестве тела ответа принимает byte[]). + +## Get (после compaction) + +### Latency + +Результаты замера ([GET-4000-60s-after-compaction.txt](GET-4000-60s-after-compaction.txt)): + +``` + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 0.99ms + 75.000% 1.42ms + 90.000% 1.75ms + 99.000% 1.95ms + 99.900% 1.98ms + 99.990% 2.06ms + 99.999% 2.33ms +100.000% 2.70ms +``` + +![GET-4000-60s-after-compaction-histogram.png](GET-4000-60s-after-compaction-histogram.png) + +На графике хорошо заметно, что в целом latency стала более ровной, но есть "проблемы" около трёх девяток + +### Cpu profiling + +![GET-after-compact-cpu-general.png](GET-after-compact-cpu-general.png) + +Всё ещё большу́ю часть времени занимают операции сравнения MemorySegment - 22% времени. +Однако запись в сокет стала потреблять 34%. Это связано с тем, что до compact у нас был список sstable, по которому +мы итерировались и для каждого sstable выполняли бинарный поиск, а после compact выполняем бинарный поиск в одном файле, +что оказалось быстрее. + +### Memory allocations + +![GET-after-compact-alloc-general.png](GET-after-compact-alloc-general.png) + +C точки зрения аллокаций в целом ситуация осталась такой же. diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact-histogram.png new file mode 100644 index 000000000..d1926dfcf Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact.txt new file mode 100644 index 000000000..556f08c68 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-aftercompact.txt @@ -0,0 +1,132 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.001ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 1.01ms 552.03us 3.15ms 59.59% + Req/Sec 31.58k 1.76k 39.33k 77.04% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 1.01ms + 75.000% 1.45ms + 90.000% 1.79ms + 99.000% 2.01ms + 99.900% 2.11ms + 99.990% 2.54ms + 99.999% 3.03ms +100.000% 3.15ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.009 0.000000 4 1.00 + 0.231 0.100000 150512 1.11 + 0.450 0.200000 300247 1.25 + 0.667 0.300000 450184 1.43 + 0.873 0.400000 600045 1.67 + 1.006 0.500000 750847 2.00 + 1.061 0.550000 825191 2.22 + 1.121 0.600000 900340 2.50 + 1.231 0.650000 975211 2.86 + 1.342 0.700000 1050179 3.33 + 1.453 0.750000 1124890 4.00 + 1.509 0.775000 1162604 4.44 + 1.565 0.800000 1200180 5.00 + 1.621 0.825000 1237756 5.71 + 1.677 0.850000 1275165 6.67 + 1.733 0.875000 1312603 8.00 + 1.762 0.887500 1331653 8.89 + 1.790 0.900000 1350197 10.00 + 1.818 0.912500 1368673 11.43 + 1.847 0.925000 1387446 13.33 + 1.876 0.937500 1406094 16.00 + 1.891 0.943750 1415701 17.78 + 1.906 0.950000 1425213 20.00 + 1.921 0.956250 1434532 22.86 + 1.936 0.962500 1443803 26.67 + 1.952 0.968750 1453506 32.00 + 1.960 0.971875 1458136 35.56 + 1.968 0.975000 1462757 40.00 + 1.976 0.978125 1467325 45.71 + 1.985 0.981250 1472050 53.33 + 1.994 0.984375 1476793 64.00 + 1.998 0.985938 1478755 71.11 + 2.003 0.987500 1481076 80.00 + 2.009 0.989062 1483677 91.43 + 2.014 0.990625 1485775 106.67 + 2.020 0.992188 1488139 128.00 + 2.024 0.992969 1489470 142.22 + 2.028 0.993750 1490641 160.00 + 2.032 0.994531 1491662 182.86 + 2.037 0.995313 1492799 213.33 + 2.044 0.996094 1494078 256.00 + 2.049 0.996484 1494745 284.44 + 2.053 0.996875 1495165 320.00 + 2.059 0.997266 1495728 365.71 + 2.067 0.997656 1496344 426.67 + 2.077 0.998047 1496906 512.00 + 2.083 0.998242 1497226 568.89 + 2.089 0.998437 1497509 640.00 + 2.097 0.998633 1497834 731.43 + 2.105 0.998828 1498141 853.33 + 2.113 0.999023 1498410 1024.00 + 2.117 0.999121 1498534 1137.78 + 2.121 0.999219 1498656 1280.00 + 2.127 0.999316 1498815 1462.86 + 2.133 0.999414 1498965 1706.67 + 2.139 0.999512 1499116 2048.00 + 2.143 0.999561 1499195 2275.56 + 2.147 0.999609 1499260 2560.00 + 2.153 0.999658 1499317 2925.71 + 2.175 0.999707 1499383 3413.33 + 2.227 0.999756 1499456 4096.00 + 2.267 0.999780 1499496 4551.11 + 2.309 0.999805 1499531 5120.00 + 2.363 0.999829 1499569 5851.43 + 2.415 0.999854 1499603 6826.67 + 2.475 0.999878 1499639 8192.00 + 2.515 0.999890 1499659 9102.22 + 2.545 0.999902 1499676 10240.00 + 2.589 0.999915 1499695 11702.86 + 2.641 0.999927 1499714 13653.33 + 2.701 0.999939 1499731 16384.00 + 2.735 0.999945 1499740 18204.44 + 2.763 0.999951 1499749 20480.00 + 2.789 0.999957 1499758 23405.71 + 2.829 0.999963 1499768 27306.67 + 2.869 0.999969 1499777 32768.00 + 2.889 0.999973 1499781 36408.89 + 2.901 0.999976 1499786 40960.00 + 2.919 0.999979 1499790 46811.43 + 2.957 0.999982 1499795 54613.33 + 2.987 0.999985 1499800 65536.00 + 2.995 0.999986 1499802 72817.78 + 3.011 0.999988 1499804 81920.00 + 3.029 0.999989 1499806 93622.86 + 3.041 0.999991 1499809 109226.67 + 3.053 0.999992 1499811 131072.00 + 3.057 0.999993 1499812 145635.56 + 3.059 0.999994 1499813 163840.00 + 3.073 0.999995 1499814 187245.71 + 3.095 0.999995 1499816 218453.33 + 3.097 0.999996 1499817 262144.00 + 3.097 0.999997 1499817 291271.11 + 3.109 0.999997 1499818 327680.00 + 3.109 0.999997 1499818 374491.43 + 3.119 0.999998 1499819 436906.67 + 3.121 0.999998 1499820 524288.00 + 3.121 0.999998 1499820 582542.22 + 3.121 0.999998 1499820 655360.00 + 3.121 0.999999 1499820 748982.86 + 3.141 0.999999 1499821 873813.33 + 3.141 0.999999 1499821 1048576.00 + 3.141 0.999999 1499821 1165084.44 + 3.141 0.999999 1499821 1310720.00 + 3.141 0.999999 1499821 1497965.71 + 3.151 0.999999 1499822 1747626.67 + 3.151 1.000000 1499822 inf +#[Mean = 1.007, StdDeviation = 0.552] +#[Max = 3.150, Total count = 1499822] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 1799949 requests in 1.00m, 127.67MB read +Requests/sec: 29999.19 +Transfer/sec: 2.13MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-histogram.png new file mode 100644 index 000000000..c63d431ab Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations-histrogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations-histrogram.png new file mode 100644 index 000000000..9007cae64 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations-histrogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations.txt new file mode 100644 index 000000000..a3a7fc27d --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s-less-allocations.txt @@ -0,0 +1,127 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.004ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 641.45us 380.09us 4.45ms 64.97% + Req/Sec 31.60k 2.24k 38.90k 66.83% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 633.00us + 75.000% 0.92ms + 90.000% 1.09ms + 99.000% 1.70ms + 99.900% 1.95ms + 99.990% 4.03ms + 99.999% 4.42ms +100.000% 4.45ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.012 0.000000 6 1.00 + 0.145 0.100000 151126 1.11 + 0.269 0.200000 300847 1.25 + 0.392 0.300000 450760 1.43 + 0.514 0.400000 601035 1.67 + 0.633 0.500000 750230 2.00 + 0.692 0.550000 825391 2.22 + 0.750 0.600000 900351 2.50 + 0.807 0.650000 975366 2.86 + 0.863 0.700000 1050221 3.33 + 0.919 0.750000 1125340 4.00 + 0.947 0.775000 1163083 4.44 + 0.975 0.800000 1200880 5.00 + 1.003 0.825000 1238678 5.71 + 1.030 0.850000 1275377 6.67 + 1.058 0.875000 1313202 8.00 + 1.072 0.887500 1332365 8.89 + 1.085 0.900000 1350062 10.00 + 1.100 0.912500 1369848 11.43 + 1.115 0.925000 1388486 13.33 + 1.132 0.937500 1406494 16.00 + 1.151 0.943750 1415481 17.78 + 1.209 0.950000 1424908 20.00 + 1.274 0.956250 1434355 22.86 + 1.341 0.962500 1443673 26.67 + 1.412 0.968750 1453088 32.00 + 1.448 0.971875 1457697 35.56 + 1.486 0.975000 1462391 40.00 + 1.525 0.978125 1467055 45.71 + 1.566 0.981250 1471746 53.33 + 1.610 0.984375 1476540 64.00 + 1.632 0.985938 1478800 71.11 + 1.656 0.987500 1481156 80.00 + 1.681 0.989062 1483528 91.43 + 1.707 0.990625 1485857 106.67 + 1.735 0.992188 1488200 128.00 + 1.750 0.992969 1489376 142.22 + 1.766 0.993750 1490532 160.00 + 1.782 0.994531 1491680 182.86 + 1.800 0.995313 1492847 213.33 + 1.820 0.996094 1494044 256.00 + 1.830 0.996484 1494604 284.44 + 1.841 0.996875 1495175 320.00 + 1.854 0.997266 1495787 365.71 + 1.867 0.997656 1496336 426.67 + 1.883 0.998047 1496931 512.00 + 1.892 0.998242 1497212 568.89 + 1.904 0.998437 1497516 640.00 + 1.916 0.998633 1497800 731.43 + 1.932 0.998828 1498098 853.33 + 1.953 0.999023 1498400 1024.00 + 1.966 0.999121 1498539 1137.78 + 1.981 0.999219 1498678 1280.00 + 2.000 0.999316 1498822 1462.86 + 2.025 0.999414 1498971 1706.67 + 2.061 0.999512 1499117 2048.00 + 2.097 0.999561 1499191 2275.56 + 2.313 0.999609 1499261 2560.00 + 2.609 0.999658 1499335 2925.71 + 2.883 0.999707 1499407 3413.33 + 3.167 0.999756 1499480 4096.00 + 3.317 0.999780 1499518 4551.11 + 3.459 0.999805 1499554 5120.00 + 3.603 0.999829 1499591 5851.43 + 3.749 0.999854 1499627 6826.67 + 3.897 0.999878 1499664 8192.00 + 3.969 0.999890 1499682 9102.22 + 4.037 0.999902 1499700 10240.00 + 4.099 0.999915 1499719 11702.86 + 4.159 0.999927 1499737 13653.33 + 4.219 0.999939 1499755 16384.00 + 4.259 0.999945 1499764 18204.44 + 4.291 0.999951 1499774 20480.00 + 4.319 0.999957 1499783 23405.71 + 4.347 0.999963 1499792 27306.67 + 4.379 0.999969 1499803 32768.00 + 4.383 0.999973 1499810 36408.89 + 4.383 0.999976 1499810 40960.00 + 4.387 0.999979 1499815 46811.43 + 4.395 0.999982 1499819 54613.33 + 4.403 0.999985 1499824 65536.00 + 4.407 0.999986 1499826 72817.78 + 4.415 0.999988 1499829 81920.00 + 4.419 0.999989 1499831 93622.86 + 4.423 0.999991 1499833 109226.67 + 4.427 0.999992 1499835 131072.00 + 4.431 0.999993 1499836 145635.56 + 4.435 0.999994 1499838 163840.00 + 4.435 0.999995 1499838 187245.71 + 4.443 0.999995 1499843 218453.33 + 4.443 0.999996 1499843 262144.00 + 4.443 0.999997 1499843 291271.11 + 4.443 0.999997 1499843 327680.00 + 4.443 0.999997 1499843 374491.43 + 4.443 0.999998 1499843 436906.67 + 4.447 0.999998 1499844 524288.00 + 4.447 0.999998 1499844 582542.22 + 4.447 0.999998 1499844 655360.00 + 4.447 0.999999 1499844 748982.86 + 4.451 0.999999 1499846 873813.33 + 4.451 1.000000 1499846 inf +#[Mean = 0.641, StdDeviation = 0.380] +#[Max = 4.448, Total count = 1499846] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 1799962 requests in 1.00m, 86.48MB read +Requests/sec: 29999.35 +Transfer/sec: 1.44MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s.txt new file mode 100644 index 000000000..10150d439 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-30000-60s.txt @@ -0,0 +1,132 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.004ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 646.75us 384.88us 5.79ms 65.27% + Req/Sec 31.61k 2.34k 40.11k 65.32% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 638.00us + 75.000% 0.93ms + 90.000% 1.09ms + 99.000% 1.72ms + 99.900% 1.98ms + 99.990% 4.51ms + 99.999% 5.66ms +100.000% 5.79ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.010 0.000000 2 1.00 + 0.146 0.100000 151046 1.11 + 0.270 0.200000 299984 1.25 + 0.394 0.300000 450119 1.43 + 0.517 0.400000 600735 1.67 + 0.638 0.500000 750757 2.00 + 0.697 0.550000 825106 2.22 + 0.756 0.600000 900208 2.50 + 0.814 0.650000 975165 2.86 + 0.871 0.700000 1049938 3.33 + 0.928 0.750000 1125772 4.00 + 0.956 0.775000 1163380 4.44 + 0.983 0.800000 1199979 5.00 + 1.011 0.825000 1238074 5.71 + 1.038 0.850000 1275076 6.67 + 1.065 0.875000 1312769 8.00 + 1.078 0.887500 1331224 8.89 + 1.092 0.900000 1351055 10.00 + 1.105 0.912500 1369030 11.43 + 1.119 0.925000 1387475 13.33 + 1.138 0.937500 1406238 16.00 + 1.164 0.943750 1415550 17.78 + 1.219 0.950000 1424926 20.00 + 1.281 0.956250 1434285 22.86 + 1.349 0.962500 1443672 26.67 + 1.422 0.968750 1452998 32.00 + 1.461 0.971875 1457699 35.56 + 1.501 0.975000 1462399 40.00 + 1.543 0.978125 1467136 45.71 + 1.586 0.981250 1471805 53.33 + 1.632 0.984375 1476464 64.00 + 1.656 0.985938 1478790 71.11 + 1.681 0.987500 1481125 80.00 + 1.708 0.989062 1483516 91.43 + 1.736 0.990625 1485838 106.67 + 1.766 0.992188 1488194 128.00 + 1.782 0.992969 1489383 142.22 + 1.798 0.993750 1490495 160.00 + 1.816 0.994531 1491673 182.86 + 1.835 0.995313 1492853 213.33 + 1.856 0.996094 1494045 256.00 + 1.867 0.996484 1494637 284.44 + 1.879 0.996875 1495212 320.00 + 1.892 0.997266 1495789 365.71 + 1.907 0.997656 1496383 426.67 + 1.923 0.998047 1496950 512.00 + 1.932 0.998242 1497228 568.89 + 1.942 0.998437 1497535 640.00 + 1.953 0.998633 1497823 731.43 + 1.966 0.998828 1498114 853.33 + 1.980 0.999023 1498398 1024.00 + 1.990 0.999121 1498553 1137.78 + 2.000 0.999219 1498689 1280.00 + 2.015 0.999316 1498839 1462.86 + 2.033 0.999414 1498983 1706.67 + 2.067 0.999512 1499128 2048.00 + 2.097 0.999561 1499201 2275.56 + 2.205 0.999609 1499276 2560.00 + 2.459 0.999658 1499347 2925.71 + 2.705 0.999707 1499420 3413.33 + 3.069 0.999756 1499493 4096.00 + 3.269 0.999780 1499530 4551.11 + 3.427 0.999805 1499567 5120.00 + 3.733 0.999829 1499603 5851.43 + 3.931 0.999854 1499640 6826.67 + 4.223 0.999878 1499677 8192.00 + 4.391 0.999890 1499695 9102.22 + 4.547 0.999902 1499713 10240.00 + 4.683 0.999915 1499731 11702.86 + 4.847 0.999927 1499750 13653.33 + 5.007 0.999939 1499768 16384.00 + 5.079 0.999945 1499777 18204.44 + 5.159 0.999951 1499786 20480.00 + 5.231 0.999957 1499795 23405.71 + 5.319 0.999963 1499805 27306.67 + 5.403 0.999969 1499815 32768.00 + 5.431 0.999973 1499818 36408.89 + 5.475 0.999976 1499823 40960.00 + 5.511 0.999979 1499827 46811.43 + 5.551 0.999982 1499832 54613.33 + 5.587 0.999985 1499837 65536.00 + 5.607 0.999986 1499839 72817.78 + 5.627 0.999988 1499841 81920.00 + 5.647 0.999989 1499843 93622.86 + 5.675 0.999991 1499846 109226.67 + 5.691 0.999992 1499848 131072.00 + 5.703 0.999993 1499849 145635.56 + 5.707 0.999994 1499850 163840.00 + 5.719 0.999995 1499851 187245.71 + 5.735 0.999995 1499853 218453.33 + 5.747 0.999996 1499854 262144.00 + 5.747 0.999997 1499854 291271.11 + 5.755 0.999997 1499855 327680.00 + 5.755 0.999997 1499855 374491.43 + 5.763 0.999998 1499856 436906.67 + 5.775 0.999998 1499857 524288.00 + 5.775 0.999998 1499857 582542.22 + 5.775 0.999998 1499857 655360.00 + 5.775 0.999999 1499857 748982.86 + 5.783 0.999999 1499858 873813.33 + 5.783 0.999999 1499858 1048576.00 + 5.783 0.999999 1499858 1165084.44 + 5.783 0.999999 1499858 1310720.00 + 5.783 0.999999 1499858 1497965.71 + 5.791 0.999999 1499859 1747626.67 + 5.791 1.000000 1499859 inf +#[Mean = 0.647, StdDeviation = 0.385] +#[Max = 5.788, Total count = 1499859] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 1799954 requests in 1.00m, 127.67MB read +Requests/sec: 29998.95 +Transfer/sec: 2.13MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-alloc-general.png new file mode 100644 index 000000000..eb2172ffd Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-cpu-general.png new file mode 100644 index 000000000..b088e0836 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-after-compact-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-general.png new file mode 100644 index 000000000..f10ba66a7 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-handle.png new file mode 100644 index 000000000..e7a2e5a1a Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-alloc-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-cpu-general.png new file mode 100644 index 000000000..1d5657043 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc-handle.png new file mode 100644 index 000000000..6788de3b2 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc.png new file mode 100644 index 000000000..ad9f27d03 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-alloc.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-cpu.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-cpu.png new file mode 100644 index 000000000..ef3f28a2f Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/GET-less-alloc-cpu.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-histogram.png new file mode 100644 index 000000000..4008cd581 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot-histogram.png new file mode 100644 index 000000000..d87c18294 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot.txt new file mode 100644 index 000000000..b6b1d6f52 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-hot.txt @@ -0,0 +1,137 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 0.579ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 584.88us 332.91us 3.10ms 59.05% + Req/Sec 62.96k 3.99k 76.44k 72.61% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 586.00us + 75.000% 0.87ms + 90.000% 1.03ms + 99.000% 1.18ms + 99.900% 1.70ms + 99.990% 2.60ms + 99.999% 2.98ms +100.000% 3.11ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.009 0.000000 5 1.00 + 0.127 0.100000 301160 1.11 + 0.243 0.200000 602358 1.25 + 0.358 0.300000 902423 1.43 + 0.472 0.400000 1201011 1.67 + 0.586 0.500000 1501055 2.00 + 0.643 0.550000 1651782 2.22 + 0.699 0.600000 1800739 2.50 + 0.755 0.650000 1950611 2.86 + 0.811 0.700000 2101923 3.33 + 0.866 0.750000 2250940 4.00 + 0.894 0.775000 2326936 4.44 + 0.921 0.800000 2400233 5.00 + 0.949 0.825000 2476516 5.71 + 0.976 0.850000 2551374 6.67 + 1.003 0.875000 2626238 8.00 + 1.016 0.887500 2662502 8.89 + 1.030 0.900000 2702000 10.00 + 1.043 0.912500 2738932 11.43 + 1.056 0.925000 2776351 13.33 + 1.069 0.937500 2814160 16.00 + 1.076 0.943750 2833435 17.78 + 1.082 0.950000 2850016 20.00 + 1.089 0.956250 2869117 22.86 + 1.096 0.962500 2888003 26.67 + 1.103 0.968750 2906939 32.00 + 1.107 0.971875 2917401 35.56 + 1.110 0.975000 2925033 40.00 + 1.114 0.978125 2934621 45.71 + 1.119 0.981250 2944134 53.33 + 1.128 0.984375 2953497 64.00 + 1.136 0.985938 2957930 71.11 + 1.147 0.987500 2962283 80.00 + 1.164 0.989062 2966984 91.43 + 1.188 0.990625 2971677 106.67 + 1.219 0.992188 2976370 128.00 + 1.238 0.992969 2978757 142.22 + 1.259 0.993750 2981058 160.00 + 1.285 0.994531 2983376 182.86 + 1.318 0.995313 2985713 213.33 + 1.359 0.996094 2988049 256.00 + 1.384 0.996484 2989242 284.44 + 1.412 0.996875 2990418 320.00 + 1.443 0.997266 2991561 365.71 + 1.483 0.997656 2992735 426.67 + 1.530 0.998047 2993898 512.00 + 1.558 0.998242 2994482 568.89 + 1.588 0.998437 2995063 640.00 + 1.620 0.998633 2995665 731.43 + 1.655 0.998828 2996242 853.33 + 1.705 0.999023 2996827 1024.00 + 1.739 0.999121 2997117 1137.78 + 1.778 0.999219 2997408 1280.00 + 1.825 0.999316 2997703 1462.86 + 1.899 0.999414 2997994 1706.67 + 1.990 0.999512 2998285 2048.00 + 2.039 0.999561 2998432 2275.56 + 2.093 0.999609 2998582 2560.00 + 2.147 0.999658 2998724 2925.71 + 2.205 0.999707 2998875 3413.33 + 2.263 0.999756 2999022 4096.00 + 2.289 0.999780 2999090 4551.11 + 2.331 0.999805 2999165 5120.00 + 2.379 0.999829 2999238 5851.43 + 2.439 0.999854 2999310 6826.67 + 2.517 0.999878 2999383 8192.00 + 2.567 0.999890 2999420 9102.22 + 2.611 0.999902 2999457 10240.00 + 2.651 0.999915 2999494 11702.86 + 2.695 0.999927 2999530 13653.33 + 2.735 0.999939 2999571 16384.00 + 2.755 0.999945 2999585 18204.44 + 2.779 0.999951 2999604 20480.00 + 2.801 0.999957 2999622 23405.71 + 2.823 0.999963 2999640 27306.67 + 2.851 0.999969 2999659 32768.00 + 2.865 0.999973 2999667 36408.89 + 2.879 0.999976 2999676 40960.00 + 2.895 0.999979 2999685 46811.43 + 2.917 0.999982 2999695 54613.33 + 2.941 0.999985 2999704 65536.00 + 2.949 0.999986 2999708 72817.78 + 2.961 0.999988 2999713 81920.00 + 2.969 0.999989 2999717 93622.86 + 2.993 0.999991 2999722 109226.67 + 3.017 0.999992 2999727 131072.00 + 3.027 0.999993 2999729 145635.56 + 3.035 0.999994 2999731 163840.00 + 3.045 0.999995 2999733 187245.71 + 3.059 0.999995 2999736 218453.33 + 3.069 0.999996 2999738 262144.00 + 3.073 0.999997 2999739 291271.11 + 3.077 0.999997 2999740 327680.00 + 3.083 0.999997 2999741 374491.43 + 3.087 0.999998 2999743 436906.67 + 3.091 0.999998 2999744 524288.00 + 3.091 0.999998 2999744 582542.22 + 3.095 0.999998 2999745 655360.00 + 3.095 0.999999 2999745 748982.86 + 3.097 0.999999 2999746 873813.33 + 3.101 0.999999 2999748 1048576.00 + 3.101 0.999999 2999748 1165084.44 + 3.101 0.999999 2999748 1310720.00 + 3.101 0.999999 2999748 1497965.71 + 3.101 0.999999 2999748 1747626.67 + 3.101 1.000000 2999748 2097152.00 + 3.101 1.000000 2999748 2330168.89 + 3.101 1.000000 2999748 2621440.00 + 3.101 1.000000 2999748 2995931.43 + 3.105 1.000000 2999749 3495253.33 + 3.105 1.000000 2999749 inf +#[Mean = 0.585, StdDeviation = 0.333] +#[Max = 3.104, Total count = 2999749] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 3599983 requests in 1.00m, 230.03MB read +Requests/sec: 59999.72 +Transfer/sec: 3.83MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-alloc-histogram.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-alloc-histogram.png new file mode 100644 index 000000000..aff1b92a6 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-alloc-histogram.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-allocations.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-allocations.txt new file mode 100644 index 000000000..c9ca2ca8c --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s-less-allocations.txt @@ -0,0 +1,137 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.243ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 570.71us 323.63us 2.92ms 58.34% + Req/Sec 63.08k 3.64k 77.89k 68.56% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 571.00us + 75.000% 848.00us + 90.000% 1.01ms + 99.000% 1.11ms + 99.900% 1.42ms + 99.990% 2.08ms + 99.999% 2.79ms +100.000% 2.92ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.009 0.000000 2 1.00 + 0.123 0.100000 300090 1.11 + 0.236 0.200000 601473 1.25 + 0.348 0.300000 901381 1.43 + 0.460 0.400000 1201771 1.67 + 0.571 0.500000 1500328 2.00 + 0.627 0.550000 1651665 2.22 + 0.682 0.600000 1800427 2.50 + 0.738 0.650000 1951864 2.86 + 0.793 0.700000 2100797 3.33 + 0.848 0.750000 2250252 4.00 + 0.876 0.775000 2326373 4.44 + 0.903 0.800000 2400030 5.00 + 0.931 0.825000 2476306 5.71 + 0.958 0.850000 2550171 6.67 + 0.986 0.875000 2627150 8.00 + 0.999 0.887500 2662789 8.89 + 1.013 0.900000 2701414 10.00 + 1.026 0.912500 2737370 11.43 + 1.040 0.925000 2776241 13.33 + 1.053 0.937500 2812566 16.00 + 1.060 0.943750 2832354 17.78 + 1.067 0.950000 2851916 20.00 + 1.074 0.956250 2870639 22.86 + 1.081 0.962500 2889491 26.67 + 1.088 0.968750 2907765 32.00 + 1.092 0.971875 2917812 35.56 + 1.095 0.975000 2925037 40.00 + 1.099 0.978125 2934991 45.71 + 1.103 0.981250 2944945 53.33 + 1.107 0.984375 2954508 64.00 + 1.109 0.985938 2959342 71.11 + 1.111 0.987500 2963943 80.00 + 1.113 0.989062 2968216 91.43 + 1.115 0.990625 2971902 106.67 + 1.118 0.992188 2976569 128.00 + 1.120 0.992969 2978762 142.22 + 1.123 0.993750 2981262 160.00 + 1.128 0.994531 2983549 182.86 + 1.138 0.995313 2985763 213.33 + 1.157 0.996094 2988066 256.00 + 1.170 0.996484 2989201 284.44 + 1.186 0.996875 2990357 320.00 + 1.206 0.997266 2991537 365.71 + 1.231 0.997656 2992717 426.67 + 1.263 0.998047 2993887 512.00 + 1.282 0.998242 2994449 568.89 + 1.305 0.998437 2995059 640.00 + 1.333 0.998633 2995622 731.43 + 1.372 0.998828 2996215 853.33 + 1.428 0.999023 2996792 1024.00 + 1.464 0.999121 2997085 1137.78 + 1.515 0.999219 2997382 1280.00 + 1.568 0.999316 2997671 1462.86 + 1.633 0.999414 2997965 1706.67 + 1.699 0.999512 2998259 2048.00 + 1.732 0.999561 2998408 2275.56 + 1.766 0.999609 2998552 2560.00 + 1.801 0.999658 2998698 2925.71 + 1.841 0.999707 2998845 3413.33 + 1.889 0.999756 2998990 4096.00 + 1.913 0.999780 2999062 4551.11 + 1.939 0.999805 2999136 5120.00 + 1.968 0.999829 2999209 5851.43 + 2.002 0.999854 2999283 6826.67 + 2.039 0.999878 2999356 8192.00 + 2.057 0.999890 2999394 9102.22 + 2.081 0.999902 2999429 10240.00 + 2.113 0.999915 2999465 11702.86 + 2.157 0.999927 2999502 13653.33 + 2.221 0.999939 2999538 16384.00 + 2.257 0.999945 2999557 18204.44 + 2.291 0.999951 2999576 20480.00 + 2.333 0.999957 2999593 23405.71 + 2.387 0.999963 2999612 27306.67 + 2.481 0.999969 2999630 32768.00 + 2.535 0.999973 2999639 36408.89 + 2.587 0.999976 2999648 40960.00 + 2.629 0.999979 2999657 46811.43 + 2.677 0.999982 2999668 54613.33 + 2.717 0.999985 2999676 65536.00 + 2.737 0.999986 2999680 72817.78 + 2.763 0.999988 2999685 81920.00 + 2.779 0.999989 2999689 93622.86 + 2.801 0.999991 2999694 109226.67 + 2.819 0.999992 2999699 131072.00 + 2.823 0.999993 2999701 145635.56 + 2.835 0.999994 2999703 163840.00 + 2.847 0.999995 2999705 187245.71 + 2.857 0.999995 2999709 218453.33 + 2.863 0.999996 2999710 262144.00 + 2.869 0.999997 2999711 291271.11 + 2.875 0.999997 2999712 327680.00 + 2.881 0.999997 2999713 374491.43 + 2.891 0.999998 2999715 436906.67 + 2.897 0.999998 2999716 524288.00 + 2.897 0.999998 2999716 582542.22 + 2.903 0.999998 2999717 655360.00 + 2.903 0.999999 2999717 748982.86 + 2.909 0.999999 2999718 873813.33 + 2.915 0.999999 2999719 1048576.00 + 2.915 0.999999 2999719 1165084.44 + 2.915 0.999999 2999719 1310720.00 + 2.915 0.999999 2999719 1497965.71 + 2.919 0.999999 2999720 1747626.67 + 2.919 1.000000 2999720 2097152.00 + 2.919 1.000000 2999720 2330168.89 + 2.919 1.000000 2999720 2621440.00 + 2.919 1.000000 2999720 2995931.43 + 2.921 1.000000 2999721 3495253.33 + 2.921 1.000000 2999721 inf +#[Mean = 0.571, StdDeviation = 0.324] +#[Max = 2.920, Total count = 2999721] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 3599945 requests in 1.00m, 230.02MB read +Requests/sec: 59999.16 +Transfer/sec: 3.83MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s.txt b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s.txt new file mode 100644 index 000000000..6852f6f18 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-60000-60s.txt @@ -0,0 +1,137 @@ +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 1.193ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 579.52us 330.73us 4.12ms 58.95% + Req/Sec 63.25k 3.80k 89.33k 68.37% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 580.00us + 75.000% 0.86ms + 90.000% 1.02ms + 99.000% 1.13ms + 99.900% 1.73ms + 99.990% 2.96ms + 99.999% 4.00ms +100.000% 4.12ms + + Detailed Percentile spectrum: + Value Percentile TotalCount 1/(1-Percentile) + + 0.009 0.000000 17 1.00 + 0.125 0.100000 300683 1.11 + 0.240 0.200000 601873 1.25 + 0.354 0.300000 902311 1.43 + 0.467 0.400000 1200624 1.67 + 0.580 0.500000 1501216 2.00 + 0.636 0.550000 1650316 2.22 + 0.693 0.600000 1802440 2.50 + 0.749 0.650000 1952157 2.86 + 0.805 0.700000 2102189 3.33 + 0.860 0.750000 2250196 4.00 + 0.888 0.775000 2326092 4.44 + 0.916 0.800000 2402216 5.00 + 0.943 0.825000 2476054 5.71 + 0.970 0.850000 2550301 6.67 + 0.997 0.875000 2624939 8.00 + 1.011 0.887500 2664070 8.89 + 1.024 0.900000 2700724 10.00 + 1.037 0.912500 2737459 11.43 + 1.051 0.925000 2777639 13.33 + 1.063 0.937500 2813053 16.00 + 1.070 0.943750 2833023 17.78 + 1.077 0.950000 2852404 20.00 + 1.083 0.956250 2869293 22.86 + 1.090 0.962500 2888213 26.67 + 1.097 0.968750 2907179 32.00 + 1.100 0.971875 2915392 35.56 + 1.104 0.975000 2926266 40.00 + 1.107 0.978125 2934752 45.71 + 1.111 0.981250 2946257 53.33 + 1.114 0.984375 2953707 64.00 + 1.116 0.985938 2957816 71.11 + 1.119 0.987500 2962730 80.00 + 1.123 0.989062 2967154 91.43 + 1.131 0.990625 2971740 106.67 + 1.147 0.992188 2976396 128.00 + 1.158 0.992969 2978748 142.22 + 1.172 0.993750 2981032 160.00 + 1.189 0.994531 2983418 182.86 + 1.210 0.995313 2985763 213.33 + 1.238 0.996094 2988036 256.00 + 1.258 0.996484 2989247 284.44 + 1.281 0.996875 2990381 320.00 + 1.315 0.997266 2991565 365.71 + 1.365 0.997656 2992721 426.67 + 1.436 0.998047 2993890 512.00 + 1.483 0.998242 2994481 568.89 + 1.534 0.998437 2995063 640.00 + 1.593 0.998633 2995651 731.43 + 1.663 0.998828 2996233 853.33 + 1.745 0.999023 2996819 1024.00 + 1.795 0.999121 2997110 1137.78 + 1.858 0.999219 2997404 1280.00 + 1.935 0.999316 2997696 1462.86 + 2.013 0.999414 2997992 1706.67 + 2.109 0.999512 2998287 2048.00 + 2.159 0.999561 2998428 2275.56 + 2.207 0.999609 2998577 2560.00 + 2.271 0.999658 2998725 2925.71 + 2.347 0.999707 2998868 3413.33 + 2.415 0.999756 2999015 4096.00 + 2.457 0.999780 2999087 4551.11 + 2.513 0.999805 2999161 5120.00 + 2.577 0.999829 2999235 5851.43 + 2.691 0.999854 2999307 6826.67 + 2.843 0.999878 2999380 8192.00 + 2.915 0.999890 2999418 9102.22 + 2.971 0.999902 2999454 10240.00 + 3.045 0.999915 2999491 11702.86 + 3.123 0.999927 2999527 13653.33 + 3.199 0.999939 2999563 16384.00 + 3.261 0.999945 2999582 18204.44 + 3.369 0.999951 2999600 20480.00 + 3.447 0.999957 2999618 23405.71 + 3.549 0.999963 2999637 27306.67 + 3.651 0.999969 2999655 32768.00 + 3.705 0.999973 2999664 36408.89 + 3.757 0.999976 2999673 40960.00 + 3.811 0.999979 2999682 46811.43 + 3.869 0.999982 2999692 54613.33 + 3.923 0.999985 2999701 65536.00 + 3.947 0.999986 2999705 72817.78 + 3.975 0.999988 2999710 81920.00 + 3.993 0.999989 2999714 93622.86 + 4.009 0.999991 2999719 109226.67 + 4.019 0.999992 2999724 131072.00 + 4.029 0.999993 2999726 145635.56 + 4.037 0.999994 2999728 163840.00 + 4.045 0.999995 2999730 187245.71 + 4.059 0.999995 2999733 218453.33 + 4.069 0.999996 2999735 262144.00 + 4.073 0.999997 2999736 291271.11 + 4.077 0.999997 2999737 327680.00 + 4.083 0.999997 2999738 374491.43 + 4.093 0.999998 2999740 436906.67 + 4.099 0.999998 2999742 524288.00 + 4.099 0.999998 2999742 582542.22 + 4.099 0.999998 2999742 655360.00 + 4.099 0.999999 2999742 748982.86 + 4.107 0.999999 2999743 873813.33 + 4.111 0.999999 2999744 1048576.00 + 4.111 0.999999 2999744 1165084.44 + 4.111 0.999999 2999744 1310720.00 + 4.111 0.999999 2999744 1497965.71 + 4.115 0.999999 2999745 1747626.67 + 4.115 1.000000 2999745 2097152.00 + 4.115 1.000000 2999745 2330168.89 + 4.115 1.000000 2999745 2621440.00 + 4.115 1.000000 2999745 2995931.43 + 4.119 1.000000 2999746 3495253.33 + 4.119 1.000000 2999746 inf +#[Mean = 0.580, StdDeviation = 0.331] +#[Max = 4.116, Total count = 2999746] +#[Buckets = 27, SubBuckets = 2048] +---------------------------------------------------------- + 3599983 requests in 1.00m, 230.03MB read +Requests/sec: 59998.72 +Transfer/sec: 3.83MB diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-general.png new file mode 100644 index 000000000..396533d59 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-handle.png new file mode 100644 index 000000000..83b22cb8a Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-save-sstable.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-save-sstable.png new file mode 100644 index 000000000..25b5d8dc4 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-alloc-save-sstable.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-cpu-general.png new file mode 100644 index 000000000..e68b690c2 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-alloc-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-alloc-general.png new file mode 100644 index 000000000..76394bf71 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-alloc-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-cpu-general.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-cpu-general.png new file mode 100644 index 000000000..ccb8d7bba Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-hot-cpu-general.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc-handle.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc-handle.png new file mode 100644 index 000000000..4c0b81448 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc-handle.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc.png new file mode 100644 index 000000000..eb087b30a Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-alloc.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-cpu.png b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-cpu.png new file mode 100644 index 000000000..e1dfe82d0 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/PUT-less-alloc-cpu.png differ diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/report.md b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/report.md new file mode 100644 index 000000000..0e8a7d4be --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/reports/stage1new/report.md @@ -0,0 +1,328 @@ +# Stage 1 + +Новый отчёт с использованием новых скриптов для создания запросов. Оказалось, что прошлая версия не могла обеспечить большой +RPS из-за долгой генерации тела запроса. То есть wrk физически не мог пропихнуть нужное количество запросов. + +- [PUT](#PUT) + * [Latency](#latency) + * [CPU profiling](#cpu-profiling) + * [Memory allocations](#memory-allocations) +- [PUT hot](#PUT-hot) + * [Latency](#latency-1) + * [CPU profiling](#cpu-profiling-1) + * [Memory allocations](#memory-allocations-1) +- [GET (после PUT)](#Get-после-PUT) + * [Latency](#latency-2) + * [CPU profiling](#cpu-profiling-2) + * [Memory allocations](#memory-allocations-2) +- [GET (после compaction)](#get-после-compaction) + * [Latency](#latency-3) + * [CPU profiling](#cpu-profiling-3) + * [Memory allocations](#memory-allocations-3) +- [Оптимизация аллокаций](#оптимизация-аллокаций) + * [Put](#put-1) + * [Get](#get) + +## PUT + +### Latency + +Стабильная нагрузка 60000 запросов в секунду. + +Результаты замера ([PUT-60000-60s.txt](PUT-60000-60s.txt)): + +``` + Thread calibration: mean lat.: 1.193ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 579.52us 330.73us 4.12ms 58.95% + Req/Sec 63.25k 3.80k 89.33k 68.37% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 580.00us + 75.000% 0.86ms + 90.000% 1.02ms + 99.000% 1.13ms + 99.900% 1.73ms + 99.990% 2.96ms + 99.999% 4.00ms +100.000% 4.12ms +``` + +![PUT-60000-60s-histogram.png](PUT-60000-60s-histogram.png) + +На графике хорошо заметно, что, до 99-го и после 99.999-го перцентилей есть плато. Вероятнее всего часть ресурсов было отнято jit +компиляцией, после которой запросы стали обрабатываться быстрее. + +### Cpu profiling + +![PUT-cpu-profiling](PUT-cpu-general.png) + +На графике профилирования cpu видно, что немалую часть времени заняла jit компиляция - примерно 10% всего процессорного +времени. + +При этом часть времени обработки запроса занимает запись ответа - почти 40% процессорного времени. +Примерно столько же занимает сама обработка запроса. +Флаш занял лишь 3% времени. + +### Memory allocations + +![PUT-alloc-profiling](PUT-alloc-general.png) + +Больше всего памяти выделяется при записи entry в базу 34%: + +![PUT-alloc-handle](PUT-alloc-handle.png) + +1. Преобразование тела запроса в MemorySegment +2. Создание объекта Response +3. Преобразование id в MemorySegment +4. Создание ноды в ConcurrentHashMap + +Явное место для оптимизации - перестать каждый раз заново создавать объект Response и возвращать заранее созданный. +Но создание константы не даст прироста, а даже скорее всего приведёт к лишним аллокациям, так как при отправке запроса\ +(метод `HttpSession::sendResponse`) добавляется заголовок `"Connection: Keep-Alive"` или `"Connection: close"`. +Заголовки добавляются инкрементально, то есть с каждым новым вызовом метода `HttpSession::sendResponse` массив +заголовков увеличивается в размере (если массив заполнен, то происходит его копирование в массив побольше (+4)). +При 60k rps будет ~15k копирований массива в секунду. При этом каждое новое копирование будет линейно больше +предыдущего. +Легко понять, что такие "улучшения" приведут к огромным утечкам памяти. + +В таком случае можно задать несколько констант с заголовком `"Connection: Keep-Alive"` или `"Connection: close"` +(разные константы для потокобезопасности, так как нам всё равно придётся проверять заголовок запроса и решать какой из +заголовков писать в response), создать свою реализацию HttpSession и переопределить метод `sendResponse`, +а также переопределить метод `HttpServer::createSession`. + +![PUT-alloc-save-sstable](PUT-alloc-save-sstable.png) + +При сохранении sstable выделяется заметное количество памяти при чтении индекс файла (readAllLines), +а также при инициализации BufferedOutputStream для записи в файл. + +Повторное чтение индекс файла можно заменить на постоянно хранящийся в памяти список sstable, +который заполняется при инициализации dao и просто модифицируется. + +## Put (hot) + +Проведём повторное тестирование и посмотрим, как будет вести себя система с уже прогретой jvm. + +### Latency + +[PUT-60000-60s-hot.txt](PUT-60000-60s-hot.txt): + +``` +Running 1m test @ http://localhost:8080 + 1 threads and 1 connections + Thread calibration: mean lat.: 0.579ms, rate sampling interval: 10ms + Thread Stats Avg Stdev Max +/- Stdev + Latency 584.88us 332.91us 3.10ms 59.05% + Req/Sec 62.96k 3.99k 76.44k 72.61% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 586.00us + 75.000% 0.87ms + 90.000% 1.03ms + 99.000% 1.18ms + 99.900% 1.70ms + 99.990% 2.60ms + 99.999% 2.98ms +100.000% 3.11ms +``` + +![PUT-60000-60s-hot-histogram.png](PUT-60000-60s-hot-histogram.png) + +В целом график похож на график теста с холодной jvm, но задержки 99.999 перцентиля стали меньше на 1ms. + +### Cpu profiling + +То, что jvm была прогрета подтверждает и график профилирования CPU - jit компиляция заняла лишь ~3% вместо 10%: + +![PUT-hot-cpu-general.png](PUT-hot-cpu-general.png) + +### Memory allocations + +С точки зрения аллокаций в целом ничего не поменялось: + +![PUT-hot-alloc-general.png](PUT-hot-alloc-general.png) + +## Get (после PUT) + +### Latency + +GET запросы отправлялись на прогретой jvm после PUT запросов. + +Экспериментальным путём было выявлено, что стабильной нагрузкой ниже точки разладки является 30000 запросов в секунду. + +Результаты замера ([GET-30000-60s](GET-30000-60s.txt)): + +``` + Thread Stats Avg Stdev Max +/- Stdev + Latency 646.75us 384.88us 5.79ms 65.27% + Req/Sec 31.61k 2.34k 40.11k 65.32% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 638.00us + 75.000% 0.93ms + 90.000% 1.09ms + 99.000% 1.72ms + 99.900% 1.98ms + 99.990% 4.51ms + 99.999% 5.66ms +100.000% 5.79ms +``` + +![GET-30000-60s-histogram.png](GET-30000-60s-histogram.png) + +На графике хорошо заметно, что есть 2 скачка - около 94% и 99.95%. Причём второй очень заметный. + +### Cpu profiling + +![GET-cpu-profiling](GET-cpu-general.png) + +На графике профилирования cpu видно, что большую часть времени занимают операции сравнения MemorySegment - 38% времени. +30% - `MemorySegment.mismatch`. + +Операции записи ответа занимают всего 17%. + +Такое большое количество операций сравнения обусловлено тем, что мы итерирумся через список sstable и выполняем +бинарный поиск над каждой sstable. Так как ключи у нас линейно растут с каждым запросом (`key${counter}`), то получается, +что в самых свежих sstable хранятся наиболее большие ключи (относительно `${counter}`), а так как мы итерироваться начинаем +как раз с самых свежих, то получается, что первые запросы выполняются дольше, чем последние. Тут также имеет влияние page cache - + очевидно, что большое количество файлов + прыжки по ним могут легко его убить. + +### Memory allocations + +![GET-alloc-profiling](GET-alloc-general.png) + +Почти 30% аллокаций приходится на парсинг запроса - `parseRequest` и `Utf8.read`. + +![GET-alloc-handle](GET-alloc-handle.png) + +1. На чтение entity уходит 12% памяти. +2. Те же 12% идут на преобразование id в MemorySegment. +3. Создание инстанса Response занимает 13%. +4. Копирование MemorySegment в byte[] для записи в Response. + +Уменьшить количество аллокаций можно засчёт своей реализации Response - принимать в качестве тела MemorySegment +и переопределить метод `toBytes`. +Это позволит избежать лишнего копирования MemorySegment в byte[]. + +## Get (после compaction) + +### Latency + +Результаты замера ([GET-30000-60s-aftercompact.txt](GET-30000-60s-aftercompact.txt)): + +``` + Thread Stats Avg Stdev Max +/- Stdev + Latency 1.01ms 552.03us 3.15ms 59.59% + Req/Sec 31.58k 1.76k 39.33k 77.04% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 1.01ms + 75.000% 1.45ms + 90.000% 1.79ms + 99.000% 2.01ms + 99.900% 2.11ms + 99.990% 2.54ms + 99.999% 3.03ms +100.000% 3.15ms +``` + +![GET-30000-60s-after-compaction-histogram.png](GET-30000-60s-aftercompact-histogram.png) + +На графике хорошо заметно, что в целом latency стала более ровной - появилось плато до 99.97%, но есть после него +также есть скачок. Возможно, это связано page cache. + +### Cpu profiling + +![GET-after-compact-cpu-general.png](GET-after-compact-cpu-general.png) + +Теперь весь поиск Entity занимает 17%, а запись ответа 43%. Очевидно, что бинарный поиск по одному файлу оказался +эффективнее. + +30% времени занимает чтение из сокета. + +### Memory allocations + +![GET-after-compact-alloc-general.png](GET-after-compact-alloc-general.png) + +C точки зрения аллокаций в целом ситуация осталась такой же. + +## Оптимизация аллокаций + +### Put + +[Старые показатели](#latency) + +[PUT-60000-60s-less-allocations.txt](PUT-60000-60s-less-allocations.txt) +``` + Thread Stats Avg Stdev Max +/- Stdev + Latency 570.71us 323.63us 2.92ms 58.34% + Req/Sec 63.08k 3.64k 77.89k 68.56% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 571.00us + 75.000% 848.00us + 90.000% 1.01ms + 99.000% 1.11ms + 99.900% 1.42ms + 99.990% 2.08ms + 99.999% 2.79ms +100.000% 2.92ms +``` +![PUT-60000-60s-less-alloc-histogram.png](PUT-60000-60s-less-alloc-histogram.png) + +Показатели улучшились. Возможно, это связано с рефакторингом кода (статические методы, final переменные), который +позволил компилятору выполнить дополнительные оптимизации в коде. + +В целом график остался прежним: до 99-го и после 99.999-го перцентилей есть плато. Вероятнее всего часть ресурсов было отнято jit +компиляцией, после которой запросы стали обрабатываться быстрее. + +![PUT-less-alloc-cpu.png](PUT-less-alloc-cpu.png) + +С точки зрения затрат по cpu изменений практически нет. + +![PUT-less-alloc-alloc.png](PUT-less-alloc-alloc.png) + +Аллокации требуемые для парсинга запроса библиотекой one-nio не изменились, а вот при обработке запроса их стало меньше: + +**Старая реализация:** +![PUT-alloc-handle](PUT-alloc-handle.png) + +**Новая реализация:** +![PUT-less-alloc-alloc-handle.png](PUT-less-alloc-alloc-handle.png) + +Как можно увидеть - аллокаций Response больше нет. + +### Get + +[GET-30000-60s-less-allocations.txt](GET-30000-60s-less-allocations.txt) +``` + Thread Stats Avg Stdev Max +/- Stdev + Latency 641.45us 380.09us 4.45ms 64.97% + Req/Sec 31.60k 2.24k 38.90k 66.83% + Latency Distribution (HdrHistogram - Recorded Latency) + 50.000% 633.00us + 75.000% 0.92ms + 90.000% 1.09ms + 99.000% 1.70ms + 99.900% 1.95ms + 99.990% 4.03ms + 99.999% 4.42ms +100.000% 4.45ms +``` + +В целом график остался прежним: + +![GET-30000-60s-less-allocations-histrogram.png](GET-30000-60s-less-allocations-histrogram.png) + +С точки зрения потребления cpu ничего не поменялось: + +![GET-less-alloc-cpu.png](GET-less-alloc-cpu.png) + +Аллокации требуемые для парсинга запроса библиотекой one-nio не изменились, а вот при обработке запроса их стало меньше - +исчезли копирования значения Entry из MemorySegment в byte[]: + +![GET-less-alloc-alloc.png](GET-less-alloc-alloc.png) + +**Старая реализация:** + +![GET-alloc-handle](GET-alloc-handle.png) + +**Новая реализация:** + +![PUT-less-alloc-alloc-handle.png](PUT-less-alloc-alloc-handle.png) + diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET-new.lua b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET-new.lua new file mode 100644 index 000000000..ab26c5843 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET-new.lua @@ -0,0 +1,17 @@ +--- +--- Created by vitekkor. +--- DateTime: 17.02.2024 19:06 +--- + +host = "localhost" +port = 8080 + +counter = 0 + +request = function() + math.randomseed(counter) + path = "/v0/entity?id=key" .. counter + method = "GET" + counter = counter + 1 + return wrk.format(method, path) +end diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET.lua b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET.lua new file mode 100644 index 000000000..a63fa6840 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-GET.lua @@ -0,0 +1,30 @@ +--- +--- Created by vitekkor. +--- DateTime: 17.02.2024 19:06 +--- + +host = "localhost" +port = 8080 + +counter = 0 + +local charset = {} do -- [0-9a-zA-Z] + for c = 48, 57 do table.insert(charset, string.char(c)) end + for c = 65, 90 do table.insert(charset, string.char(c)) end + for c = 97, 122 do table.insert(charset, string.char(c)) end +end + +local function randomString(length) + if not length or length <= 0 then + return '' + end + return randomString(length - 1) .. charset[math.random(1, #charset)] +end + +request = function() + math.randomseed(counter) + path = "/v0/entity?id=" .. randomString(math.random(1, 10)) + method = "GET" + counter = counter + 100 + return wrk.format(method, path) +end diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT-new.lua b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT-new.lua new file mode 100644 index 000000000..40555d331 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT-new.lua @@ -0,0 +1,18 @@ +--- +--- Created by vitekkor. +--- DateTime: 17.02.2024 19:06 +--- + +host = "localhost" +port = 8080 + +counter = 0 + +request = function() + path = "/v0/entity?id=key" .. counter + method = "PUT" + math.randomseed(counter) + body = "value" .. counter + counter = counter + 1 + return wrk.format(method, path, nil, body) +end diff --git a/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT.lua b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT.lua new file mode 100644 index 000000000..3f4c9df19 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/viktorkorotkikh/scripts/stage-1-PUT.lua @@ -0,0 +1,41 @@ +--- +--- Created by vitekkor. +--- DateTime: 17.02.2024 19:06 +--- + +host = "localhost" +port = 8080 + +counter = 0 + +local charset = {} do -- [0-9a-zA-Z] + for c = 48, 57 do table.insert(charset, string.char(c)) end + for c = 65, 90 do table.insert(charset, string.char(c)) end + for c = 97, 122 do table.insert(charset, string.char(c)) end +end + +local function randomString(length) + if not length or length <= 0 then + return '' + end + return randomString(length - 1) .. charset[math.random(1, #charset)] +end + +function generateRandomBytes(length) + local result = {} + for i = 1, length do + result[i] = math.random(0, 255) + end + + return table.concat(result) +end + +request = function() + math.randomseed(counter) + path = "/v0/entity?id=" .. randomString(math.random(1, 10)) + method = "PUT" + math.randomseed(counter) + body = generateRandomBytes(1024) + counter = counter + 100 + return wrk.format(method, path, nil, body) +end