diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 326cc2c9f..000000000 --- a/.gitignore +++ /dev/null @@ -1,49 +0,0 @@ -# Ignore Gradle project-specific cache directory -.gradle - -# Ignore Gradle build output directory -build - -.idea - -# Compiled source # -################### -*.com -*.class -*.dll -*.exe -*.o -*.so - -# Packages # -############ -# it's better to unpack these files and commit the raw source -# git has its own built in compression methods -*.7z -*.dmg -*.gz -*.iso -*.jar -*.rar -*.tar -*.zip - -# Logs and databases # -###################### -*.log -*.sql -*.sqlite - -# OS generated files # -###################### -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Ignoring database # -###################### -db diff --git a/build.gradle b/build.gradle deleted file mode 100644 index c0d11cf4a..000000000 --- a/build.gradle +++ /dev/null @@ -1,76 +0,0 @@ -plugins { - id 'java' - id 'application' - id 'net.ltgt.errorprone' version '3.1.0' - id 'checkstyle' -} - -java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 -} - -configurations.checkstyle { - resolutionStrategy.capabilitiesResolution.withCapability("com.google.collections:google-collections") { - select("com.google.guava:guava:0") - } -} - -repositories { - mavenCentral() -} - -dependencies { - // Checks - errorprone 'com.google.errorprone:error_prone_core:2.15.0' - checkstyle 'com.puppycrawl.tools:checkstyle:10.13.0' - - // Errorprone annotations for solve with warnings - implementation group: 'com.google.errorprone', name: 'error_prone_annotations', version: '2.15.0' - - // Logging - implementation 'org.slf4j:slf4j-api:2.0.11' - implementation 'ch.qos.logback:logback-classic:1.4.14' - // As one-nio uses log4j directly - implementation 'org.slf4j:log4j-over-slf4j:2.0.11' - - // Annotations for better code documentation - implementation 'com.google.code.findbugs:jsr305:3.0.2' - - // Our beloved one-nio - implementation 'ru.odnoklassniki:one-nio:1.7.1' - - // JUnit Jupiter test framework - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1' -} - -test { - maxHeapSize = "128m" - useJUnitPlatform() - jvmArgs += ["--enable-preview"] -} - -checkstyle { - configFile = new File("checkstyle.xml") - checkstyleTest.enabled = false -} - -compileJava { - options.compilerArgs += ["--enable-preview"] - - // Enforce errors - options.compilerArgs += ["-Werror"] -} - -compileTestJava { - options.compilerArgs += ["--enable-preview"] -} - -application { - mainClass = 'ru.vk.itmo.test.reference.ReferenceService' - applicationDefaultJvmArgs = ['-Xmx128m', '--enable-preview', '-Xlog:gc'] - // + ['-XX:+UseSerialGC'] -// -} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/ChunkedResponse.java b/src/main/java/ru/vk/itmo/test/osokindm/ChunkedResponse.java new file mode 100644 index 000000000..fdcf981f6 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/ChunkedResponse.java @@ -0,0 +1,26 @@ +package ru.vk.itmo.test.osokindm; + +import one.nio.http.Response; +import ru.vk.itmo.test.osokindm.dao.Entry; + +import java.lang.foreign.MemorySegment; +import java.util.Iterator; + +public class ChunkedResponse extends Response { + + private static final String CHUNKED_HEADER = "Transfer-Encoding: chunked"; + private static final String CONTENT_TYPE_HEADER = "Content-Type: result-range"; + private final Iterator> rangeResult; + + public ChunkedResponse(String resultCode, Iterator> rangeResult) { + super(resultCode); + super.addHeader(CHUNKED_HEADER); + super.addHeader(CONTENT_TYPE_HEADER); + this.rangeResult = rangeResult; + } + + public Iterator> getResultIterator() { + return rangeResult; + } + + } diff --git a/src/main/java/ru/vk/itmo/test/osokindm/CustomHttpSession.java b/src/main/java/ru/vk/itmo/test/osokindm/CustomHttpSession.java new file mode 100644 index 000000000..57a1e78e1 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/CustomHttpSession.java @@ -0,0 +1,59 @@ +package ru.vk.itmo.test.osokindm; + +import one.nio.http.HttpServer; +import one.nio.http.HttpSession; +import one.nio.http.Response; +import one.nio.net.Socket; +import ru.vk.itmo.test.osokindm.dao.Entry; + +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; + +public class CustomHttpSession extends HttpSession { + + private static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(StandardCharsets.UTF_8); + private static final byte[] DELIMITER = "\n".getBytes(StandardCharsets.UTF_8); + private static final byte[] ZERO = "0".getBytes(StandardCharsets.UTF_8); + + public CustomHttpSession(Socket socket, HttpServer server) { + super(socket, server); + } + + @Override + protected void writeResponse(Response response, boolean includeBody) throws IOException { + if (response instanceof ChunkedResponse) { + super.writeResponse(response, false); + Iterator> iterator = ((ChunkedResponse) response).getResultIterator(); + while (iterator.hasNext()) { + writeChunk(iterator.next()); + } + + super.write(ZERO, 0, ZERO.length); + super.write(CHUNK_SEPARATOR, 0, CHUNK_SEPARATOR.length); + super.write(CHUNK_SEPARATOR, 0, CHUNK_SEPARATOR.length); + super.close(); + } else { + super.writeResponse(response, includeBody); + } + } + + private void writeChunk(Entry entry) throws IOException { + byte[] keyBytes = entry.key().toArray(ValueLayout.JAVA_BYTE); + byte[] valueBytes = entry.value().toArray(ValueLayout.JAVA_BYTE); + int entryLength = keyBytes.length + valueBytes.length + DELIMITER.length; + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + entryLength + CHUNK_SEPARATOR.length * 2); + buffer.put(Integer.toHexString(entryLength).getBytes(StandardCharsets.UTF_8)); + buffer.put(CHUNK_SEPARATOR); + buffer.put(entry.key().toArray(ValueLayout.JAVA_BYTE)); + buffer.put(DELIMITER); + buffer.put(entry.value().toArray(ValueLayout.JAVA_BYTE)); + buffer.put(CHUNK_SEPARATOR); + + super.write(buffer.array(), 0, buffer.position()); + } + +} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/DaoWrapper.java b/src/main/java/ru/vk/itmo/test/osokindm/DaoWrapper.java index 16ac7e602..329aed667 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/DaoWrapper.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/DaoWrapper.java @@ -1,14 +1,15 @@ package ru.vk.itmo.test.osokindm; import one.nio.http.Request; -import ru.vk.itmo.dao.BaseEntry; import ru.vk.itmo.dao.Config; -import ru.vk.itmo.dao.Entry; +import ru.vk.itmo.test.osokindm.dao.BaseEntry; +import ru.vk.itmo.test.osokindm.dao.Entry; import ru.vk.itmo.test.osokindm.dao.ReferenceDao; import java.io.IOException; import java.lang.foreign.MemorySegment; import java.nio.charset.StandardCharsets; +import java.util.Iterator; public class DaoWrapper { @@ -23,15 +24,21 @@ public Entry get(String id) { return storage.get(key); } - public void delete(String id) { + public Iterator> get(String start, String end) { + MemorySegment startKey = getMemorySegment(start); + MemorySegment endKey = getMemorySegment(end); + return storage.get(startKey, endKey); + } + + public void delete(String id, long timestamp) { MemorySegment key = getMemorySegment(id); - storage.upsert(new BaseEntry<>(key, null)); + storage.upsert(new BaseEntry<>(key, null, timestamp)); } - public void upsert(String id, Request request) { + public void upsert(String id, Request request, long timestamp) { MemorySegment key = getMemorySegment(id); MemorySegment value = getMemorySegment(request.getBody()); - storage.upsert(new BaseEntry<>(key, value)); + storage.upsert(new BaseEntry<>(key, value, timestamp)); } public void stop() throws IOException { diff --git a/src/main/java/ru/vk/itmo/test/osokindm/HttpServerImpl.java b/src/main/java/ru/vk/itmo/test/osokindm/HttpServerImpl.java index 48dce6d25..3525bb455 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/HttpServerImpl.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/HttpServerImpl.java @@ -5,6 +5,8 @@ import one.nio.http.HttpSession; import one.nio.http.Request; import one.nio.http.Response; +import one.nio.net.Socket; +import one.nio.server.RejectedSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,6 +20,7 @@ public class HttpServerImpl extends HttpServer { private static final String ID_REQUEST = "id="; + private static final String START_REQUEST = "start="; private static final long KEEP_ALIVE_TIME = 60L; private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerImpl.class); private final ThreadPoolExecutor requestWorkers; @@ -34,6 +37,11 @@ public HttpServerImpl(HttpServerConfig config) throws IOException { requestWorkers.prestartAllCoreThreads(); } + @Override + public HttpSession createSession(Socket socket) throws RejectedSessionException { + return new CustomHttpSession(socket, this); + } + @Override public synchronized void stop() { super.stop(); @@ -52,7 +60,8 @@ public synchronized void stop() { @Override public void handleRequest(Request request, HttpSession session) { String id = request.getParameter(ID_REQUEST); - if (id == null || id.isEmpty()) { + String start = request.getParameter(START_REQUEST); + if ((id == null || id.isEmpty()) && (start == null || start.isEmpty())) { try { handleDefault(request, session); } catch (IOException e) { diff --git a/src/main/java/ru/vk/itmo/test/osokindm/Node.java b/src/main/java/ru/vk/itmo/test/osokindm/Node.java index 8bba4c547..d0b6a6c3b 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/Node.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/Node.java @@ -3,9 +3,9 @@ import java.util.concurrent.atomic.AtomicInteger; public class Node { + private static final int MAX_ERRORS = 10; public final String address; public final int name; - private static final int MAX_ERRORS = 10; private final AtomicInteger errors; private volatile boolean isAlive; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/RendezvousRouter.java b/src/main/java/ru/vk/itmo/test/osokindm/RendezvousRouter.java index d81b2f1d8..0893d7930 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/RendezvousRouter.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/RendezvousRouter.java @@ -3,7 +3,9 @@ import one.nio.util.Hash; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.TreeMap; public class RendezvousRouter { @@ -32,4 +34,25 @@ public Node getNode(String key) { return maxHashNode; } + @SuppressWarnings("MixedMutabilityReturnType") + public List getNodes(String key, int nodeAmount) { + if (key == null) { + return Collections.emptyList(); + } + TreeMap sortedNodes = new TreeMap<>(); + for (Node node : nodes) { + int hash = Hash.murmur3(node.name + key); + sortedNodes.put(hash, node); + } + List selectedNodes = new ArrayList<>(); + int nodesNeeded = nodeAmount; + for (Node node : sortedNodes.values()) { + if (nodesNeeded == 0) break; + selectedNodes.add(node); + nodesNeeded--; + } + + return selectedNodes; + } + } diff --git a/src/main/java/ru/vk/itmo/test/osokindm/RequestHandler.java b/src/main/java/ru/vk/itmo/test/osokindm/RequestHandler.java new file mode 100644 index 000000000..4ced3af50 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/RequestHandler.java @@ -0,0 +1,156 @@ +package ru.vk.itmo.test.osokindm; + +import one.nio.http.HttpSession; +import one.nio.http.Request; +import one.nio.http.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.vk.itmo.test.osokindm.dao.Entry; + +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +public class RequestHandler { + private static final String TIMESTAMP_HEADER = "Request-timestamp"; + private static final String TIMESTAMP_HEADER_LC = "request-timestamp"; + private static final int EMPTY_TS = -1; + private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerImpl.class); + private final HttpClient client; + private DaoWrapper daoWrapper; + + public RequestHandler(HttpClient client) { + this.client = client; + } + + public void setDaoWrapper(DaoWrapper daoWrapper) { + this.daoWrapper = daoWrapper; + } + + public CompletableFuture processRequest( + Request request, + String id, + Node node, + long timestamp, + String selfUrl + ) { + if (node.address.equals(selfUrl)) { + return CompletableFuture.supplyAsync( + () -> handleRequestLocally(request, id, timestamp) + ); + } else { + return forwardRequestToNode(request, node, timestamp); + } + } + + public static void sendResponse(HttpSession session, Response latestResponse, AtomicInteger failures) { + try { + session.sendResponse(latestResponse); + } catch (IOException e) { + logFailure(e.getMessage(), failures); + } + } + + private static void logFailure(String e, AtomicInteger failures) { + failures.incrementAndGet(); + LOGGER.error(e); + } + + public Response handleRequestLocally(Request request, String id, long timestamp) { + switch (request.getMethod()) { + case Request.METHOD_GET -> { + Entry result = daoWrapper.get(id); + if (result == null) { + return new Response(Response.NOT_FOUND, Response.EMPTY); + } + + if (result.value() == null) { + Response response = new Response(Response.NOT_FOUND, longToBytes(result.timestamp())); + response.addHeader(TIMESTAMP_HEADER + ": " + result.timestamp()); + return response; + } + Response response = Response.ok(result.value().toArray(ValueLayout.JAVA_BYTE)); + response.addHeader(TIMESTAMP_HEADER + ": " + result.timestamp()); + return response; + } + case Request.METHOD_PUT -> { + daoWrapper.upsert(id, request, timestamp); + return new Response(Response.CREATED, Response.EMPTY); + } + case Request.METHOD_DELETE -> { + daoWrapper.delete(id, timestamp); + return new Response(Response.ACCEPTED, Response.EMPTY); + } + default -> { + return new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY); + } + } + } + + private CompletableFuture forwardRequestToNode(Request request, Node node, long timestamp) { + try { + return makeProxyRequest(request, node.address, timestamp); + } catch (TimeoutException e) { + node.captureError(); + LOGGER.error(node + " not responding", e); + return CompletableFuture.completedFuture(null); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return CompletableFuture.completedFuture(null); + + } catch (ExecutionException | IOException e) { + LOGGER.error(node + " not responding", e); + return CompletableFuture.completedFuture(null); + + } + } + + private CompletableFuture makeProxyRequest(Request request, String nodeAddress, long timestamp) + throws ExecutionException, InterruptedException, TimeoutException, IOException { + byte[] body = request.getBody(); + if (body == null) { + body = Response.EMPTY; + } + HttpRequest proxyRequest = HttpRequest + .newBuilder(URI.create(nodeAddress + request.getURI())) + .header(TIMESTAMP_HEADER, String.valueOf(timestamp)) + .method(request.getMethodName(), + HttpRequest.BodyPublishers.ofByteArray(body)) + .build(); + + return client + .sendAsync(proxyRequest, HttpResponse.BodyHandlers.ofByteArray()) + .thenApply(this::processedResponse); + } + + private Response processedResponse(HttpResponse response) { + List tsHeaders = response.headers().map().get(TIMESTAMP_HEADER_LC); + long timestamp = (tsHeaders == null || tsHeaders.isEmpty()) ? EMPTY_TS : Long.parseLong(tsHeaders.getFirst()); + + if (response.body().length == 0 && timestamp == EMPTY_TS) { + return new Response(String.valueOf(response.statusCode()), Response.EMPTY); + } + Response processedResponse = new Response(String.valueOf(response.statusCode()), response.body()); + processedResponse.addHeader(TIMESTAMP_HEADER + ": " + timestamp); + return processedResponse; + } + + private static byte[] longToBytes(long l) { + byte[] result = new byte[Long.BYTES]; + for (int i = Long.BYTES - 1; i >= 0; i--) { + result[i] = (byte) (l & 0xFF); + l >>= Byte.SIZE; + } + return result; + } + +} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/ServiceImpl.java b/src/main/java/ru/vk/itmo/test/osokindm/ServiceImpl.java index 4ca6ccd2b..c7a0db880 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/ServiceImpl.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/ServiceImpl.java @@ -1,6 +1,7 @@ package ru.vk.itmo.test.osokindm; import one.nio.http.HttpServerConfig; +import one.nio.http.HttpSession; import one.nio.http.Param; import one.nio.http.Path; import one.nio.http.Request; @@ -11,53 +12,70 @@ import ru.vk.itmo.Service; import ru.vk.itmo.ServiceConfig; import ru.vk.itmo.dao.Config; -import ru.vk.itmo.dao.Entry; import ru.vk.itmo.test.ServiceFactory; +import ru.vk.itmo.test.osokindm.dao.Entry; import java.io.IOException; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; -import java.net.URI; +import java.net.HttpURLConnection; import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.Iterator; +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static java.lang.Math.floor; +import static ru.vk.itmo.test.osokindm.RequestHandler.sendResponse; public class ServiceImpl implements Service { private static final int MEMORY_LIMIT_BYTES = 8 * 1024 * 1024; private static final int CONNECTION_TIMEOUT_MS = 250; private static final String DEFAULT_PATH = "/v0/entity"; + private static final String RANGE_PATH = "/v0/entities"; + private static final String TIMESTAMP_HEADER = "Request-timestamp"; + private static final String WRONG_ACK = "wrong 'ack' value"; private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerImpl.class); + private static final Response badIdResponse + = new Response(Response.BAD_REQUEST, "Invalid id".getBytes(StandardCharsets.UTF_8)); + private static final Response wrongAckResponse + = new Response(Response.BAD_REQUEST, WRONG_ACK.getBytes(StandardCharsets.UTF_8)); private final ServiceConfig config; + private final Executor responseExecutor; + private final RequestHandler requestHandler; private RendezvousRouter router; private DaoWrapper daoWrapper; private HttpServerImpl server; - private final HttpClient client; public ServiceImpl(ServiceConfig config) { this.config = config; - client = HttpClient.newBuilder() + HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.of(CONNECTION_TIMEOUT_MS, ChronoUnit.MILLIS)) .executor(Executors.newVirtualThreadPerTaskExecutor()) .build(); + responseExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + requestHandler = new RequestHandler(client); } @Override public CompletableFuture start() throws IOException { try { daoWrapper = new DaoWrapper(new Config(config.workingDir(), MEMORY_LIMIT_BYTES)); + requestHandler.setDaoWrapper(daoWrapper); router = new RendezvousRouter(config.clusterUrls()); server = new HttpServerImpl(createServerConfig(config.selfPort())); server.addRequestHandlers(this); server.start(); - LOGGER.debug("Server started"); + LOGGER.debug("Server started: " + config.selfUrl()); } catch (IOException e) { LOGGER.error("Error occurred while starting the server"); throw new IOException(e); @@ -77,76 +95,161 @@ public CompletableFuture stop() { } @Path(DEFAULT_PATH) - public Response entity(Request request, @Param(value = "id", required = true) String id) throws TimeoutException { + public void entity(Request request, HttpSession session, + @Param(value = "id", required = true) String id, + @Param(value = "ack") Integer ackNumber, + @Param(value = "from") Integer fromNumber) throws TimeoutException, IOException { if (id == null || id.isBlank()) { - return new Response(Response.BAD_REQUEST, "Invalid id".getBytes(StandardCharsets.UTF_8)); + session.sendResponse(badIdResponse); + return; } - Node node = router.getNode(id); - if (!node.isAlive()) { - return new Response(Response.SERVICE_UNAVAILABLE, "Node is unavailable".getBytes(StandardCharsets.UTF_8)); + if (handleTimestampHeader(request, session, id)) { + return; } - if (node.address.equals(config.selfUrl())) { - return handleRequestLocally(request, id); + int from = (fromNumber == null || fromNumber < 0) ? config.clusterUrls().size() : fromNumber; + int ack = (ackNumber == null || ackNumber < 0) ? calculateAck(from) : ackNumber; + + if (ack > from || ack == 0) { + session.sendResponse(wrongAckResponse); + return; } - return forwardRequestToNode(request, node); + List targetNodes = router.getNodes(id, from); + dispatchRequestsToNodes(request, session, targetNodes, id, ack, from); + } + + @Path(RANGE_PATH) + public void entities(HttpSession session, + @Param(value = "start", required = true) String start, + @Param(value = "end") String end) throws IOException { + if (start == null || start.isBlank()) { + session.sendResponse(badIdResponse); + return; + } + + responseExecutor.execute(() -> { + Iterator> it = daoWrapper.get(start, end); + + try { + session.sendResponse(new ChunkedResponse(Response.OK, it)); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + } + }); + } - private Response forwardRequestToNode(Request request, Node node) { + private boolean handleTimestampHeader(Request request, HttpSession session, String id) throws IOException { + String timestamp = request.getHeader(TIMESTAMP_HEADER + ": "); + if (timestamp == null || timestamp.isBlank()) { + return false; + } try { - LOGGER.debug("Request has been forwarded to {}", node.address); - return makeProxyRequest(request, node.address); - } catch (TimeoutException | IOException e) { - node.captureError(); - LOGGER.error(node + " not responding", e); - return new Response(Response.SERVICE_UNAVAILABLE, Response.EMPTY); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return new Response(Response.SERVICE_UNAVAILABLE, Response.EMPTY); - } catch (ExecutionException e) { - LOGGER.error(node + " not responding", e); - return new Response(Response.INTERNAL_ERROR, Response.EMPTY); + long parsedTimestamp = Long.parseLong(timestamp); + session.sendResponse(requestHandler.handleRequestLocally(request, id, parsedTimestamp)); + } catch (NumberFormatException e) { + session.sendResponse(new Response(Response.BAD_REQUEST, Response.EMPTY)); } + return true; } - private Response handleRequestLocally(Request request, String id) { - switch (request.getMethod()) { - case Request.METHOD_GET -> { - Entry result = daoWrapper.get(id); - if (result != null && result.value() != null) { - return Response.ok(result.value().toArray(ValueLayout.JAVA_BYTE)); - } - return new Response(Response.NOT_FOUND, Response.EMPTY); - } - case Request.METHOD_PUT -> { - daoWrapper.upsert(id, request); - return new Response(Response.CREATED, Response.EMPTY); - } - case Request.METHOD_DELETE -> { - daoWrapper.delete(id); - return new Response(Response.ACCEPTED, Response.EMPTY); - } - default -> { - return new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY); + private void dispatchRequestsToNodes( + Request request, + HttpSession session, + List targetNodes, + String id, + Integer ack, + Integer from + ) { + AtomicInteger successes = new AtomicInteger(); + AtomicInteger failures = new AtomicInteger(); + AtomicLong responseTime = new AtomicLong(); + AtomicReference latestResponse = new AtomicReference<>(); + AtomicBoolean responseSent = new AtomicBoolean(); + + for (Node node : targetNodes) { + if (!node.isAlive()) { + LOGGER.info("node is unreachable: " + node.address); } + long timestamp = System.currentTimeMillis(); + CompletableFuture futureResponse = + requestHandler.processRequest(request, id, node, timestamp, config.selfUrl()); + + futureResponse + .whenCompleteAsync((resp, ex) -> { + if (resp == null) { + unableToRespondCheck(session, failures, ack, from); + } else { + updateLatestResponse(resp, request.getMethod(), responseTime, latestResponse); + if (canEarlyResponse(resp, successes, ack, responseSent)) { + sendResponse(session, latestResponse.get(), failures); + } + } + }, responseExecutor) + .exceptionally(ex -> { + logFailure(ex.getMessage(), failures); + return null; + }); + } } - private Response makeProxyRequest(Request request, String nodeAddress) - throws ExecutionException, InterruptedException, TimeoutException, IOException { - byte[] body = request.getBody(); - if (body == null) { - body = Response.EMPTY; + private boolean canEarlyResponse(Response resp, AtomicInteger successes, Integer ack, AtomicBoolean responseSent) { + return responseIsGood(resp) + && successes.incrementAndGet() >= ack + && !responseSent.getAndSet(true); + } + + private void unableToRespondCheck(HttpSession session, AtomicInteger failures, Integer ack, Integer from) { + if (failures.incrementAndGet() > from - ack) { + String message = "Not enough replicas responded"; + LOGGER.info(message); + byte[] mes = message.getBytes(StandardCharsets.UTF_8); + sendResponse(session, new Response(Response.GATEWAY_TIMEOUT, mes), failures); } - HttpRequest proxyRequest = HttpRequest - .newBuilder(URI.create(nodeAddress + request.getURI())) - .method(request.getMethodName(), - HttpRequest.BodyPublishers.ofByteArray(body)) - .build(); - HttpResponse httpResponse = client.send(proxyRequest, HttpResponse.BodyHandlers.ofByteArray()); - return new Response(Integer.toString(httpResponse.statusCode()), httpResponse.body()); + } + + private boolean responseIsGood(Response response) { + // accepting all responses we can get from handleRequestLocally() + return response.getStatus() <= HttpURLConnection.HTTP_PARTIAL + || response.getStatus() == HttpURLConnection.HTTP_NOT_FOUND + || response.getStatus() == HttpURLConnection.HTTP_BAD_METHOD; + } + + private void logFailure(String e, AtomicInteger failures) { + failures.incrementAndGet(); + LOGGER.error(e); + } + + private long extractTimestampFromResponse(Response response) { + String timestamp = response.getHeaders()[2]; + if (timestamp == null) { + return -1; + } + String timestampValue = timestamp.substring(TIMESTAMP_HEADER.length() + 2).trim(); + return Long.parseLong(timestampValue); + } + + private void updateLatestResponse( + Response response, + int method, + AtomicLong responseTime, + AtomicReference latestResponse + ) { + latestResponse.compareAndSet(null, response); + if (method == Request.METHOD_GET) { + long timestamp = extractTimestampFromResponse(response); + long currentResponseTime; + do { + currentResponseTime = responseTime.get(); + if (timestamp <= currentResponseTime) { + return; + } + } while (!responseTime.compareAndSet(currentResponseTime, timestamp)); + } + latestResponse.set(response); } private static HttpServerConfig createServerConfig(int port) { @@ -160,9 +263,15 @@ private static HttpServerConfig createServerConfig(int port) { return serverConfig; } - @ServiceFactory(stage = 3) - public static class Factory implements ServiceFactory.Factory { + private static int calculateAck(int clusterSize) { + if (clusterSize <= 2) { + return clusterSize; + } + return (int) floor(clusterSize * 0.75); + } + @ServiceFactory(stage = 6) + public static class Factory implements ServiceFactory.Factory { @Override public Service create(ServiceConfig config) { return new ServiceImpl(config); diff --git a/src/main/java/ru/vk/itmo/test/osokindm/TestServer.java b/src/main/java/ru/vk/itmo/test/osokindm/TestServer.java index 7dbbe5f62..e14ddca3d 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/TestServer.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/TestServer.java @@ -3,22 +3,51 @@ import ru.vk.itmo.ServiceConfig; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public final class TestServer { + private static final String LOCALHOST_PREFIX = "http://localhost:"; + private TestServer() { } - public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { - ServiceImpl service = new ServiceImpl(new ServiceConfig( - 8080, "http://localhost", - List.of("http://localhost"), - Path.of("/home/john/database/") - )); - service.start().get(); - } + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + //port -> url + Map nodes = new HashMap<>(); + int nodePort = 8080; + for (int i = 0; i < 3; i++) { + nodes.put(nodePort, LOCALHOST_PREFIX + nodePort); + nodePort += 10; + } + List clusterUrls = new ArrayList<>(nodes.values()); + List clusterConfs = new ArrayList<>(); + for (Map.Entry entry : nodes.entrySet()) { + int port = entry.getKey(); + String url = entry.getValue(); + Path path = Paths.get("tmp/db/" + port); + Files.createDirectories(path); + ServiceConfig serviceConfig = new ServiceConfig(port, + url, + clusterUrls, + path); + clusterConfs.add(serviceConfig); + } + + for (ServiceConfig serviceConfig : clusterConfs) { + ServiceImpl instance = new ServiceImpl(serviceConfig); + instance.start().get(1, TimeUnit.SECONDS); + } + } } diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/BaseEntry.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/BaseEntry.java new file mode 100644 index 000000000..284e3db5a --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/BaseEntry.java @@ -0,0 +1,11 @@ +package ru.vk.itmo.test.osokindm.dao; + +import java.nio.ByteBuffer; + +public record BaseEntry(D key, D value, long timestamp) implements Entry { + @Override + public String toString() { + return "{" + key + ":" + value + ":" + timestamp + "}"; + } + +} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/Dao.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/Dao.java new file mode 100644 index 000000000..d5a8d0b01 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/Dao.java @@ -0,0 +1,87 @@ +package ru.vk.itmo.test.osokindm.dao; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Iterator; + +public interface Dao> extends Closeable { + /** + * 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) + */ + Iterator get(D from, D to); + + /** + * Returns entry by key. Note: default implementation is far from optimal. + * @param key entry`s key + * @return entry + */ + default E get(D key) { + Iterator iterator = get(key, null); + if (!iterator.hasNext()) { + return null; + } + + E next = iterator.next(); + if (next.key().equals(key)) { + return next; + } + return null; + } + + /** + * Returns ordered iterator of all entries with keys from (inclusive). + * @param from lower bound of range (inclusive) + * @return entries with key >= from + */ + default Iterator allFrom(D from) { + return get(from, null); + } + + /** + * Returns ordered iterator of all entries with keys < to. + * @param to upper bound of range (exclusive) + * @return entries with key < to + */ + default Iterator allTo(D to) { + return get(null, to); + } + + /** + * Returns ordered iterator of all entries. + * @return all entries + */ + default Iterator all() { + return get(null, null); + } + + /** + * Inserts of replaces entry. + * @param entry element to upsert + */ + void upsert(E entry); + + /** + * Persists data (no-op by default). + */ + default void flush() throws IOException { + // Do nothing + } + + /** + * Compacts data (no-op by default). + */ + default void compact() throws IOException { + // Do nothing + } + + /* + * Releases Dao (calls flush by default). + */ + @Override + default void close() throws IOException { + flush(); + } +} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/Entry.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/Entry.java new file mode 100644 index 000000000..e54e9304a --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/Entry.java @@ -0,0 +1,10 @@ +package ru.vk.itmo.test.osokindm.dao; + +public interface Entry { + D key(); + + D value(); + + long timestamp(); + +} diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/LiveFilteringIterator.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/LiveFilteringIterator.java index 432c003f2..b5d90cd00 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/LiveFilteringIterator.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/LiveFilteringIterator.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.util.Iterator; import java.util.NoSuchElementException; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/MemTable.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/MemTable.java index 929397a21..88d634c71 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/MemTable.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/MemTable.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.util.Iterator; import java.util.NavigableMap; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/MergingEntryIterator.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/MergingEntryIterator.java index a126b6f78..fe51f9375 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/MergingEntryIterator.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/MergingEntryIterator.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.util.Iterator; import java.util.List; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/ReferenceDao.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/ReferenceDao.java index aa0077b0a..10dbf779a 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/ReferenceDao.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/ReferenceDao.java @@ -1,8 +1,6 @@ package ru.vk.itmo.test.osokindm.dao; import ru.vk.itmo.dao.Config; -import ru.vk.itmo.dao.Dao; -import ru.vk.itmo.dao.Entry; import java.io.IOException; import java.lang.foreign.Arena; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTable.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTable.java index f94821862..eb228499a 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTable.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTable.java @@ -1,8 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.BaseEntry; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.util.Collections; @@ -54,7 +51,7 @@ private long entryBinarySearch(final MemorySegment key) { while (low <= high) { final long mid = (low + high) >>> 1; final long midEntryOffset = entryOffset(mid); - final long midKeyLength = getLength(midEntryOffset); + final long midKeyLength = getLongValue(midEntryOffset); final int compare = MemorySegmentComparator.compare( data, @@ -82,7 +79,7 @@ private long entryOffset(final long entry) { entry * Long.BYTES); } - private long getLength(final long offset) { + private long getLongValue(final long offset) { return data.get( ValueLayout.OfLong.JAVA_LONG_UNALIGNED, offset); @@ -143,16 +140,21 @@ Entry get(final MemorySegment key) { // Skip key (will reuse the argument) long offset = entryOffset(entry); offset += Long.BYTES + key.byteSize(); + + // Extract timestamp + final long timestamp = getLongValue(offset); + offset += Long.BYTES; + // Extract value length - final long valueLength = getLength(offset); + final long valueLength = getLongValue(offset); if (valueLength == SSTables.TOMBSTONE_VALUE_LENGTH) { // Tombstone encountered - return new BaseEntry<>(key, null); + return new BaseEntry<>(key, null, timestamp); } else { // Get value offset += Long.BYTES; final MemorySegment value = data.asSlice(offset, valueLength); - return new BaseEntry<>(key, value); + return new BaseEntry<>(key, value, timestamp); } } @@ -179,25 +181,29 @@ public Entry next() { } // Read key length - final long keyLength = getLength(offset); + final long keyLength = getLongValue(offset); offset += Long.BYTES; // Read key final MemorySegment key = data.asSlice(offset, keyLength); offset += keyLength; + // Read timestamp + final long timestamp = getLongValue(offset); + offset += Long.BYTES; + // Read value length - final long valueLength = getLength(offset); + final long valueLength = getLongValue(offset); offset += Long.BYTES; // Read value if (valueLength == SSTables.TOMBSTONE_VALUE_LENGTH) { // Tombstone encountered - return new BaseEntry<>(key, null); + return new BaseEntry<>(key, null, timestamp); } else { final MemorySegment value = data.asSlice(offset, valueLength); offset += valueLength; - return new BaseEntry<>(key, value); + return new BaseEntry<>(key, value, timestamp); } } } diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTableWriter.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTableWriter.java index d9081c387..d9498d40c 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTableWriter.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/SSTableWriter.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -21,7 +19,7 @@ * {@code [offset0, offset1, ...]} * *

