Skip to content

Commit 0fa0697

Browse files
Queenorelamtev
authored andcommitted
Stage 1, Андрей Чешев, Политех (polis-vk#16)
* rename package * 8/14 tests * 12/14 tests * 14/14 tests * fixes * fixes * fixes * + report * codestyle fixes * codestyle fixes * add lua scripts * report fix * HTML to SVG * add THRESHOLD_BYTES const * code review fixes * code climate fixes * style fixes --------- Co-authored-by: Anton Lamtev <lamtev@users.noreply.github.com>
1 parent 14026f7 commit 0fa0697

File tree

76 files changed

+3593
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3593
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package ru.vk.itmo.test.andreycheshev;
2+
3+
import one.nio.http.HttpServer;
4+
import one.nio.http.HttpServerConfig;
5+
import one.nio.http.HttpSession;
6+
import one.nio.http.Request;
7+
import one.nio.http.Response;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
10+
import ru.vk.itmo.dao.BaseEntry;
11+
import ru.vk.itmo.dao.Config;
12+
import ru.vk.itmo.dao.Entry;
13+
import ru.vk.itmo.test.andreycheshev.dao.PersistentReferenceDao;
14+
15+
import java.io.IOException;
16+
import java.io.UncheckedIOException;
17+
import java.lang.foreign.MemorySegment;
18+
import java.lang.foreign.ValueLayout;
19+
import java.nio.charset.StandardCharsets;
20+
21+
import static one.nio.http.Request.METHOD_DELETE;
22+
import static one.nio.http.Request.METHOD_GET;
23+
import static one.nio.http.Request.METHOD_PUT;
24+
import static one.nio.http.Response.ACCEPTED;
25+
import static one.nio.http.Response.BAD_REQUEST;
26+
import static one.nio.http.Response.CREATED;
27+
import static one.nio.http.Response.INTERNAL_ERROR;
28+
import static one.nio.http.Response.METHOD_NOT_ALLOWED;
29+
import static one.nio.http.Response.NOT_FOUND;
30+
import static one.nio.http.Response.OK;
31+
32+
public class ServerImpl extends HttpServer {
33+
private static final Logger logger = LoggerFactory.getLogger(ServerImpl.class);
34+
private static final String REQUEST_PATH = "/v0/entity";
35+
private static final String ID = "id=";
36+
37+
private final PersistentReferenceDao dao;
38+
39+
public ServerImpl(HttpServerConfig config, Config daoConfig) throws IOException {
40+
super(config);
41+
42+
this.dao = new PersistentReferenceDao(daoConfig);
43+
}
44+
45+
private Response get(final Request request) {
46+
String id = request.getParameter(ID);
47+
if (id == null || id.isEmpty()) {
48+
return new Response(BAD_REQUEST, Response.EMPTY);
49+
}
50+
51+
Entry<MemorySegment> entry = dao.get(fromString(id));
52+
53+
return (entry == null)
54+
? new Response(NOT_FOUND, Response.EMPTY)
55+
: new Response(OK, entry.value().toArray(ValueLayout.JAVA_BYTE));
56+
}
57+
58+
private Response put(final Request request) {
59+
String id = request.getParameter(ID);
60+
if (id == null || id.isEmpty()) {
61+
return new Response(BAD_REQUEST, Response.EMPTY);
62+
}
63+
64+
Entry<MemorySegment> entry = new BaseEntry<>(
65+
fromString(id),
66+
MemorySegment.ofArray(request.getBody())
67+
);
68+
dao.upsert(entry);
69+
70+
return new Response(CREATED, Response.EMPTY);
71+
}
72+
73+
private Response delete(final Request request) {
74+
String id = request.getParameter(ID);
75+
if (id == null || id.isEmpty()) {
76+
return new Response(BAD_REQUEST, Response.EMPTY);
77+
}
78+
79+
Entry<MemorySegment> entry = new BaseEntry<>(fromString(id), null);
80+
dao.upsert(entry);
81+
82+
return new Response(ACCEPTED, Response.EMPTY);
83+
}
84+
85+
@Override
86+
public void handleRequest(Request request, HttpSession session) {
87+
String path = request.getPath();
88+
if (!path.equals(REQUEST_PATH)) {
89+
sendResponse(
90+
new Response(BAD_REQUEST, Response.EMPTY),
91+
session
92+
);
93+
return;
94+
}
95+
96+
int method = request.getMethod();
97+
98+
Response response;
99+
try {
100+
response = switch (method) {
101+
case METHOD_GET -> get(request);
102+
case METHOD_PUT -> put(request);
103+
case METHOD_DELETE -> delete(request);
104+
default -> new Response(METHOD_NOT_ALLOWED, Response.EMPTY);
105+
};
106+
} catch (Exception e) {
107+
logger.error("Internal error of the DAO operation", e);
108+
sendResponse(new Response(INTERNAL_ERROR, Response.EMPTY), session);
109+
return;
110+
}
111+
112+
sendResponse(response, session);
113+
}
114+
115+
private void sendResponse(Response response, HttpSession session) {
116+
try {
117+
session.sendResponse(response);
118+
} catch (IOException e) {
119+
throw new UncheckedIOException("Error while sending response to the client", e);
120+
}
121+
}
122+
123+
private MemorySegment fromString(String data) {
124+
return MemorySegment.ofArray(data.getBytes(StandardCharsets.UTF_8));
125+
}
126+
127+
@Override
128+
public synchronized void stop() {
129+
super.stop();
130+
try {
131+
dao.close();
132+
} catch (IOException e) {
133+
throw new UncheckedIOException(e);
134+
}
135+
}
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package ru.vk.itmo.test.andreycheshev;
2+
3+
import one.nio.http.HttpServerConfig;
4+
import one.nio.server.AcceptorConfig;
5+
import ru.vk.itmo.dao.Config;
6+
7+
import java.io.IOException;
8+
import java.nio.file.Path;
9+
10+
public final class ServerStarter {
11+
private static final Path DIT_PATH = Path.of("/home/andrey/andrey/tmp");
12+
private static final int THRESHOLD_BYTES = 100000;
13+
private static final int PORT = 8080;
14+
15+
private ServerStarter() {
16+
17+
}
18+
19+
public static void main(String[] args) throws IOException {
20+
AcceptorConfig acceptorConfig = new AcceptorConfig();
21+
acceptorConfig.port = PORT;
22+
acceptorConfig.reusePort = true;
23+
HttpServerConfig serverConfig = new HttpServerConfig();
24+
serverConfig.acceptors = new AcceptorConfig[]{acceptorConfig};
25+
serverConfig.closeSessions = true;
26+
27+
Config daoConfig = new Config(DIT_PATH, THRESHOLD_BYTES);
28+
29+
ServerImpl server = new ServerImpl(serverConfig, daoConfig);
30+
31+
server.start();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package ru.vk.itmo.test.andreycheshev;
2+
3+
import one.nio.http.HttpServerConfig;
4+
import one.nio.server.AcceptorConfig;
5+
import one.nio.server.Server;
6+
import ru.vk.itmo.Service;
7+
import ru.vk.itmo.ServiceConfig;
8+
import ru.vk.itmo.dao.Config;
9+
import ru.vk.itmo.test.ServiceFactory;
10+
11+
import java.io.IOException;
12+
import java.io.UncheckedIOException;
13+
import java.util.concurrent.CompletableFuture;
14+
15+
public class ServiceImpl implements Service {
16+
private static final int THRESHOLD_BYTES = 100000;
17+
18+
private final HttpServerConfig serverConfig;
19+
private final Config daoConfig;
20+
21+
private Server server;
22+
23+
public ServiceImpl(ServiceConfig config) {
24+
this.serverConfig = createServerConfig(config);
25+
this.daoConfig = new Config(config.workingDir(), THRESHOLD_BYTES);
26+
}
27+
28+
private HttpServerConfig createServerConfig(ServiceConfig config) {
29+
AcceptorConfig acceptorConfig = new AcceptorConfig();
30+
acceptorConfig.port = config.selfPort();
31+
acceptorConfig.reusePort = true;
32+
33+
HttpServerConfig newServerConfig = new HttpServerConfig();
34+
newServerConfig.acceptors = new AcceptorConfig[]{acceptorConfig};
35+
newServerConfig.closeSessions = true;
36+
37+
return newServerConfig;
38+
}
39+
40+
@Override
41+
public CompletableFuture<Void> start() throws IOException {
42+
try {
43+
server = new ServerImpl(serverConfig, daoConfig);
44+
} catch (IOException e) {
45+
throw new UncheckedIOException(e);
46+
}
47+
server.start();
48+
return CompletableFuture.completedFuture(null);
49+
}
50+
51+
@Override
52+
public CompletableFuture<Void> stop() throws IOException {
53+
server.stop();
54+
return CompletableFuture.completedFuture(null);
55+
}
56+
57+
@ServiceFactory(stage = 1)
58+
public static class Factory implements ServiceFactory.Factory {
59+
60+
@Override
61+
public Service create(ServiceConfig config) {
62+
return new ServiceImpl(config);
63+
}
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package ru.vk.itmo.test.andreycheshev.dao;
2+
3+
import java.io.IOException;
4+
import java.lang.foreign.MemorySegment;
5+
import java.nio.ByteBuffer;
6+
7+
/**
8+
* Growable buffer with {@link ByteBuffer} and {@link MemorySegment} interface.
9+
*
10+
* @author incubos
11+
*/
12+
final class ByteArraySegment {
13+
private byte[] array;
14+
private MemorySegment segment;
15+
16+
ByteArraySegment(final int capacity) {
17+
this.array = new byte[capacity];
18+
this.segment = MemorySegment.ofArray(array);
19+
}
20+
21+
void withArray(final ArrayConsumer consumer) throws IOException {
22+
consumer.process(array);
23+
}
24+
25+
MemorySegment segment() {
26+
return segment;
27+
}
28+
29+
void ensureCapacity(final long size) {
30+
if (size > Integer.MAX_VALUE) {
31+
throw new IllegalArgumentException("Too big!");
32+
}
33+
34+
final int capacity = (int) size;
35+
if (array.length >= capacity) {
36+
return;
37+
}
38+
39+
// Grow to the nearest bigger power of 2
40+
final int newSize = Integer.highestOneBit(capacity) << 1;
41+
array = new byte[newSize];
42+
segment = MemorySegment.ofArray(array);
43+
}
44+
45+
interface ArrayConsumer {
46+
void process(byte[] array) throws IOException;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package ru.vk.itmo.test.andreycheshev.dao;
2+
3+
import ru.vk.itmo.dao.Entry;
4+
5+
import java.lang.foreign.MemorySegment;
6+
import java.util.Iterator;
7+
import java.util.NoSuchElementException;
8+
9+
/**
10+
* Filters non tombstone {@link Entry}s.
11+
*
12+
* @author incubos
13+
*/
14+
final class LiveFilteringIterator implements Iterator<Entry<MemorySegment>> {
15+
private final Iterator<Entry<MemorySegment>> delegate;
16+
private Entry<MemorySegment> next;
17+
18+
LiveFilteringIterator(final Iterator<Entry<MemorySegment>> delegate) {
19+
this.delegate = delegate;
20+
skipTombstones();
21+
}
22+
23+
private void skipTombstones() {
24+
while (delegate.hasNext()) {
25+
final Entry<MemorySegment> entry = delegate.next();
26+
if (entry.value() != null) {
27+
this.next = entry;
28+
break;
29+
}
30+
}
31+
}
32+
33+
@Override
34+
public boolean hasNext() {
35+
return next != null;
36+
}
37+
38+
@Override
39+
public Entry<MemorySegment> next() {
40+
if (!hasNext()) {
41+
throw new NoSuchElementException();
42+
}
43+
44+
// Consume
45+
final Entry<MemorySegment> result = next;
46+
next = null;
47+
48+
skipTombstones();
49+
50+
return result;
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package ru.vk.itmo.test.andreycheshev.dao;
2+
3+
import ru.vk.itmo.dao.Entry;
4+
5+
import java.lang.foreign.MemorySegment;
6+
import java.util.Iterator;
7+
import java.util.NavigableMap;
8+
import java.util.concurrent.ConcurrentSkipListMap;
9+
10+
/**
11+
* Memory table.
12+
*
13+
* @author incubos
14+
*/
15+
final class MemTable {
16+
private final NavigableMap<MemorySegment, Entry<MemorySegment>> map =
17+
new ConcurrentSkipListMap<>(
18+
MemorySegmentComparator.INSTANCE);
19+
20+
boolean isEmpty() {
21+
return map.isEmpty();
22+
}
23+
24+
Iterator<Entry<MemorySegment>> get(
25+
final MemorySegment from,
26+
final MemorySegment to) {
27+
if (from == null && to == null) {
28+
// All
29+
return map.values().iterator();
30+
} else if (from == null) {
31+
// Head
32+
return map.headMap(to).values().iterator();
33+
} else if (to == null) {
34+
// Tail
35+
return map.tailMap(from).values().iterator();
36+
} else {
37+
// Slice
38+
return map.subMap(from, to).values().iterator();
39+
}
40+
}
41+
42+
Entry<MemorySegment> get(final MemorySegment key) {
43+
return map.get(key);
44+
}
45+
46+
Entry<MemorySegment> upsert(final Entry<MemorySegment> entry) {
47+
return map.put(entry.key(), entry);
48+
}
49+
}

0 commit comments

Comments
 (0)