Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/0.1.2 #77

Merged
merged 10 commits into from
Dec 16, 2021
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ with TCP sockets and a [RESP](https://redis.io/topics/protocol) protocol.
![Lines of code](https://img.shields.io/tokei/lines/github/keva-dev/keva?style=flat-square)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/keva-dev/keva?style=flat-square)
![GitHub](https://img.shields.io/github/license/keva-dev/keva?style=flat-square)
![Maven Central](https://img.shields.io/maven-central/v/dev.keva/kevadb?style=flat-square)

Major dependencies: [Netty](https://github.com/netty/netty), [ChronicleMap](https://github.com/OpenHFT/Chronicle-Map)

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ subprojects {
apply plugin: 'jacoco'

group 'dev.keva'
version '0.1.1'
version '0.1.2'

repositories {
mavenCentral()
Expand Down
13 changes: 9 additions & 4 deletions core/src/main/java/dev/keva/core/aof/AOFContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,26 @@ public void sync() throws IOException {
bufferLock.lock();
try {
for (Command command : buffer) {
output.writeObject(command);
output.writeObject(command.getObjects());
}
} finally {
output.flush();
for (Command command : buffer) {
command.recycle();
}
buffer.clear();
bufferLock.unlock();
}
}

public void syncPerMutation(Command command) {
try {
output.writeObject(command);
output.writeObject(command.getObjects());
output.flush();
} catch (IOException e) {
log.error("Error writing AOF file", e);
} finally {
command.recycle();
}
}

Expand All @@ -106,8 +111,8 @@ public List<Command> read() throws IOException {
ObjectInputStream input = new ObjectInputStream(fis);
while (true) {
try {
Command command = (Command) input.readObject();
commands.add(command);
byte[][] objects = (byte[][]) input.readObject();
commands.add(Command.newInstance(objects, false));
} catch (EOFException e) {
fis.close();
return commands;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void executeExpire(byte[] key) {
byte[][] data = new byte[2][];
data[0] = "delete".getBytes();
data[1] = key;
Command command = new Command(data, false);
Command command = Command.newInstance(data, false);
Lock lock = database.getLock();
lock.lock();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.SerializationException;
import org.reflections.Reflections;

import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -108,14 +109,19 @@ public void init() {
}
Object[] objects = new Object[types.length];
command.toArguments(objects, types, ctx);
// If not in AOF mode, then recycle(),
// else, the command will be recycled in the AOF dump
if (!kevaConfig.getAof()) {
command.recycle();
}
return (Reply<?>) method.invoke(instance, objects);
} finally {
lock.unlock();
}
} catch (Exception e) {
log.error(e.getMessage(), e);
if (e instanceof InvocationTargetException) {
if (e.getCause() instanceof ClassCastException) {
if (e.getCause() instanceof SerializationException || e.getCause() instanceof ClassCastException) {
return new ErrorReply("ERR WRONGTYPE Operation against a key holding the wrong kind of value");
}
return new ErrorReply("ERR " + e.getCause().getMessage());
Expand Down
33 changes: 33 additions & 0 deletions core/src/test/java/dev/keva/core/server/AbstractServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,39 @@ void del() {
}
}

@Test
void wrongTypeSet() {
try {
val setAbc = jedis.set("abc", "123");
assertEquals("OK", setAbc);
jedis.lpush("abc", "123");
} catch (Exception e) {
assertEquals(JedisDataException.class, e.getClass());
}
}

@Test
void wrongTypeSet2() {
try {
val setAbc = jedis.lpush("abc", "123");
assertEquals(1, setAbc);
jedis.hset("abc", "key", "val");
} catch (Exception e) {
assertEquals(JedisDataException.class, e.getClass());
}
}

@Test
void wrongTypeSet3() {
try {
val setAbc = jedis.hset("abc", "key", "val");
assertEquals(1, setAbc);
jedis.lpush("abc", "123");
} catch (Exception e) {
assertEquals(JedisDataException.class, e.getClass());
}
}

@Test
void transaction() {
try {
Expand Down
2 changes: 1 addition & 1 deletion docs/src/guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ with TCP sockets and a [RESP](https://redis.io/topics/protocol) protocol.

## Features

- High performance and low latency in-memory key-value database
- Low latency in-memory key-value database
- Compatible with Redis protocol
- Multithreading engine helps to maximize the use of system's resources
- Persistence in-memory data to disk
Expand Down
4 changes: 2 additions & 2 deletions docs/src/guide/insight/benchmark.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Benchmark

:::warning WIP
Because we're in early development stage, a more detailed test will be provided later (when Keva is more mature and stable)
Because we're in early development stage, a more detailed benchmark will be provided later (when Keva is more mature and stable)
:::

Keva's performance is on par with the Redis 6.2.6
Keva's throughput and latency (`GET`/`SET`) is on par with the Redis 6.2.6

Redis 6.2.6:

Expand Down
2 changes: 1 addition & 1 deletion docs/src/guide/overview/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Parameters:

## Docker

Available in [Docker Hub](https://hub.docker.com/r/kevadev/keva-server)
Available at [Docker Hub](https://hub.docker.com/r/kevadev/keva-server)

Pull image:

Expand Down
2 changes: 2 additions & 0 deletions docs/src/guide/overview/replication.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
:::warning WIP
This feature is under development
:::

This feature is not available yet.
3 changes: 2 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ features:
- title: High Throughput
details: Keva comes with multithreaded engine which will maximize the use of your system's resources
- title: High Availability
details: Simplify your high availability setup with master-replica nodes, sharded cluster mode will also be available soon
details: Simplify your high availability setup with master-replica nodes and sharded cluster mode (will be available soon)
footer: Apache-2.0 Licensed | Copyright © 2021 Keva Team
---

<p style="text-align: right; max-width: 960px; margin: auto;">
<img src="https://img.shields.io/github/workflow/status/keva-dev/keva/Build/master?label=build&style=flat-square" alt="GitHub Workflow Status (branch)">
<img src="https://img.shields.io/tokei/lines/github/keva-dev/keva?style=flat-square" alt="Lines of code">
<img src="https://img.shields.io/github/license/keva-dev/keva?style=flat-square" alt="GitHub">
<img src="https://img.shields.io/maven-central/v/dev.keva/kevadb?style=flat-square" alt="Maven Central">
</p>

<div style="margin-top: 2rem; max-width: 960px; text-align: center; font-size: 1.5rem; font-weight: bold;">Lab Product of</div>
Expand Down
15 changes: 15 additions & 0 deletions docs/src/team/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,19 @@ Keva project is built by a team of enthusiastic and passionate engineers formed
</p>
</td>
</tr>
<tr style="background: #fff">
<td><img src="https://avatars.githubusercontent.com/u/18006732?v=4" style="max-width: 100px;"></td>
<td>
<h3>An Nguyen</h3>
<p>Core Focus</p>
<p>
<img src="https://image.flaticon.com/icons/png/512/25/25231.png" style="max-width: 12.5px; margin-right: 2.5px;"/>
<a href="https://github.com/yampml" target="_blank">yampml</a>
</p>
<p>
<img src="https://i.imgur.com/dKTwo4A.jpg" style="max-width: 12.5px; margin-right: 2.5px;"/>
<a href="mailto:[email protected]" target="_blank">[email protected]</a>
</p>
</td>
</tr>
</table>
58 changes: 43 additions & 15 deletions resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,56 @@
package dev.keva.protocol.resp;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Recycler;
import lombok.Getter;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;

public class Command implements Serializable {
private static final byte LOWER_DIFF = 'a' - 'A';

private final Object[] objects;
private static final Recycler<Command> RECYCLER = new Recycler<Command>() {
protected Command newObject(Recycler.Handle<Command> handle) {
return new Command(handle);
}
};

private final Recycler.Handle<Command> handle;

@Getter
private byte[][] objects;

private Command(Recycler.Handle<Command> handle) {
this.handle = handle;
}

public Command(Object[] objects, boolean inline) {
public static Command newInstance(byte[][] objects, boolean inline) {
if (objects == null) {
throw new IllegalArgumentException("objects must not be null");
}
if (objects.length == 0) {
throw new IllegalArgumentException("objects must not be empty");
}

Command command = RECYCLER.get();
if (inline) {
byte[] objs = ByteUtil.getBytes(objects[0]);
byte[] objs = objects[0];
String[] strings = new String(objs, StandardCharsets.UTF_8).trim().split("\\s+");
objects = new Object[strings.length];
objects = new byte[strings.length][];
for (int i = 0; i < strings.length; i++) {
objects[i] = ByteUtil.getBytes(strings[i]);
}
}
this.objects = objects;
command.objects = objects;
// LowerCase bytes
for (int i = 0; i < objects[0].length; i++) {
byte b = objects[0][i];
if (b >= 'A' && b <= 'Z') {
objects[0][i] = (byte) (b + LOWER_DIFF);
}
}
return command;
}

public int getLength() {
Expand All @@ -31,15 +62,7 @@ public int getLength() {
}

public byte[] getName() {
byte[] name = ByteUtil.getBytes(objects[0]);
// LowerCase bytes
for (int i = 0; i < name.length; i++) {
byte b = name[i];
if (b >= 'A' && b <= 'Z') {
name[i] = (byte) (b + LOWER_DIFF);
}
}
return name;
return objects[0];
}

public void toArguments(Object[] arguments, Class<?>[] types, ChannelHandlerContext ctx) {
Expand All @@ -60,11 +83,16 @@ public void toArguments(Object[] arguments, Class<?>[] types, ChannelHandlerCont
int left = isFirstVararg ? (objects.length - position - 1) : (objects.length - 1);
byte[][] lastArgument = new byte[left][];
for (int i = 0; i < left; i++) {
lastArgument[i] = isFirstVararg ? (byte[]) (objects[i + position + 1]) : (byte[]) (objects[i + position]);
lastArgument[i] = isFirstVararg ? objects[i + position + 1] : objects[i + position];
}
arguments[position] = lastArgument;
}
position++;
}
}

public void recycle() {
objects = null;
handle.recycle(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
}
}
try {
out.add(new Command(bytes, false));
out.add(Command.newInstance(bytes, false));
} finally {
bytes = null;
arguments = 0;
Expand All @@ -60,7 +60,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
b[0] = new byte[buf.readableBytes()];
buf.getBytes(0, b[0]);
in.skipBytes(isCRLF ? 2 : 1);
out.add(new Command(b, true));
out.add(Command.newInstance(b, true));
}
}
}