Skip to content

Commit 1bfacef

Browse files
committed
INTERNAL: add option to disable optimization in CollectionTranscoder
1 parent 75d8e5d commit 1bfacef

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

src/main/java/net/spy/memcached/transcoders/CollectionTranscoder.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class CollectionTranscoder extends SerializingTranscoder implements
3434
*/
3535
public static final int MAX_ELEMENT_BYTES = 32 * 1024;
3636

37+
private boolean optimize = true;
38+
3739
/**
3840
* Get a serializing transcoder with the default max data size.
3941
*/
@@ -52,6 +54,11 @@ public CollectionTranscoder(int max, ClassLoader cl) {
5254
super(max, cl);
5355
}
5456

57+
private CollectionTranscoder(int max, ClassLoader cl, boolean optimize) {
58+
super(max, cl);
59+
this.optimize = optimize;
60+
}
61+
5562
public static int examineFlags(ElementValueType type) {
5663
int flags = 0;
5764
if (type == ElementValueType.STRING) {
@@ -78,8 +85,14 @@ public static int examineFlags(ElementValueType type) {
7885
return flags;
7986
}
8087

88+
@Override
8189
public Object decode(CachedData d) {
8290
byte[] data = d.getData();
91+
92+
if (!optimize) {
93+
return deserialize(data);
94+
}
95+
8396
Object rv = null;
8497
int flags = d.getFlags() & SPECIAL_MASK;
8598
if ((d.getFlags() & SERIALIZED) != 0 && data != null) {
@@ -119,7 +132,12 @@ public Object decode(CachedData d) {
119132
return rv;
120133
}
121134

135+
@Override
122136
public CachedData encode(Object o) {
137+
if (!optimize) {
138+
return new CachedData(SERIALIZED, serialize(o), getMaxSize());
139+
}
140+
123141
byte[] b = null;
124142
int flags = 0;
125143
if (o instanceof String) {
@@ -155,4 +173,36 @@ public CachedData encode(Object o) {
155173
assert b != null;
156174
return new CachedData(flags, b, getMaxSize());
157175
}
176+
177+
public static class Builder {
178+
private int max = MAX_ELEMENT_BYTES;
179+
private boolean optimize = true;
180+
private ClassLoader cl;
181+
182+
public Builder setMaxElementBytes(int max) {
183+
this.max = max;
184+
return this;
185+
}
186+
187+
/**
188+
* By default, this transcoder uses Java serialization only if the type is a user-defined class.
189+
* This mechanism may cause malfunction if you store Object type values
190+
* into an Arcus collection item and the values are actually
191+
* different types like String, Integer.
192+
* In this case, you should disable optimization.
193+
*/
194+
public Builder disableOptimization() {
195+
this.optimize = false;
196+
return this;
197+
}
198+
199+
public Builder setClassLoader(ClassLoader cl) {
200+
this.cl = cl;
201+
return this;
202+
}
203+
204+
public CollectionTranscoder build() {
205+
return new CollectionTranscoder(max, cl, optimize);
206+
}
207+
}
158208
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package net.spy.memcached.collection.transcoder;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ExecutionException;
5+
6+
import net.spy.memcached.collection.BaseIntegrationTest;
7+
import net.spy.memcached.collection.CollectionAttributes;
8+
import net.spy.memcached.transcoders.CollectionTranscoder;
9+
10+
import org.junit.jupiter.api.AfterEach;
11+
import org.junit.jupiter.api.Test;
12+
13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
17+
class CollectionTranscoderTest extends BaseIntegrationTest {
18+
19+
private static final String KEY = "CollectionTranscoderTest";
20+
21+
private final CollectionTranscoder transcoder = new CollectionTranscoder.Builder()
22+
.disableOptimization()
23+
.build();
24+
25+
@AfterEach
26+
@Override
27+
protected void tearDown() throws Exception {
28+
mc.delete(KEY);
29+
super.tearDown();
30+
}
31+
32+
@Test
33+
void decodeIntegerAsString() throws ExecutionException, InterruptedException {
34+
// given
35+
Boolean b1 = mc.asyncMopInsert(KEY, "mkey1", "value", new CollectionAttributes()).get();
36+
Boolean b2 = mc.asyncMopInsert(KEY, "mkey2", 35, new CollectionAttributes()).get();
37+
assertTrue(b1);
38+
assertTrue(b2);
39+
40+
// when
41+
Map<String, Object> map1 = mc.asyncMopGet(KEY, "mkey1", false, false).get();
42+
Map<String, Object> map2 = mc.asyncMopGet(KEY, "mkey2", false, false).get();
43+
44+
// then
45+
assertEquals("value", map1.get("mkey1"));
46+
assertEquals("#", map2.get("mkey2"));
47+
}
48+
49+
@Test
50+
void failToDecodeString() throws ExecutionException, InterruptedException {
51+
// given
52+
Boolean b2 = mc.asyncMopInsert(KEY, "mkey3", 35, new CollectionAttributes())
53+
.get();
54+
Boolean b1 = mc.asyncMopInsert(KEY, "mkey4", "value", new CollectionAttributes())
55+
.get();
56+
assertTrue(b1);
57+
assertTrue(b2);
58+
59+
// when & then
60+
Map<String, Object> map1 = mc.asyncMopGet(KEY, "mkey3", false, false).get();
61+
assertEquals(35, map1.get("mkey3"));
62+
assertThrows(AssertionError.class, () -> mc.asyncMopGet(KEY, "mkey4", false, false).get());
63+
}
64+
65+
@Test
66+
void decodeStringAndInteger() throws ExecutionException, InterruptedException {
67+
// given
68+
Boolean b1 = mc.asyncMopInsert(KEY, "mkey1", "value", new CollectionAttributes(), transcoder)
69+
.get();
70+
Boolean b2 = mc.asyncMopInsert(KEY, "mkey2", 35, new CollectionAttributes(), transcoder)
71+
.get();
72+
assertTrue(b1);
73+
assertTrue(b2);
74+
75+
// when
76+
Map<String, Object> map1 = mc.asyncMopGet(KEY, "mkey1", false, false, transcoder).get();
77+
Map<String, Object> map2 = mc.asyncMopGet(KEY, "mkey2", false, false, transcoder).get();
78+
79+
// then
80+
assertEquals("value", map1.get("mkey1"));
81+
assertEquals(35, map2.get("mkey2"));
82+
}
83+
}

0 commit comments

Comments
 (0)