Skip to content

Commit 829231f

Browse files
committed
Improve codec caching.
Codecs are now cached using their value type for simple value/null encoding. Additionally, for decoding we check whether the cached codec can decode the desired format/data type/value type and fall back to the cache supplier if there was a hash collision. [resolves #511] Signed-off-by: Mark Paluch <[email protected]>
1 parent ff21b5a commit 829231f

File tree

1 file changed

+26
-24
lines changed

1 file changed

+26
-24
lines changed

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

+26-24
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class CachedCodecLookup implements CodecLookup {
4040

4141
private final Map<Integer, Codec<?>> decodeCodecsCache = new ConcurrentHashMap<>();
4242

43-
private final Map<Integer, Codec<?>> encodeCodecsCache = new ConcurrentHashMap<>();
43+
private final Map<Class<?>, Codec<?>> encodeCodecsCache = new ConcurrentHashMap<>();
4444

45-
private final Map<Integer, Codec<?>> encodeNullCodecsCache = new ConcurrentHashMap<>();
45+
private final Map<Class<?>, Codec<?>> encodeNullCodecsCache = new ConcurrentHashMap<>();
4646

4747
private final CodecLookup delegate;
4848

@@ -100,7 +100,7 @@ public void afterCodecAdded() {
100100
@Override
101101
public <T> Codec<T> findDecodeCodec(int dataType, Format format, Class<? extends T> type) {
102102
Integer hash = generateCodecHash(dataType, format, type);
103-
return findCodec(hash, this.decodeCodecsCache, () -> {
103+
return findCodec(hash, dataType, format, type, this.decodeCodecsCache, () -> {
104104
LOG.trace("[codec-finder dataType={}, format={}, type={}] Decode codec not found in cache", dataType, format, type.getName());
105105
Codec<T> c = this.delegate.findDecodeCodec(dataType, format, type);
106106
if (c != null) {
@@ -112,25 +112,23 @@ public <T> Codec<T> findDecodeCodec(int dataType, Format format, Class<? extends
112112

113113
@Override
114114
public <T> Codec<T> findEncodeCodec(T value) {
115-
Integer hash = generateCodecHash(value.getClass());
116-
return findCodec(hash, this.encodeCodecsCache, () -> {
115+
return findCodec(value.getClass(), this.encodeCodecsCache, () -> {
117116
LOG.trace("[codec-finder type={}] Encode codec not found in cache", value.getClass().getName());
118117
Codec<T> c = this.delegate.findEncodeCodec(value);
119118
if (c != null) {
120-
this.encodeCodecsCache.putIfAbsent(hash, c);
119+
this.encodeCodecsCache.putIfAbsent(value.getClass(), c);
121120
}
122121
return c;
123122
});
124123
}
125124

126125
@Override
127126
public <T> Codec<T> findEncodeNullCodec(Class<T> type) {
128-
Integer hash = generateCodecHash(type);
129-
return findCodec(hash, this.encodeNullCodecsCache, () -> {
127+
return findCodec(type, this.encodeNullCodecsCache, () -> {
130128
LOG.trace("[codec-finder type={}] Encode null codec not found in cache", type.getName());
131129
Codec<T> c = this.delegate.findEncodeNullCodec(type);
132130
if (c != null) {
133-
this.encodeNullCodecsCache.putIfAbsent(hash, c);
131+
this.encodeNullCodecsCache.putIfAbsent(type, c);
134132
}
135133
return c;
136134
});
@@ -142,32 +140,36 @@ private void cacheDecode(Codec<?> c, Class<?> type, PostgresTypeIdentifier ident
142140
}
143141

144142
private void cacheEncode(Codec<?> c, Class<?> type) {
145-
Integer encodeHash = generateCodecHash(type);
146-
this.encodeCodecsCache.putIfAbsent(encodeHash, c);
143+
this.encodeCodecsCache.putIfAbsent(type, c);
147144
if (c.canEncodeNull(type)) {
148-
this.encodeNullCodecsCache.putIfAbsent(encodeHash, c);
145+
this.encodeNullCodecsCache.putIfAbsent(type, c);
149146
}
150147
}
151148

152149
@SuppressWarnings("unchecked")
153-
private synchronized <T> Codec<T> findCodec(Integer codecHash, Map<Integer, Codec<?>> cache, Supplier<Codec<T>> fallback) {
154-
Codec<T> value = (Codec<T>) cache.get(codecHash);
150+
private synchronized <T> Codec<T> findCodec(Class<?> cacheKey, Map<Class<?>, Codec<?>> cache, Supplier<Codec<T>> fallback) {
151+
Codec<T> value = (Codec<T>) cache.get(cacheKey);
155152
return value != null ? value : fallback.get();
156153
}
157154

158-
private static Integer generateCodecHash(int dataType, Format format, Class<?> type) {
159-
int hash = (dataType << 5) - dataType;
160-
hash = (hash << 5) - hash + format.hashCode();
161-
hash = (hash << 5) - hash + generateCodecHash(type);
162-
return hash;
155+
@SuppressWarnings("unchecked")
156+
private synchronized <T> Codec<T> findCodec(Integer cacheKey, int dataType, Format format, Class<? extends T> type, Map<Integer, Codec<?>> cache, Supplier<Codec<T>> fallback) {
157+
Codec<T> value = (Codec<T>) cache.get(cacheKey);
158+
return (value != null && value.canDecode(dataType, format, type)) ? value : fallback.get();
163159
}
164160

165-
private static Integer generateCodecHash(Class<?> type) {
166-
int hash = type.hashCode();
167-
if (type.getComponentType() != null) {
168-
hash = (hash << 5) - hash + generateCodecHash(type.getComponentType());
161+
private static Integer generateCodecHash(int dataType, Format format, Class<?> type) {
162+
int result = 1;
163+
164+
result = 31 * result + dataType;
165+
result = 31 * result + format.hashCode();
166+
result = 31 * result + type.hashCode();
167+
168+
if (type.isArray()) {
169+
result = 31 * result + type.getComponentType().hashCode();
169170
}
170-
return hash;
171+
172+
return result;
171173
}
172174

173175
}

0 commit comments

Comments
 (0)