Data file {@code .data} contains serialized entries: - * {@code } + * {@code } * *

Tombstones are encoded as {@code valueLength} {@code -1} and no subsequent value. * @@ -136,6 +134,7 @@ private long writeEntry( final OutputStream os) throws IOException { final MemorySegment key = entry.key(); final MemorySegment value = entry.value(); + final long timestamp = entry.timestamp(); long result = 0L; // Key size @@ -146,6 +145,10 @@ private long writeEntry( writeSegment(key, os); result += key.byteSize(); + // timestamp + writeLong(timestamp, os); + result += Long.BYTES; + // Value size and possibly value if (value == null) { // Tombstone diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/TableSet.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/TableSet.java index 5c3e60623..ef405adad 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/TableSet.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/TableSet.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.util.ArrayList; import java.util.Collections; @@ -152,16 +150,14 @@ Entry get(final MemorySegment key) { // First check MemTable Entry result = memTable.get(key); if (result != null) { - // Transform tombstone - return swallowTombstone(result); + return result; } // Then check flushing if (flushingTable != null) { result = flushingTable.get(key); if (result != null) { - // Transform tombstone - return swallowTombstone(result); + return result; } } @@ -169,8 +165,7 @@ Entry get(final MemorySegment key) { for (final SSTable ssTable : ssTables) { result = ssTable.get(key); if (result != null) { - // Transform tombstone - return swallowTombstone(result); + return result; } } @@ -178,10 +173,6 @@ Entry get(final MemorySegment key) { return null; } - private static Entry swallowTombstone(final Entry entry) { - return entry.value() == null ? null : entry; - } - Entry upsert(final Entry entry) { return memTable.upsert(entry); } diff --git a/src/main/java/ru/vk/itmo/test/osokindm/dao/WeightedPeekingEntryIterator.java b/src/main/java/ru/vk/itmo/test/osokindm/dao/WeightedPeekingEntryIterator.java index 84350e86b..eacaaaf5b 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/dao/WeightedPeekingEntryIterator.java +++ b/src/main/java/ru/vk/itmo/test/osokindm/dao/WeightedPeekingEntryIterator.java @@ -1,7 +1,5 @@ package ru.vk.itmo.test.osokindm.dao; -import ru.vk.itmo.dao.Entry; - import java.lang.foreign.MemorySegment; import java.util.Iterator; import java.util.NoSuchElementException; diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/get_graph.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/get_graph.png new file mode 100644 index 000000000..21375d2b7 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/get_graph.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.html new file mode 100644 index 000000000..6c426c36a --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.html @@ -0,0 +1,3037 @@ + + + + + + + +

Allocation profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.png new file mode 100644 index 000000000..87c9fb67a Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.html new file mode 100644 index 000000000..472c5b7b1 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.html @@ -0,0 +1,3127 @@ + + + + + + + +

Allocation profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.png new file mode 100644 index 000000000..3abf0ca50 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_alloc_put.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.html new file mode 100644 index 000000000..6e8dd853e --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.html @@ -0,0 +1,4137 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.png new file mode 100644 index 000000000..aa0ca3256 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get2.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get2.png new file mode 100644 index 000000000..e4f735c22 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_get2.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_put.html new file mode 100644 index 000000000..7353525a7 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_cpu_put.html @@ -0,0 +1,7361 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.html new file mode 100644 index 000000000..121e0a2a1 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.html @@ -0,0 +1,651 @@ + + + + + + + +

Lock profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.png new file mode 100644 index 000000000..021b2d96f Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.html new file mode 100644 index 000000000..7795163ea --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.html @@ -0,0 +1,687 @@ + + + + + + + +

Lock profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.png new file mode 100644 index 000000000..31c344a4e Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_lock_put.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_put_cpu.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_put_cpu.png new file mode 100644 index 000000000..d21928eca Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/profile_put_cpu.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/put_4k.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/put_4k.png new file mode 100644 index 000000000..e5f284ee7 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/profiler/put_4k.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/result_hw4.md b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/result_hw4.md new file mode 100644 index 000000000..c8fa2aaf6 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw4/result_hw4.md @@ -0,0 +1,299 @@ +## Репликация +Требовалось реализовать поддержку хранения нескольких реплик данных в кластере для обеспечения отказоустойчивости. + +Рассмотрим изменения в производительности в сравнении с предыдущим этапом. + +### PUT +До добавления репликации предельное количество запросов в секунду составляло 15000. + +Нагружаем новое решение, 4к rps +
+wrk +
+ wrk -d 30 -t 64 -c 64 -R 4000 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency    17.82ms   18.25ms  85.95ms   80.09%
+Req/Sec    62.38      7.78    89.00     81.17%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%   10.95ms
+75.000%   31.61ms
+90.000%   45.28ms
+99.000%   66.50ms
+99.900%   77.25ms
+99.990%   82.37ms
+99.999%   84.74ms
+100.000%   86.01ms
+
+
+
+ +
+График +4k put +
+Можно сказать, что нагрузка является допустимой, так как кривая задержки не имеет резких скачков и максимальная задержка 86ms. + +Пробуем увеличить до 5к rps, сразу же получаем задержки в несколько секунд: +
+wrk +
+ wrk -d 30 -t 64 -c 64 -R 5000 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency     4.91s     1.79s    8.55s    60.85%
+Req/Sec    54.40      5.55    60.00     75.00%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    4.70s
+75.000%    6.06s
+90.000%    7.89s
+99.000%    8.47s
+99.900%    8.51s
+99.990%    8.54s
+99.999%    8.55s
+100.000%    8.56s
+
+
+
+ +**Профилирование** +1. CPU + +
+Флеймграф +put +
+Первое, что заметно, 40% процессорного времени уходит на работу HttpClient. Наибольшая проблема заключается в том, что использованный client.sendAsync() не работает, так как все равно блокируемся на CompletableFuture.get. Получается, что лишь добавились накладные расходы на работу виртуальных потоков. + +Выполнение локальных запросов занимает 27% времени процессора, тут никаких изменений относительно прошлой реализации нет. Ну и 12% на работу SelectorThread + +2. Alloc +
+Флеймграф +put +
+ +Все так же 58% аллокаций связаны с выполнением работы Http Клиента, 1.5% из них уходят на создания новых CompletableFuture, от которых избавлюсь в следующем этапе. +Дополнительные аллокации теперь также в методе entity() при вычислении номеров нод, на которые мы отправим запрос: в RendezvousRouter теперь на 1.41% больше аллокацих, приходящихся на TreeMap, на 0.7% больше аллокаций ArrayList, чего можно было избежать путем увеличения нагрузки на cpu (пришлось бы запускать дополнительные сортировки). + +Последнее, в прошлом этапе 4% приходилось на логгер (в частности внутри метода entity), я не сразу это заметил, поэтому исправил в данном решении. + + +3. Lock + +
+Флеймграф +put +
+ +Из заметного: 3.27% локов приходились на злополучный логгер, теперь таких затрат нет. +В остальном: 32% уходят на работу с ответами нод - отправка response, регистрация событий селектора для установления соединения между нодами (34% отправка, 32% ожидание ответа) + + +### GET + +Начнем с 2000 rps, как прошлом этапе: +
+wrk +
+ wrk -d 30 -t 64 -c 64 -R 2000 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency     5.63s     1.61s    8.50s    57.74%
+Req/Sec     1.43k     0.47     1.43k   100.00%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    5.62s
+75.000%    7.03s
+90.000%    7.86s
+99.000%    8.36s
+99.900%    8.46s
+99.990%    8.50s
+99.999%    8.51s
+100.000%    8.51s
+
+
+
+
+
+ + +1500 rps: +
+wrk +
+ wrk -d 30 -t 64 -c 64 -R 1500 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency   406.33ms  261.34ms   1.11s    58.58%
+Req/Sec    22.06      5.72    44.00     75.90%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%  376.06ms
+75.000%  644.61ms
+90.000%  758.78ms
+99.000%  921.09ms
+99.900%    1.01s
+99.990%    1.09s
+99.999%    1.12s
+100.000%    1.12s
+
+
+
+ +Задержка все еще стабильно высокая. + +Наконец 1200 rps: + +
+wrk +
+ wrk -d 30 -t 64 -c 64 -R 1200 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity
+
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency    17.17ms    9.07ms  38.37ms   60.97%
+Req/Sec    18.77     18.12    62.00     89.45%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%   17.01ms
+75.000%   24.21ms
+90.000%   29.18ms
+99.000%   34.08ms
+99.900%   36.70ms
+99.990%   38.14ms
+99.999%   38.40ms
+100.000%   38.40ms
+
+Detailed Percentile spectrum:
+Value   Percentile   TotalCount 1/(1-Percentile)
+
+       0.460     0.000000            1         1.00
+       2.577     0.100000         2398         1.11
+       8.319     0.200000         4797         1.25
+      12.559     0.300000         7195         1.43
+      14.527     0.400000         9592         1.67
+      17.007     0.500000        11992         2.00
+      17.951     0.550000        13187         2.22
+      19.903     0.600000        14390         2.50
+      21.599     0.650000        15595         2.86
+      22.591     0.700000        16797         3.33
+      24.207     0.750000        17980         4.00
+      25.743     0.775000        18583         4.44
+      26.575     0.800000        19187         5.00
+      27.183     0.825000        19798         5.71
+      27.711     0.850000        20384         6.67
+      28.335     0.875000        20984         8.00
+      28.703     0.887500        21281         8.89
+      29.183     0.900000        21585        10.00
+      29.759     0.912500        21880        11.43
+      30.559     0.925000        22176        13.33
+      31.295     0.937500        22484        16.00
+      31.551     0.943750        22626        17.78
+      31.855     0.950000        22787        20.00
+      32.095     0.956250        22923        22.86
+      32.367     0.962500        23080        26.67
+      32.671     0.968750        23227        32.00
+      32.863     0.971875        23304        35.56
+      33.055     0.975000        23379        40.00
+      33.247     0.978125        23453        45.71
+      33.407     0.981250        23527        53.33
+      33.599     0.984375        23610        64.00
+      33.663     0.985938        23635        71.11
+      33.855     0.987500        23676        80.00
+      33.983     0.989062        23713        91.43
+      34.111     0.990625        23749       106.67
+      34.239     0.992188        23784       128.00
+      34.335     0.992969        23804       142.22
+      34.495     0.993750        23822       160.00
+      34.655     0.994531        23846       182.86
+      34.783     0.995313        23862       213.33
+      34.975     0.996094        23879       256.00
+      35.135     0.996484        23888       284.44
+      35.263     0.996875        23897       320.00
+      35.519     0.997266        23906       365.71
+      35.903     0.997656        23917       426.67
+      36.159     0.998047        23925       512.00
+      36.287     0.998242        23930       568.89
+      36.415     0.998437        23937       640.00
+      36.447     0.998633        23939       731.43
+      36.511     0.998828        23944       853.33
+      36.703     0.999023        23948      1024.00
+      36.863     0.999121        23950      1137.78
+      36.991     0.999219        23953      1280.00
+      37.055     0.999316        23956      1462.86
+      37.087     0.999414        23957      1706.67
+      37.183     0.999512        23960      2048.00
+      37.439     0.999561        23961      2275.56
+      37.535     0.999609        23962      2560.00
+      37.567     0.999658        23963      2925.71
+      37.663     0.999707        23964      3413.33
+      37.791     0.999756        23966      4096.00
+      37.791     0.999780        23966      4551.11
+      38.047     0.999805        23967      5120.00
+      38.047     0.999829        23967      5851.43
+      38.143     0.999854        23969      6826.67
+      38.143     0.999878        23969      8192.00
+      38.143     0.999890        23969      9102.22
+      38.143     0.999902        23969     10240.00
+      38.143     0.999915        23969     11702.86
+      38.175     0.999927        23970     13653.33
+      38.175     0.999939        23970     16384.00
+      38.175     0.999945        23970     18204.44
+      38.175     0.999951        23970     20480.00
+      38.175     0.999957        23970     23405.71
+      38.399     0.999963        23971     27306.67
+      38.399     1.000000        23971          inf
+#[Mean    =       17.174, StdDeviation   =        9.070]
+#[Max     =       38.368, Total count    =        23971]
+#[Buckets =           27, SubBuckets     =         2048]
+----------------------------------------------------------
+36009 requests in 30.01s, 2.30MB read
+Non-2xx or 3xx responses: 21532
+Requests/sec:   1200.02
+Transfer/sec:     78.39KB
+
+
+ +
+График зависимости задержки от % +put +
+ +Результат: равномерное увеличение задержки, отсутствие резких скачков, максимальная задержка 38.4ms. + +**Профилирование** + +1. CPU + +Был проведен тест двух случаев: в первом случае ключи, по которым производился поиск были преимущественно добавлены в бд, во втором случае преимущественно отсутствовали. +В первом случае локальная обработка запрос с дальнейшим поиском по БД заняла всего 4.5% +
+Флеймграф +put +
+ +Во втором - 87% +
+Флеймграф +put +
+ +Если же сравнивать первый флеймграф с ситуацией в PUT, то можно заметить, что в случае с PUT больше процессорного времени уходит на чтение сессии one/nio/net/Session.read с дальнейшей обработкой системных вызовов (5% против 1.74% у GET) +2. Alloc +
+Флеймграф +put +
+ +Нет существенных отличий от PUT + +3. Lock + +
+Флеймграф +put +
+ + Блокировок больше, чем при PUT. Добавился ReentrantLock, который составляет 13% локов и обеспечивает процесс one/nio/http/HttpSession.processRead + +**Вывод** + +С добавлением репликации пропускная способность PUT упала почти в 4 раза, а GET - в 1.6 раз. \ No newline at end of file diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.html new file mode 100644 index 000000000..57a6aeb82 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.html @@ -0,0 +1,3339 @@ + + + + + + + +

Allocation profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.png new file mode 100644 index 000000000..f8ced2382 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.html new file mode 100644 index 000000000..cce69d731 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.html @@ -0,0 +1,3662 @@ + + + + + + + +

Allocation profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.png new file mode 100644 index 000000000..87b397a4f Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_alloc_put.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get.png new file mode 100644 index 000000000..87323bc45 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get2.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get2.html new file mode 100644 index 000000000..05e8965ed --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_get2.html @@ -0,0 +1,7147 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.html new file mode 100644 index 000000000..b11000ea8 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.html @@ -0,0 +1,9245 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.png new file mode 100644 index 000000000..ea626966e Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_cpu_put.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.html new file mode 100644 index 000000000..2e2d2734f --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.html @@ -0,0 +1,855 @@ + + + + + + + +

Lock profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.png new file mode 100644 index 000000000..c0dc77565 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_get.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.html new file mode 100644 index 000000000..4ff0e254f --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.html @@ -0,0 +1,752 @@ + + + + + + + +

Lock profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.png new file mode 100644 index 000000000..dc7d52784 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/profile_lock_put.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_6500.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_6500.png new file mode 100644 index 000000000..9418d679a Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_6500.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_7k.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_7k.png new file mode 100644 index 000000000..7315577af Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/profiler/put_7k.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/result_hw5.md b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/result_hw5.md new file mode 100644 index 000000000..6c004318e --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw5/result_hw5.md @@ -0,0 +1,362 @@ +## Асинхронное взаимодействие +Требовалось параллельно отправлять запросы репликам и собирать самые быстрые ответы на CompletableFuture. + +Для реализации параллельной отправки использовался метод HttpClient.sendAsync(). Данный метод я использовал еще в прошлых этапах, но неправильно, блокируя поток get'ом. В результате чего производительность упала сильнее ожидаемого. Проверим, насколько получилось исправить ситуацию: + ### PUT +В четвертом этапе удалось получить лишь 4к rps. +Нагружаем новое решение. +10к rps + +
+wrk, 10к rps, 30 sec +
+ wrk -d 30 -t 64 -c 64 -R 10000 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency     1.92ms    2.62ms  43.42ms   96.56%
+Req/Sec   163.20     45.88   500.00     66.37%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    1.44ms
+75.000%    1.89ms
+90.000%    2.71ms
+99.000%   16.51ms
+99.900%   33.38ms
+99.990%   38.14ms
+99.999%   40.70ms
+100.000%   43.46ms
+
+
+
+ +Нагрузка не является стабильной, так как при увеличении времени профилирования получаем скачок на 99 персентиле. +
+wrk, 10к rps, 60 sec +
+ wrk -d 60 -t 64 -c 64 -R 10000 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency    25.33ms   70.96ms 441.09ms   91.61%
+Req/Sec   164.65     60.20   800.00     74.62%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    1.84ms
+75.000%    5.80ms
+90.000%   43.90ms
+99.000%  340.99ms
+99.900%  391.68ms
+99.990%  415.23ms
+99.999%  437.76ms
+100.000%  441.34ms
+
+
+
+
+ +7к запросов все еще многовато для сервера, так как заметен неравномерный рост кривой +
+График +7k put +
+ +Снизив нагрузку до 6500 rps получилось снизить максимальную задержку до 20 мс, а также избавиться от "скачка" на графике. +
+График +6.5k put +
+ +
+wrk, 6.5к rps, 60 sec +
+ wrk -d 60 -t 64 -c 64 -R 6500 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency     1.07ms  838.06us  20.45ms   96.89%
+Req/Sec   106.82     28.86   333.00     92.67%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    0.98ms
+75.000%    1.24ms
+90.000%    1.40ms
+99.000%    4.34ms
+99.900%   11.94ms
+99.990%   15.69ms
+99.999%   19.49ms
+100.000%   20.46ms
+
+Detailed Percentile spectrum:
+Value   Percentile   TotalCount 1/(1-Percentile)
+
+       0.231     0.000000            1         1.00
+       0.542     0.100000        32685         1.11
+       0.658     0.200000        65232         1.25
+       0.766     0.300000        97606         1.43
+       0.871     0.400000       130103         1.67
+       0.975     0.500000       162553         2.00
+       1.029     0.550000       178732         2.22
+       1.082     0.600000       195121         2.50
+       1.134     0.650000       211408         2.86
+       1.186     0.700000       227755         3.33
+       1.237     0.750000       243934         4.00
+       1.262     0.775000       252002         4.44
+       1.287     0.800000       259964         5.00
+       1.313     0.825000       268205         5.71
+       1.339     0.850000       276240         6.67
+       1.367     0.875000       284362         8.00
+       1.382     0.887500       288387         8.89
+       1.400     0.900000       292586        10.00
+       1.420     0.912500       296511        11.43
+       1.447     0.925000       300668        13.33
+       1.484     0.937500       304696        16.00
+       1.508     0.943750       306682        17.78
+       1.544     0.950000       308694        20.00
+       1.596     0.956250       310714        22.86
+       1.691     0.962500       312751        26.67
+       1.900     0.968750       314772        32.00
+       2.073     0.971875       315784        35.56
+       2.297     0.975000       316808        40.00
+       2.559     0.978125       317823        45.71
+       2.831     0.981250       318829        53.33
+       3.169     0.984375       319849        64.00
+       3.375     0.985938       320353        71.11
+       3.637     0.987500       320861        80.00
+       4.019     0.989062       321368        91.43
+       4.575     0.990625       321875       106.67
+       5.359     0.992188       322388       128.00
+       5.787     0.992969       322637       142.22
+       6.235     0.993750       322891       160.00
+       6.715     0.994531       323145       182.86
+       7.247     0.995313       323398       213.33
+       7.915     0.996094       323652       256.00
+       8.287     0.996484       323779       284.44
+       8.687     0.996875       323906       320.00
+       9.143     0.997266       324036       365.71
+       9.551     0.997656       324160       426.67
+      10.063     0.998047       324289       512.00
+      10.351     0.998242       324350       568.89
+      10.679     0.998437       324414       640.00
+      11.071     0.998633       324477       731.43
+      11.471     0.998828       324542       853.33
+      12.039     0.999023       324604      1024.00
+      12.343     0.999121       324637      1137.78
+      12.575     0.999219       324668      1280.00
+      12.847     0.999316       324699      1462.86
+      13.103     0.999414       324731      1706.67
+      13.455     0.999512       324764      2048.00
+      13.655     0.999561       324780      2275.56
+      13.855     0.999609       324795      2560.00
+      14.087     0.999658       324810      2925.71
+      14.295     0.999707       324826      3413.33
+      14.535     0.999756       324842      4096.00
+      14.703     0.999780       324851      4551.11
+      14.799     0.999805       324859      5120.00
+      15.095     0.999829       324866      5851.43
+      15.167     0.999854       324874      6826.67
+      15.399     0.999878       324882      8192.00
+      15.527     0.999890       324886      9102.22
+      15.711     0.999902       324890     10240.00
+      15.911     0.999915       324894     11702.86
+      16.415     0.999927       324898     13653.33
+      16.815     0.999939       324902     16384.00
+      16.895     0.999945       324904     18204.44
+      17.375     0.999951       324906     20480.00
+      17.503     0.999957       324908     23405.71
+      17.663     0.999963       324911     27306.67
+      17.775     0.999969       324912     32768.00
+      18.767     0.999973       324913     36408.89
+      18.831     0.999976       324914     40960.00
+      18.847     0.999979       324915     46811.43
+      19.279     0.999982       324916     54613.33
+      19.455     0.999985       324917     65536.00
+      19.455     0.999986       324917     72817.78
+      19.487     0.999988       324918     81920.00
+      19.487     0.999989       324918     93622.86
+      20.015     0.999991       324919    109226.67
+      20.015     0.999992       324919    131072.00
+      20.015     0.999993       324919    145635.56
+      20.255     0.999994       324920    163840.00
+      20.255     0.999995       324920    187245.71
+      20.255     0.999995       324920    218453.33
+      20.255     0.999996       324920    262144.00
+      20.255     0.999997       324920    291271.11
+      20.463     0.999997       324921    327680.00
+      20.463     1.000000       324921          inf
+#[Mean    =        1.066, StdDeviation   =        0.838]
+#[Max     =       20.448, Total count    =       324921]
+#[Buckets =           27, SubBuckets     =         2048]
+
+
+
+ +Таким образом, новое решение позволило увеличить rps на 2500. + +#### Профилирование + +
+CPU флеймграф +cpu +
+ +Все так же 40% процессорного времени уходит на HttpClient - асинхронную отправку запросов. +12% - handleRequest. 8% - код, ответственный за асинхронную обработку запросов внутри dispatchRequestsToNodes (4% из которых отведено responseExecutor, выполняющего проверку собранных ответов и досрочную их отправку). +Единственное, я не очень понял, откуда взялся ArrayBlockingQueue в разделе обработки запросов на флеймграфе, так как responseExecutor работает на LinkedBlockingQueue. Скорее всего, это связано с методом CompletableFuture.thenApply(), так как я явно не указал исполнителя, и операция выполнилась в CompletableFuture#defaultExecutor. + +. + +
+Alloc флеймграф +alloc +
+ +Различий не заметил (ch/qos/logback/classic/Logger.info не считаю) + +
+Lock флеймграф +lock +
+ +Аналогично. + + +### GET + +4 этап позволил нагрузить лишь 1200 rps. +Проверяем 2.2к rps: + + +
+wrk +
+wrk -d 30 -t 64 -c 64 -R 2200 -L  -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity
+
+Thread Stats   Avg      Stdev     Max   +/- Stdev
+Latency     2.33ms    1.65ms  19.70ms   87.34%
+Req/Sec    36.28     49.94   111.00     65.37%
+Latency Distribution (HdrHistogram - Recorded Latency)
+50.000%    1.88ms
+75.000%    2.64ms
+90.000%    4.10ms
+99.000%    9.06ms
+99.900%   14.28ms
+99.990%   19.12ms
+99.999%   19.71ms
+100.000%   19.71ms
+
+Detailed Percentile spectrum:
+Value   Percentile   TotalCount 1/(1-Percentile)
+
+       0.263     0.000000            1         1.00
+       1.051     0.100000         4414         1.11
+       1.291     0.200000         8808         1.25
+       1.484     0.300000        13204         1.43
+       1.672     0.400000        17606         1.67
+       1.876     0.500000        22009         2.00
+       1.986     0.550000        24194         2.22
+       2.109     0.600000        26414         2.50
+       2.245     0.650000        28597         2.86
+       2.425     0.700000        30797         3.33
+       2.641     0.750000        33004         4.00
+       2.773     0.775000        34094         4.44
+       2.931     0.800000        35205         5.00
+       3.125     0.825000        36298         5.71
+       3.375     0.850000        37391         6.67
+       3.687     0.875000        38492         8.00
+       3.875     0.887500        39043         8.89
+       4.099     0.900000        39598        10.00
+       4.367     0.912500        40147        11.43
+       4.699     0.925000        40691        13.33
+       5.123     0.937500        41240        16.00
+       5.367     0.943750        41515        17.78
+       5.631     0.950000        41794        20.00
+       5.915     0.956250        42067        22.86
+       6.231     0.962500        42341        26.67
+       6.655     0.968750        42616        32.00
+       6.855     0.971875        42753        35.56
+       7.131     0.975000        42892        40.00
+       7.419     0.978125        43027        45.71
+       7.759     0.981250        43165        53.33
+       8.079     0.984375        43302        64.00
+       8.271     0.985938        43371        71.11
+       8.511     0.987500        43440        80.00
+       8.823     0.989062        43508        91.43
+       9.231     0.990625        43578       106.67
+       9.663     0.992188        43646       128.00
+       9.863     0.992969        43681       142.22
+      10.111     0.993750        43716       160.00
+      10.319     0.994531        43749       182.86
+      10.703     0.995313        43783       213.33
+      11.007     0.996094        43819       256.00
+      11.207     0.996484        43835       284.44
+      11.455     0.996875        43852       320.00
+      11.775     0.997266        43869       365.71
+      12.135     0.997656        43886       426.67
+      12.655     0.998047        43904       512.00
+      12.879     0.998242        43914       568.89
+      13.207     0.998437        43921       640.00
+      13.511     0.998633        43929       731.43
+      14.039     0.998828        43938       853.33
+      14.295     0.999023        43947      1024.00
+      14.839     0.999121        43951      1137.78
+      15.167     0.999219        43955      1280.00
+      15.399     0.999316        43959      1462.86
+      15.575     0.999414        43964      1706.67
+      15.855     0.999512        43968      2048.00
+      16.071     0.999561        43970      2275.56
+      16.335     0.999609        43972      2560.00
+      16.655     0.999658        43974      2925.71
+      16.927     0.999707        43978      3413.33
+      17.119     0.999756        43979      4096.00
+      17.199     0.999780        43980      4551.11
+      17.375     0.999805        43981      5120.00
+      17.487     0.999829        43982      5851.43
+      17.711     0.999854        43983      6826.67
+      18.559     0.999878        43984      8192.00
+      19.119     0.999890        43985      9102.22
+      19.119     0.999902        43985     10240.00
+      19.295     0.999915        43986     11702.86
+      19.295     0.999927        43986     13653.33
+      19.359     0.999939        43987     16384.00
+      19.359     0.999945        43987     18204.44
+      19.359     0.999951        43987     20480.00
+      19.615     0.999957        43988     23405.71
+      19.615     0.999963        43988     27306.67
+      19.615     0.999969        43988     32768.00
+      19.615     0.999973        43988     36408.89
+      19.615     0.999976        43988     40960.00
+      19.711     0.999979        43989     46811.43
+      19.711     1.000000        43989          inf
+#[Mean    =        2.333, StdDeviation   =        1.645]
+#[Max     =       19.696, Total count    =        43989]
+#[Buckets =           27, SubBuckets     =         2048]
+
+
+
+ +Больше увеличить нагрузку не удается, при 3к rps получаем среднюю задержку 1.38 с. + +#### Профилирование + +
+CPU флеймграф +cpu +
+Во-первых, на флеймграфе видно, что используются другие методы CompletableFuture, на которых уходит больше времени: 16.5% против ~20% в новом решении. +Заметно также, что от асинхронной обработки больше работы появилось для gc, 5%. +Также, HttpSession.sendResponse видно, что разбит по разным потокам. +В остальном комментарии к put-флеймграфу справедливы и в данном, get, случае. + +. +
+Alloc флеймграф +alloc +
+Изменения минимальные, заметил только увеличение с 3.5% до 4.8% локов на Http1AsyncReceiver. + +. +
+Lock флеймграф +lock +
+ +Здесь также заметно увеличение количества локов, отведенных Http1AsyncReceiver с 14% до 28%. +Но, что самое важное, с уходом CompletableFuture.get количество блокировок ThreadPoolExecutor.getTask снизилось с 14% до 0.3%, что довольно показательно. + +### Вывод +C добавлением асинхронного взаимодействия производительность увеличилась. PUT теперь выдерживает 6.5k rps, GET 2.2k rps \ No newline at end of file diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/alloc.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/alloc.png new file mode 100644 index 000000000..57bc8ce8f Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/alloc.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu1.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu1.png new file mode 100644 index 000000000..cf63ca0cb Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu1.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu2.png b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu2.png new file mode 100644 index 000000000..c529b0e41 Binary files /dev/null and b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/cpu2.png differ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_alloc.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_alloc.html new file mode 100644 index 000000000..12c5109e5 --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_alloc.html @@ -0,0 +1,335 @@ + + + + + + + +

Allocation profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu.html new file mode 100644 index 000000000..82553f3ea --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu.html @@ -0,0 +1,1350 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu2.html b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu2.html new file mode 100644 index 000000000..077c8620b --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/profiler/profile_cpu2.html @@ -0,0 +1,578 @@ + + + + + + + +

CPU profile

+
  
+
Produced by async-profiler
+ +
+

+

Matched:

+ diff --git a/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/result_hw6.md b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/result_hw6.md new file mode 100644 index 000000000..b10854c4e --- /dev/null +++ b/src/main/java/ru/vk/itmo/test/osokindm/results/hw6/result_hw6.md @@ -0,0 +1,28 @@ +## Range-запросы +Требовалось реализовать получение диапазона данных текущего узла. + +Результаты: + +Был выполнен следующий запрос: + +```curl "http://localhost:8080/v0/entities?start=1230&end=4560000000" ``` + + +Два запуска профайлера на cpu. При первом запуске видна работа компилятора +
+CPU флеймграф +cpu +
+ +По одному запросу флеймграфы получились не очень информативные, но в целом видно, что с добавлением диапазонных get запросов процессорное время стало уходить на работу с dao - LiveFilteringIterator 75%, запись в сокет, для отправки ответа - 14% +
+CPU флеймграф 2 +cpu +
+Аллокации только объектов BaseEntry, MemorySegment, QueueItem внутри метода write, который, на самом деле, можно было создавать самому, тем самым контролируя процесс создания объекта, ну и также аллоцируются массивы байт внутри writeChunk. +
+Alloc флеймграф +alloc +
+ + Флеймграф с локами пуст. \ No newline at end of file diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua index 00a3f20de..d26c93554 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua +++ b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua @@ -8,7 +8,7 @@ function getRandomId() minId = 1 - maxId = 10000000 + maxId = 10000 return math.random(minId, maxId) end diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_8000.svg b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_8000.svg deleted file mode 100644 index 5c1a8bcc8..000000000 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_8000.svg +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_alloc_8000.svg b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_alloc_8000.svg deleted file mode 100644 index 6106235b1..000000000 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_get_alloc_8000.svg +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_8000.svg b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_8000.svg deleted file mode 100644 index e7aa8cae8..000000000 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_8000.svg +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_alloc_8000.svg b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_alloc_8000.svg deleted file mode 100644 index b3416eb97..000000000 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/profile_put_alloc_8000.svg +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put.lua b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put.lua index 8cd8c9e74..89cc5e118 100644 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put.lua +++ b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put.lua @@ -1,5 +1,5 @@ minId = 1 -maxId = 100000000 +maxId = 1000000 function getRandomId() return math.random(minId, maxId) @@ -30,4 +30,4 @@ function request() path = "/v0/entity?id=" .. id body = getRandomString(10) return wrk.format("PUT", path, nil, body) -end \ No newline at end of file +end diff --git a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/result_hw1.md b/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/result_hw1.md deleted file mode 100644 index 6b4a1684d..000000000 --- a/src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/result_hw1.md +++ /dev/null @@ -1,161 +0,0 @@ -## Нахождение точки разладки -### GET -Размер БД = 2.1gb - -при выполнении 10'000 запросов в секунду на протяжении 30 секунд получили среднее -время выполнения запроса равное 22.64ms, максимальное - 182.66ms, что уже сопоставимо с одной секундой -и выше допустимого порядка. -Количество запросов было уменьшено на 20%. Получили следующие результаты: \ -`wrk -d 30 -t 1 -c 1 -R 8000 -L -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity` - - -``` -Running 30s test @ http://localhost:8080/v0/entity - 1 threads and 1 connections - Thread calibration: mean lat.: 1.055ms, rate sampling interval: 10ms - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.36ms 2.31ms 22.88ms 94.51% - Req/Sec 8.44k 770.72 14.44k 73.79% - Latency Distribution (HdrHistogram - Recorded Latency) - 50.000% 0.85ms - 75.000% 1.20ms - 90.000% 2.22ms - 99.000% 15.76ms - 99.900% 21.26ms - 99.990% 22.77ms - 99.999% 22.85ms -100.000% 22.90ms -``` - -Необходимо убедиться, что нагрузка является стабильной, для этого проведем повторное тестирование, но длительностью -5 минут: - -` wrk -d 300 -t 1 -c 1 -R 8000 -L -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/get.lua http://localhost:8080/v0/entity` - -``` -Running 5m test @ http://localhost:8080/v0/entity - 1 threads and 1 connections - Thread calibration: mean lat.: 1.194ms, rate sampling interval: 10ms - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.43ms 2.49ms 39.58ms 96.02% - Req/Sec 8.44k 0.88k 19.67k 72.04% - Latency Distribution (HdrHistogram - Recorded Latency) - 50.000% 0.94ms - 75.000% 1.52ms - 90.000% 2.37ms - 99.000% 13.39ms - 99.900% 33.85ms - 99.990% 38.72ms - 99.999% 39.39ms -100.000% 39.62ms ----------------------------------------------------------- - 2399973 requests in 5.00m, 148.53MB read -Requests/sec: 7999.91 -Transfer/sec: 506.97KB - -``` -Можно считать, что нагрузка является допустимой и проведена ниже точки разладки. -Низкое Latency при столь высоком rps достигается за счет того, что все Page object свободно вмещаеются в оперативной памяти и, как следствие, у нас нет медленных запросов к диску - -[Flamegraph CPU](../results/hw1/profiler/profile_get_8000.svg) -Большую часть процессорного времени (>43%) занимает работа с MemorySegment, бинарный поиск и метод сравнения сегментов MemorySegmentComparator.compare -20% процессорного времени занимало выполенине кода ядра, в честности 19% ушло на syscall получения набора семафоров (SEMGET). - -[Flamegraph Alloc](../results/hw1/profiler/profile_get_alloc_8000.svg) - -Больше всего выделений памяти (8%) уходило на Mapped MemorySegment - -На данный момент все запросы выполняются внутри SelectorThread, чего быть по идее не должно, поэтому возможна оптимизация путем делегирования задачи WorkerPool'у - - -Если сделать GET запрос не по рандомному индексу, а по одному и тому же, то средняя задержка выполнения запроса снизилась -на 30%, что связано с кешированием результата: -``` Thread calibration: mean lat.: 1.071ms, rate sampling interval: 10ms -Thread Stats Avg Stdev Max +/- Stdev -Latency 1.00ms 650.63us 5.64ms 67.41% -Req/Sec 8.45k 765.16 12.56k 71.79% -Latency Distribution (HdrHistogram - Recorded Latency) -50.000% 0.91ms -75.000% 1.35ms -90.000% 1.92ms -99.000% 2.91ms -99.900% 4.41ms -99.990% 5.47ms -99.999% 5.63ms -100.000% 5.64ms -``` - -### PUT -**Вставка уникального id на каждом запросе** - -`john@Batman:~/IdeaProjects/2024-highload-dht-1$ wrk -d 30 -t 1 -c 1 -R 8000 -L -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put_new.lua http://localhost:8080/v0/entity` - -``` -Running 30s test @ http://localhost:8080/v0/entity -1 threads and 1 connections -Thread calibration: mean lat.: 1.237ms, rate sampling interval: 10ms -Thread Stats Avg Stdev Max +/- Stdev -Latency 1.19ms 0.86ms 23.86ms 78.25% -Req/Sec 8.44k 839.27 20.60k 72.12% -Latency Distribution (HdrHistogram - Recorded Latency) -50.000% 1.10ms -75.000% 1.72ms -90.000% 2.05ms -99.000% 2.80ms -99.900% 9.42ms -99.990% 23.41ms -99.999% 23.85ms -100.000% 23.87ms - ----------------------------------------------------------- -239995 requests in 30.00s, 15.33MB read -Requests/sec: 7999.72 -Transfer/sec: 523.42KB -``` - -Повторное выполнение запроса на пустой БД не приводит к существенному изменению результатов, в связи с тем, что созданные записи не просматриваются при вставке. -``` - Thread Stats Avg Stdev Max +/- Stdev - Latency 1.47ms 1.53ms 26.43ms 93.23% - Req/Sec 8.27k 1.05k 20.62k 84.83% - Latency Distribution (HdrHistogram - Recorded Latency) - 50.000% 1.15ms - 75.000% 1.86ms - 90.000% 2.44ms - 99.000% 7.97ms - 99.900% 17.97ms - 99.990% 25.58ms - 99.999% 26.45ms -100.000% 26.45ms -``` - -[Flamegraph CPU](../results/hw1/profiler/profile_put_8000.svg) -Здесь оптимизация, касающаяся SelectorThread также применима, что и постараюсь реализовать в следующей дз. - -[Flamegraph Alloc](../results/hw1/profiler/profile_put_alloc_8000.svg) -В отличие от GET, здесь происходит намного меньше обращений к памяти. - -**Замена существующего значения** - -`john@Batman:~/IdeaProjects/2024-highload-dht-1$ wrk -d 30 -t 1 -c 1 -R 8000 -L -s ./src/main/java/ru/vk/itmo/test/osokindm/wrk_scripts/put.lua http://localhost:8080/v0/entity` - -``` -Running 30s test @ http://localhost:8080/v0/entity -1 threads and 1 connections -Thread calibration: mean lat.: 1.326ms, rate sampling interval: 10ms -Thread Stats Avg Stdev Max +/- Stdev -Latency 1.17ms 1.14ms 33.95ms 97.95% -Req/Sec 8.44k 0.96k 29.00k 76.08% -Latency Distribution (HdrHistogram - Recorded Latency) -50.000% 1.09ms -75.000% 1.66ms -90.000% 1.98ms -99.000% 2.66ms -99.900% 20.54ms -99.990% 33.18ms -99.999% 33.95ms -100.000% 33.98ms -``` -Если изменить скрипт lua и наполнять БД, обращаясь по ключам с уже существующими значениями, то можно заметить, что время работы также не изменилось (1.19ms vs 1.17ms). - -