Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'java'
id 'application'
id 'checkstyle'
}

group = 'org.example'
Expand All @@ -13,6 +14,7 @@ repositories {
dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.mockito:mockito-junit-jupiter:5.12.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand All @@ -26,4 +28,9 @@ tasks.withType(JavaCompile).configureEach {

test {
useJUnitPlatform()
systemProperty 'net.bytebuddy.experimental', 'true'
}

checkstyle {
configFile = file('config/checkstyle/checkstyle.xml')
}
18 changes: 18 additions & 0 deletions config/checkstyle/checkstyle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>

<module name="TreeWalker">
<module name="UnusedImports"/>
<module name="AvoidStarImport"/>
<module name="NeedBraces"/>
<module name="EmptyBlock"/>
</module>

<module name="LineLength">
<property name="max" value="140"/>
</module>
</module>
14 changes: 13 additions & 1 deletion src/main/java/org/cache/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.cache.core.LocalCache;
import org.cache.core.ValueType;
import org.cache.eviction.LruEvictionPolicy;
import org.cache.network.connection.ClientConnectionHandler;
import org.cache.network.TcpCacheServer;
import org.cache.protocol.CommandParser;
import org.cache.protocol.CommandProcessor;
Expand All @@ -12,8 +13,12 @@
import org.cache.protocol.codec.ValueCodecRegistry;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.Executors;

public class Main {
private static final int SERVER_THREAD_COUNT = 16;

public static void main(String[] args) throws IOException {
int port = args.length > 0 ? Integer.parseInt(args[0]) : 2020;
var cache = new LocalCache<String>(1_000, new LruEvictionPolicy<>());
Expand All @@ -24,7 +29,14 @@ public static void main(String[] args) throws IOException {
var commandProcessor = new CommandProcessor<>(cache, commandParser, valueCodecs);


try (cache; var server = new TcpCacheServer(port, commandProcessor)) {
try (cache;
var serverSocket = new ServerSocket(port);
var executor = Executors.newFixedThreadPool(SERVER_THREAD_COUNT);
var server = new TcpCacheServer(
serverSocket,
executor,
socket -> new ClientConnectionHandler(socket, commandProcessor)
)) {
server.start();
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/cache/client/CacheClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.cache.core.metrics.Snapshot;

import java.util.List;
import java.util.Optional;

public interface CacheClient<K, V> {
Expand All @@ -19,4 +20,10 @@ public interface CacheClient<K, V> {
int size();

void clear();

void push(K key, V value);

List<V> lrange(K key, int to);

List<V> lrange(K key, int from, int to);
}
35 changes: 35 additions & 0 deletions src/main/java/org/cache/client/TcpCacheClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class TcpCacheClient<K, V> implements CacheClient<K, V>, AutoCloseable {
private static final int FIRST_VALUE_INDEX = 1;
private static final int METRIC_NAME_VALUE_START_INDEX = 1;
private static final int METRIC_NAME_VALUE_PAIR_SIZE = 2;
private static final int DEFAULT_LRANGE_FROM_INDEX = 0;

private final RespConnection connection;
private final KeyCodec<K> keyCodec;
Expand All @@ -45,6 +46,12 @@ public TcpCacheClient(
}
}

TcpCacheClient(RespConnection connection, KeyCodec<K> keyCodec, Serializer<V> valueSerializer) {
this.connection = connection;
this.keyCodec = keyCodec;
this.valueSerializer = valueSerializer;
}

@Override
public void put(K key, V value, long ttl) {
expectOk(List.of("PUT", keyCodec.encode(key), valueSerializer.encode(value), Long.toString(ttl)));
Expand Down Expand Up @@ -127,6 +134,34 @@ public void clear() {
expectOk(List.of("CLEAR"));
}

@Override
public void push(K key, V value) {
expectOk(List.of("PUSH", keyCodec.encode(key), valueSerializer.encode(value)));
}

@Override
public List<V> lrange(K key, int to) {
return lrange(key, DEFAULT_LRANGE_FROM_INDEX, to);
}

@Override
public List<V> lrange(K key, int from, int to) {
List<String> response = send(List.of(
"LRANGE",
keyCodec.encode(key),
Integer.toString(from),
Integer.toString(to)
));

if (isResponse(response, ResponseConstants.NOT_FOUND.name(), SINGLE_PART_RESPONSE_SIZE)) {
return List.of();
}

return response.stream()
.map(valueSerializer::decode)
.toList();
}

@Override
public void close() {
try {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/cache/eviction/LruEvictionPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public synchronized void onKeyAccessed(K key) {
@Override
public synchronized void onKeyRemoved(K key) {
var removedNode = keyMap.get(key);
if (removedNode == null) return;
if (removedNode == null) {
return;
}

var prevNode = removedNode.prev;
var nextNode = removedNode.next;
Expand All @@ -67,4 +69,4 @@ public synchronized Optional<K> selectVictim() {

return Optional.ofNullable(selectedNode.value);
}
}
}
6 changes: 4 additions & 2 deletions src/main/java/org/cache/eviction/MruEvictionPolicy.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public synchronized void onKeyAccessed(K key) {
@Override
public synchronized void onKeyRemoved(K key) {
var removedNode = keyMap.get(key);
if (removedNode == null) return;
if (removedNode == null) {
return;
}

var prevNode = removedNode.prev;
var nextNode = removedNode.next;
Expand All @@ -67,4 +69,4 @@ public synchronized Optional<K> selectVictim() {

return Optional.ofNullable(selectedNode.value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.cache.network;

import java.net.Socket;

@FunctionalInterface
public interface ClientConnectionHandlerFactory {

Runnable create(Socket socket);
}
26 changes: 11 additions & 15 deletions src/main/java/org/cache/network/TcpCacheServer.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,32 @@
package org.cache.network;

import org.cache.network.connection.ClientConnectionHandler;
import org.cache.protocol.CommandProcessor;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpCacheServer implements AutoCloseable {

private final int port;
private final CommandProcessor<?> commandProcessor;
private final ExecutorService executor;
private final ClientConnectionHandlerFactory handlerFactory;
private ServerSocket serverSocket;

private static final int THREAD_COUNT = 16;

public TcpCacheServer(int port, CommandProcessor<?> commandProcessor) {
this.port = port;
this.commandProcessor = commandProcessor;
this.executor = Executors.newFixedThreadPool(THREAD_COUNT);
public TcpCacheServer(
ServerSocket serverSocket,
ExecutorService executor,
ClientConnectionHandlerFactory handlerFactory
) {
this.serverSocket = serverSocket;
this.executor = executor;
this.handlerFactory = handlerFactory;
}

public void start() throws IOException {
serverSocket = new ServerSocket(port);
System.out.println("TCP cache server listening on port " + port);
System.out.println("TCP cache server listening on port " + serverSocket.getLocalPort());

while (!Thread.currentThread().isInterrupted() && !serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
executor.submit(new ClientConnectionHandler(socket, commandProcessor));
executor.submit(handlerFactory.create(socket));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@

import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.cache.protocol.ProtocolConstants.*;
import static org.cache.protocol.ProtocolConstants.ARRAY_PREFIX;
import static org.cache.protocol.ProtocolConstants.BULK_STRING_PREFIX;
import static org.cache.protocol.ProtocolConstants.CARRIAGE_RETURN;
import static org.cache.protocol.ProtocolConstants.CRLF;
import static org.cache.protocol.ProtocolConstants.ERROR_PREFIX;
import static org.cache.protocol.ProtocolConstants.INTEGER_PREFIX;
import static org.cache.protocol.ProtocolConstants.SIMPLE_STRING_PREFIX;
import static org.cache.protocol.ProtocolConstants.NEW_LINE;
import static org.cache.protocol.RegexConstants.COMMA_WITH_OPTIONAL_WHITESPACE;
import static org.cache.protocol.RegexConstants.KEY_VALUE_SEPARATOR;
import static org.cache.protocol.RegexConstants.SPACE;
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/org/cache/protocol/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@

import org.cache.core.ValueType;
import org.cache.protocol.codec.KeyCodec;
import org.cache.protocol.commands.*;
import org.cache.protocol.commands.CacheCommand;
import org.cache.protocol.commands.ClearCommand;
import org.cache.protocol.commands.CommandType;
import org.cache.protocol.commands.DeleteCommand;
import org.cache.protocol.commands.GetCommand;
import org.cache.protocol.commands.InvalidCommand;
import org.cache.protocol.commands.LrangeCommand;
import org.cache.protocol.commands.MetricsCommand;
import org.cache.protocol.commands.PushCommand;
import org.cache.protocol.commands.PutCommand;
import org.cache.protocol.commands.SizeCommand;
import org.cache.protocol.commands.UnknownCommand;

import java.util.List;

Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/cache/protocol/codec/ListValueCodec.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.cache.protocol.codec;

import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/cache/protocol/commands/LrangeCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import java.util.List;
import java.util.Optional;

import static org.cache.protocol.commands.ResponseConstants.*;
import static org.cache.protocol.commands.ResponseConstants.ERROR;
import static org.cache.protocol.commands.ResponseConstants.LIST;
import static org.cache.protocol.commands.ResponseConstants.NOT_FOUND;

public class LrangeCommand<K> implements CacheCommand<K> {

Expand Down
2 changes: 0 additions & 2 deletions src/main/java/org/cache/protocol/commands/PutCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import org.cache.protocol.codec.ValueCodec;
import org.cache.protocol.codec.ValueCodecRegistry;

import java.util.Arrays;

import static org.cache.protocol.commands.ResponseConstants.OK;

public class PutCommand<K> implements CacheCommand<K> {
Expand Down
Loading