Skip to content

Commit f885190

Browse files
committed
gh-409: Default codec registry cache
* Refactored the codec registry to use a CodecFinder (default to SPI definition in the classpath) * Provided 2 implementations of the codec finder, one without cache and another with cache * Added a build cache method that will attempt to fill the cache when the codecs are updated. This cannot covers all the cases like the nested arrays, therefore for those type the cache will be filled dynamically on per-request basis * Added microbenchmarks for codec encode and decode using the cache based implementation or not * Disabled unixDomainSocketTest IT when running on Mac or Windows
1 parent 2d79aa7 commit f885190

35 files changed

+959
-188
lines changed

pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
<junit.version>5.7.0</junit.version>
4242
<jmh.version>1.32</jmh.version>
4343
<mbr.version>0.2.0.RELEASE</mbr.version>
44-
<logback.version>1.2.3</logback.version>
45-
<mockito.version>3.11.2</mockito.version>
44+
<logback.version>1.2.5</logback.version>
45+
<mockito.version>3.12.4</mockito.version>
4646
<netty.version>4.1.66.Final</netty.version>
4747
<postgresql.version>42.2.23</postgresql.version>
4848
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -265,7 +265,7 @@
265265
<version>3.8.1</version>
266266
<configuration>
267267
<compilerArgs>
268-
<!-- <arg>-Werror</arg>-->
268+
<arg>-Werror</arg>
269269
<arg>-Xlint:all</arg>
270270
<arg>-Xlint:-deprecation</arg>
271271
<arg>-Xlint:-options</arg>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.r2dbc.postgresql;
18+
19+
import io.netty.buffer.ByteBufAllocator;
20+
import io.netty.buffer.UnpooledByteBufAllocator;
21+
import io.r2dbc.postgresql.codec.CodecFinderCacheImpl;
22+
import io.r2dbc.postgresql.codec.CodecFinderDefaultImpl;
23+
import io.r2dbc.postgresql.codec.Codecs;
24+
import io.r2dbc.postgresql.codec.DefaultCodecs;
25+
import io.r2dbc.postgresql.util.ByteBufUtils;
26+
import org.junit.platform.commons.annotation.Testable;
27+
import org.openjdk.jmh.annotations.Benchmark;
28+
import org.openjdk.jmh.annotations.BenchmarkMode;
29+
import org.openjdk.jmh.annotations.Mode;
30+
import org.openjdk.jmh.annotations.OutputTimeUnit;
31+
import org.openjdk.jmh.annotations.Param;
32+
import org.openjdk.jmh.annotations.Scope;
33+
import org.openjdk.jmh.annotations.State;
34+
import org.openjdk.jmh.infra.Blackhole;
35+
36+
import java.time.LocalDateTime;
37+
import java.util.concurrent.TimeUnit;
38+
39+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT4;
40+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT8;
41+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT8_ARRAY;
42+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT2;
43+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT2_ARRAY;
44+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT4;
45+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.INT4_ARRAY;
46+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.TIMESTAMP;
47+
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.VARCHAR;
48+
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
49+
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
50+
import static io.r2dbc.postgresql.util.TestByteBufAllocator.TEST;
51+
52+
/**
53+
* Benchmarks for codec encoding and decoding using cached enabled or disabled registries.
54+
*/
55+
@BenchmarkMode(Mode.Throughput)
56+
@OutputTimeUnit(TimeUnit.SECONDS)
57+
@Testable
58+
public class CodecRegistryBenchmarks extends BenchmarkSettings {
59+
60+
@State(Scope.Benchmark)
61+
public static class CodecRegistryHolder {
62+
63+
@Param({"10", "100", "1000"})
64+
public int iterations;
65+
66+
final ByteBufAllocator byteBufAllocator = new UnpooledByteBufAllocator(false, true);
67+
68+
DefaultCodecs cacheEnabledRegistry = new DefaultCodecs(byteBufAllocator, false, new CodecFinderCacheImpl());
69+
70+
DefaultCodecs cacheDisabledRegistry = new DefaultCodecs(byteBufAllocator, false, new CodecFinderDefaultImpl());
71+
72+
}
73+
74+
private void decode(Codecs codecs, int iterations, Blackhole voodoo) {
75+
for (int i = 0; i < iterations; i++) {
76+
voodoo.consume(codecs.decode(
77+
TEST.buffer(4).writeInt(200), INT4.getObjectId(), FORMAT_BINARY, Integer.class));
78+
voodoo.consume(codecs.decode(
79+
ByteBufUtils.encode(TEST, "100"), INT2.getObjectId(), FORMAT_TEXT, Short.class));
80+
voodoo.consume(codecs.decode(
81+
ByteBufUtils.encode(TEST, "-125.369"), FLOAT8.getObjectId(), FORMAT_TEXT, Double.class));
82+
voodoo.consume(codecs.decode(
83+
TEST.buffer(4).writeFloat(-65.369f), FLOAT4.getObjectId(), FORMAT_BINARY, Float.class));
84+
voodoo.consume(
85+
codecs.decode(
86+
ByteBufUtils.encode(TEST, "test"),
87+
VARCHAR.getObjectId(),
88+
FORMAT_TEXT,
89+
String.class));
90+
voodoo.consume(
91+
codecs.decode(
92+
ByteBufUtils.encode(TEST, "2018-11-04 15:35:00.847108"),
93+
TIMESTAMP.getObjectId(),
94+
FORMAT_TEXT,
95+
LocalDateTime.class));
96+
voodoo.consume(codecs.decode(ByteBufUtils.encode(TEST, "{100,200}"), INT2_ARRAY.getObjectId(), FORMAT_TEXT, Object.class));
97+
voodoo.consume(codecs.decode(ByteBufUtils.encode(TEST, "{100,200}"), INT4_ARRAY.getObjectId(), FORMAT_TEXT, Object.class));
98+
voodoo.consume(codecs.decode(ByteBufUtils.encode(TEST, "{100.5,200.8}"), FLOAT8_ARRAY.getObjectId(), FORMAT_TEXT, Object.class));
99+
}
100+
}
101+
102+
@Benchmark
103+
public void decodeWithCacheEnabledRegistry(CodecRegistryHolder holder, Blackhole voodoo) {
104+
decode(holder.cacheEnabledRegistry, holder.iterations, voodoo);
105+
}
106+
107+
@Benchmark
108+
public void decodeWithCacheDisabledRegistry(CodecRegistryHolder holder, Blackhole voodoo) {
109+
decode(holder.cacheDisabledRegistry, holder.iterations, voodoo);
110+
}
111+
112+
private void encode(Codecs codecs, int iterations, Blackhole voodoo) {
113+
for (int i = 0; i < iterations; i++) {
114+
voodoo.consume(codecs.encode((short) 12));
115+
voodoo.consume(codecs.encode(35698));
116+
voodoo.consume(codecs.encode(-256.3698));
117+
voodoo.consume(codecs.encode(85.7458f));
118+
voodoo.consume(codecs.encode("A text value"));
119+
voodoo.consume(codecs.encode(LocalDateTime.now()));
120+
voodoo.consume(codecs.encode(new Long[]{100L, 200L}));
121+
voodoo.consume(codecs.encode(new Double[]{100.5, 200.25}));
122+
voodoo.consume(codecs.encodeNull(Integer.class));
123+
voodoo.consume(codecs.encodeNull(String.class));
124+
voodoo.consume(codecs.encodeNull(Double[].class));
125+
}
126+
}
127+
128+
@Benchmark
129+
public void encodeWithCacheEnabledRegistry(CodecRegistryHolder holder, Blackhole voodoo) {
130+
encode(holder.cacheEnabledRegistry, holder.iterations, voodoo);
131+
}
132+
133+
@Benchmark
134+
public void encodeWithCacheDisabledRegistry(CodecRegistryHolder holder, Blackhole voodoo) {
135+
encode(holder.cacheDisabledRegistry, holder.iterations, voodoo);
136+
}
137+
138+
}

