Skip to content

Commit 43f61e0

Browse files
authored
add Netty Thread-local object pool (#72)
1 parent 1a34c94 commit 43f61e0

File tree

5 files changed

+60
-22
lines changed

5 files changed

+60
-22
lines changed

core/src/main/java/dev/keva/core/aof/AOFContainer.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,21 +81,26 @@ public void sync() throws IOException {
8181
bufferLock.lock();
8282
try {
8383
for (Command command : buffer) {
84-
output.writeObject(command);
84+
output.writeObject(command.getObjects());
8585
}
8686
} finally {
8787
output.flush();
88+
for (Command command : buffer) {
89+
command.recycle();
90+
}
8891
buffer.clear();
8992
bufferLock.unlock();
9093
}
9194
}
9295

9396
public void syncPerMutation(Command command) {
9497
try {
95-
output.writeObject(command);
98+
output.writeObject(command.getObjects());
9699
output.flush();
97100
} catch (IOException e) {
98101
log.error("Error writing AOF file", e);
102+
} finally {
103+
command.recycle();
99104
}
100105
}
101106

@@ -106,8 +111,8 @@ public List<Command> read() throws IOException {
106111
ObjectInputStream input = new ObjectInputStream(fis);
107112
while (true) {
108113
try {
109-
Command command = (Command) input.readObject();
110-
commands.add(command);
114+
byte[][] objects = (byte[][]) input.readObject();
115+
commands.add(Command.newInstance(objects, false));
111116
} catch (EOFException e) {
112117
fis.close();
113118
return commands;

core/src/main/java/dev/keva/core/command/impl/key/manager/ExpirationManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void executeExpire(byte[] key) {
7070
byte[][] data = new byte[2][];
7171
data[0] = "delete".getBytes();
7272
data[1] = key;
73-
Command command = new Command(data, false);
73+
Command command = Command.newInstance(data, false);
7474
Lock lock = database.getLock();
7575
lock.lock();
7676
try {

core/src/main/java/dev/keva/core/command/mapping/CommandMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ public void init() {
108108
}
109109
Object[] objects = new Object[types.length];
110110
command.toArguments(objects, types, ctx);
111+
// If not in AOF mode, then recycle(),
112+
// else, the command will be recycled in the AOF dump
113+
if (!kevaConfig.getAof()) {
114+
command.recycle();
115+
}
111116
return (Reply<?>) method.invoke(instance, objects);
112117
} finally {
113118
lock.unlock();

resp-protocol/src/main/java/dev/keva/protocol/resp/Command.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
11
package dev.keva.protocol.resp;
22

33
import io.netty.channel.ChannelHandlerContext;
4+
import io.netty.util.Recycler;
5+
import lombok.Getter;
46

57
import java.io.Serializable;
68
import java.nio.charset.StandardCharsets;
79

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

11-
private final Object[] objects;
13+
private static final Recycler<Command> RECYCLER = new Recycler<Command>() {
14+
protected Command newObject(Recycler.Handle<Command> handle) {
15+
return new Command(handle);
16+
}
17+
};
18+
19+
private final Recycler.Handle<Command> handle;
20+
21+
@Getter
22+
private byte[][] objects;
23+
24+
private Command(Recycler.Handle<Command> handle) {
25+
this.handle = handle;
26+
}
1227

13-
public Command(Object[] objects, boolean inline) {
28+
public static Command newInstance(byte[][] objects, boolean inline) {
29+
if (objects == null) {
30+
throw new IllegalArgumentException("objects must not be null");
31+
}
32+
if (objects.length == 0) {
33+
throw new IllegalArgumentException("objects must not be empty");
34+
}
35+
36+
Command command = RECYCLER.get();
1437
if (inline) {
15-
byte[] objs = ByteUtil.getBytes(objects[0]);
38+
byte[] objs = objects[0];
1639
String[] strings = new String(objs, StandardCharsets.UTF_8).trim().split("\\s+");
17-
objects = new Object[strings.length];
40+
objects = new byte[strings.length][];
1841
for (int i = 0; i < strings.length; i++) {
1942
objects[i] = ByteUtil.getBytes(strings[i]);
2043
}
2144
}
22-
this.objects = objects;
45+
command.objects = objects;
46+
// LowerCase bytes
47+
for (int i = 0; i < objects[0].length; i++) {
48+
byte b = objects[0][i];
49+
if (b >= 'A' && b <= 'Z') {
50+
objects[0][i] = (byte) (b + LOWER_DIFF);
51+
}
52+
}
53+
return command;
2354
}
2455

2556
public int getLength() {
@@ -31,15 +62,7 @@ public int getLength() {
3162
}
3263

3364
public byte[] getName() {
34-
byte[] name = ByteUtil.getBytes(objects[0]);
35-
// LowerCase bytes
36-
for (int i = 0; i < name.length; i++) {
37-
byte b = name[i];
38-
if (b >= 'A' && b <= 'Z') {
39-
name[i] = (byte) (b + LOWER_DIFF);
40-
}
41-
}
42-
return name;
65+
return objects[0];
4366
}
4467

4568
public void toArguments(Object[] arguments, Class<?>[] types, ChannelHandlerContext ctx) {
@@ -60,11 +83,16 @@ public void toArguments(Object[] arguments, Class<?>[] types, ChannelHandlerCont
6083
int left = isFirstVararg ? (objects.length - position - 1) : (objects.length - 1);
6184
byte[][] lastArgument = new byte[left][];
6285
for (int i = 0; i < left; i++) {
63-
lastArgument[i] = isFirstVararg ? (byte[]) (objects[i + position + 1]) : (byte[]) (objects[i + position]);
86+
lastArgument[i] = isFirstVararg ? objects[i + position + 1] : objects[i + position];
6487
}
6588
arguments[position] = lastArgument;
6689
}
6790
position++;
6891
}
6992
}
93+
94+
public void recycle() {
95+
objects = null;
96+
handle.recycle(this);
97+
}
7098
}

resp-protocol/src/main/java/dev/keva/protocol/resp/RedisCommandDecoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
3434
}
3535
}
3636
try {
37-
out.add(new Command(bytes, false));
37+
out.add(Command.newInstance(bytes, false));
3838
} finally {
3939
bytes = null;
4040
arguments = 0;
@@ -60,7 +60,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
6060
b[0] = new byte[buf.readableBytes()];
6161
buf.getBytes(0, b[0]);
6262
in.skipBytes(isCRLF ? 2 : 1);
63-
out.add(new Command(b, true));
63+
out.add(Command.newInstance(b, true));
6464
}
6565
}
6666
}

0 commit comments

Comments
 (0)