src/main/java/io/r2dbc/postgresql/codec/AbstractBinaryCodec.java

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.r2dbc.postgresql.util.ByteBufUtils;
2626

2727
import java.nio.charset.StandardCharsets;
28+
import java.util.Collections;
2829
import java.util.Objects;
2930
import java.util.regex.Matcher;
3031
import java.util.regex.Pattern;
@@ -74,6 +75,11 @@ public PostgresTypeIdentifier getArrayDataType() {
7475
return BYTEA_ARRAY;
7576
}
7677

78+
@Override
79+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
80+
return Collections.singleton(BYTEA);
81+
}
82+
7783
byte[] decode(Format format, ByteBuf byteBuf) {
7884
if (format == FORMAT_TEXT) {
7985
return decodeFromHex(byteBuf);

src/main/java/io/r2dbc/postgresql/codec/AbstractCodec.java

+11-17
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525
import reactor.core.publisher.Mono;
2626
import reactor.util.annotation.Nullable;
2727

28+
import java.util.EnumSet;
2829
import java.util.function.Supplier;
2930

3031
import static io.r2dbc.postgresql.client.EncodedParameter.NULL_VALUE;
32+
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
33+
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
3134

3235
/**
3336
* Abstract codec class that provides a basis for all concrete
@@ -97,8 +100,8 @@ public EncodedParameter encode(Object value, int dataType) {
97100
return doEncode((T) value, getDataType(dataType));
98101
}
99102

100-
PostgresTypeIdentifier getDataType(int dataType) {
101-
return PostgresqlObjectId.isValid(dataType) ? PostgresqlObjectId.valueOf(dataType) : new SimplePostgresTypeIdentifier(dataType);
103+
public static PostgresTypeIdentifier getDataType(int dataType) {
104+
return PostgresqlObjectId.isValid(dataType) ? PostgresqlObjectId.valueOf(dataType) : () -> dataType;
102105
}
103106

104107
public EncodedParameter encodeNull(int dataType) {
@@ -110,6 +113,12 @@ public Class<?> type() {
110113
return this.type;
111114
}
112115

116+
@Override
117+
public Iterable<Format> getFormats() {
118+
// Unless overridden all codecs supports both text and binary format
119+
return EnumSet.of(FORMAT_TEXT, FORMAT_BINARY);
120+
}
121+
113122
/**
114123
* Create a {@link EncodedParameter}.
115124
*
@@ -214,19 +223,4 @@ boolean isTypeAssignable(Class<?> type) {
214223
return type.isAssignableFrom(this.type);
215224
}
216225

217-
static class SimplePostgresTypeIdentifier implements PostgresTypeIdentifier {
218-
219-
private final int oid;
220-
221-
public SimplePostgresTypeIdentifier(int oid) {
222-
this.oid = oid;
223-
}
224-
225-
@Override
226-
public int getObjectId() {
227-
return this.oid;
228-
}
229-
230-
}
231-
232226
}

src/main/java/io/r2dbc/postgresql/codec/AbstractGeometryCodec.java

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.r2dbc.postgresql.util.ByteBufUtils;
2525

2626
import java.util.ArrayList;
27+
import java.util.Collections;
2728
import java.util.Iterator;
2829
import java.util.List;
2930

@@ -112,6 +113,11 @@ public EncodedParameter encodeNull() {
112113
return createNull(Format.FORMAT_BINARY, this.postgresqlObjectId);
113114
}
114115

116+
@Override
117+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
118+
return Collections.singleton(this.postgresqlObjectId);
119+
}
120+
115121
/**
116122
* Create a {@link TokenStream} given {@code content}.
117123
*

src/main/java/io/r2dbc/postgresql/codec/AbstractJsonCodec.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@
2020
import io.r2dbc.postgresql.message.Format;
2121
import io.r2dbc.postgresql.util.Assert;
2222

23+
import java.util.EnumSet;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
2327
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.JSON;
2428
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.JSONB;
2529
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
2630

2731
abstract class AbstractJsonCodec<T> extends AbstractCodec<T> {
2832

33+
private static final Set<PostgresqlObjectId> SUPPORTED_TYPES = EnumSet.of(JSON, JSONB);
34+
2935
AbstractJsonCodec(Class<T> type) {
3036
super(type);
3137
}
@@ -45,7 +51,12 @@ boolean doCanDecode(PostgresqlObjectId type, Format format) {
4551
Assert.requireNonNull(format, "format must not be null");
4652
Assert.requireNonNull(type, "type must not be null");
4753

48-
return JSONB == type || JSON == type;
54+
return SUPPORTED_TYPES.contains(type);
55+
}
56+
57+
@Override
58+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
59+
return SUPPORTED_TYPES.stream().map(PostgresTypeIdentifier.class::cast).collect(Collectors.toList());
4960
}
5061

5162
}

src/main/java/io/r2dbc/postgresql/codec/AbstractNumericCodec.java

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.EnumSet;
2828
import java.util.Set;
2929
import java.util.function.Function;
30+
import java.util.stream.Collectors;
3031

3132
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT4;
3233
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.FLOAT8;
@@ -144,6 +145,11 @@ public EncodedParameter encodeNull() {
144145
return createNull(FORMAT_BINARY, getDefaultType());
145146
}
146147

148+
@Override
149+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
150+
return SUPPORTED_TYPES.stream().map(PostgresTypeIdentifier.class::cast).collect(Collectors.toList());
151+
}
152+
147153
/**
148154
* Returns the {@link PostgresqlObjectId} for to identify whether this codec is the default codec.
149155
*

src/main/java/io/r2dbc/postgresql/codec/ArrayCodec.java

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.charset.StandardCharsets;
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
29+
import java.util.Collections;
2930
import java.util.List;
3031
import java.util.function.Function;
3132
import java.util.function.Supplier;
@@ -202,6 +203,11 @@ EncodedParameter doEncode(Object[] value, PostgresTypeIdentifier dataType) {
202203
}, dataType);
203204
}
204205

206+
@Override
207+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
208+
return Collections.singleton(this.dataType);
209+
}
210+
205211
/**
206212
* Create the encoded array representation.
207213
*

src/main/java/io/r2dbc/postgresql/codec/BlobCodec.java

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import reactor.util.annotation.Nullable;
2929

3030
import java.nio.ByteBuffer;
31+
import java.util.Collections;
3132

3233
import static io.r2dbc.postgresql.codec.PostgresqlObjectId.BYTEA;
3334
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
@@ -79,6 +80,11 @@ EncodedParameter doEncode(Blob value, PostgresTypeIdentifier dataType) {
7980
);
8081
}
8182

83+
@Override
84+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
85+
return Collections.singleton(BYTEA);
86+
}
87+
8288
private static final class ByteABlob implements Blob {
8389

8490
private final ByteBuf byteBuf;

src/main/java/io/r2dbc/postgresql/codec/BuiltinCodecSupport.java

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.r2dbc.postgresql.util.Assert;
2323
import io.r2dbc.postgresql.util.ByteBufUtils;
2424

25+
import java.util.Collections;
2526
import java.util.function.Function;
2627

2728
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
@@ -87,6 +88,11 @@ public final EncodedParameter encodeNull() {
8788
return createNull(FORMAT_TEXT, this.postgresType);
8889
}
8990

91+
@Override
92+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
93+
return Collections.singleton(this.postgresType);
94+
}
95+
9096
@Override
9197
public final PostgresTypeIdentifier getArrayDataType() {
9298
return this.postgresArrayType;

src/main/java/io/r2dbc/postgresql/codec/ByteCodec.java

+10
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,14 @@ public PostgresTypeIdentifier getArrayDataType() {
7878
return PostgresqlObjectId.INT2_ARRAY;
7979
}
8080

81+
@Override
82+
public Iterable<Format> getFormats() {
83+
return this.delegate.getFormats();
84+
}
85+
86+
@Override
87+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
88+
return this.delegate.getDataTypes();
89+
}
90+
8191
}

src/main/java/io/r2dbc/postgresql/codec/CharacterCodec.java

+10
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,14 @@ public PostgresTypeIdentifier getArrayDataType() {
8080
return PostgresqlObjectId.CHAR_ARRAY;
8181
}
8282

83+
@Override
84+
public Iterable<Format> getFormats() {
85+
return this.delegate.getFormats();
86+
}
87+
88+
@Override
89+
public Iterable<PostgresTypeIdentifier> getDataTypes() {
90+
return this.delegate.getDataTypes();
91+
}
92+
8393
}

0 commit comments

Comments
 (0)