Repository: cassandra
Updated Branches:
  refs/heads/trunk b63e6eef5 -> 6dc1004ce


Make SELECT JSON and toJson() threadsafe

Patch by Ivan Ryndin and Tyler Hobbs; reviewed by Sergio Bossa for
CASSANDRA-11048


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/6b1bd174
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/6b1bd174
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/6b1bd174

Branch: refs/heads/trunk
Commit: 6b1bd1745ab9f04f9e379e6d22764b97693ba2ad
Parents: 90fc896
Author: Tyler Hobbs <tylerlho...@gmail.com>
Authored: Thu Feb 11 10:26:47 2016 -0600
Committer: Tyler Hobbs <tylerlho...@gmail.com>
Committed: Thu Feb 11 10:26:47 2016 -0600

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 src/java/org/apache/cassandra/cql3/Json.java    | 10 ++-
 .../cassandra/cql3/selection/Selection.java     |  2 +-
 .../apache/cassandra/db/marshal/AsciiType.java  |  2 +-
 .../apache/cassandra/db/marshal/MapType.java    |  2 +-
 .../apache/cassandra/db/marshal/UTF8Type.java   |  2 +-
 .../apache/cassandra/db/marshal/UserType.java   |  2 +-
 .../cql3/validation/entities/JsonTest.java      | 70 +++++++++++++++++---
 8 files changed, 76 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 5674b9d..3aca720 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.6
+ * Make SELECT JSON and toJson() threadsafe (CASSANDRA-11048)
  * Fix SELECT on tuple relations for mixed ASC/DESC clustering order 
(CASSANDRA-7281)
  * (cqlsh) Support utf-8/cp65001 encoding on Windows (CASSANDRA-11030)
  * Fix paging on DISTINCT queries repeats result when first row in partition 
changes (CASSANDRA-10010)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/cql3/Json.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Json.java 
b/src/java/org/apache/cassandra/cql3/Json.java
index 5284793..78fbd08 100644
--- a/src/java/org/apache/cassandra/cql3/Json.java
+++ b/src/java/org/apache/cassandra/cql3/Json.java
@@ -35,10 +35,16 @@ public class Json
 {
     public static final ObjectMapper JSON_OBJECT_MAPPER = new ObjectMapper();
 
-    public static final JsonStringEncoder JSON_STRING_ENCODER = new 
JsonStringEncoder();
-
     public static final ColumnIdentifier JSON_COLUMN_ID = new 
ColumnIdentifier("[json]", true);
 
+    /**
+     * Quotes string contents using standard JSON quoting.
+     */
+    public static String quoteAsJsonString(String s)
+    {
+        return new String(JsonStringEncoder.getInstance().quoteAsString(s));
+    }
+
     public static Object decodeJson(String json)
     {
         try

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/cql3/selection/Selection.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java 
b/src/java/org/apache/cassandra/cql3/selection/Selection.java
index f6925b2..6016059 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selection.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java
@@ -375,7 +375,7 @@ public abstract class Selection
 
                 ByteBuffer buffer = row.get(i);
                 sb.append('"');
-                sb.append(Json.JSON_STRING_ENCODER.quoteAsString(columnName));
+                sb.append(Json.quoteAsJsonString(columnName));
                 sb.append("\": ");
                 if (buffer == null)
                     sb.append("null");

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/db/marshal/AsciiType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AsciiType.java 
b/src/java/org/apache/cassandra/db/marshal/AsciiType.java
index 953fc09..2356c1c 100644
--- a/src/java/org/apache/cassandra/db/marshal/AsciiType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AsciiType.java
@@ -88,7 +88,7 @@ public class AsciiType extends AbstractType<String>
     {
         try
         {
-            return '"' + new 
String(Json.JSON_STRING_ENCODER.quoteAsString(ByteBufferUtil.string(buffer, 
Charset.forName("US-ASCII")))) + '"';
+            return '"' + Json.quoteAsJsonString(ByteBufferUtil.string(buffer, 
Charset.forName("US-ASCII"))) + '"';
         }
         catch (CharacterCodingException exc)
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/db/marshal/MapType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/MapType.java 
b/src/java/org/apache/cassandra/db/marshal/MapType.java
index 22f4df1..434702a 100644
--- a/src/java/org/apache/cassandra/db/marshal/MapType.java
+++ b/src/java/org/apache/cassandra/db/marshal/MapType.java
@@ -244,7 +244,7 @@ public class MapType<K, V> extends CollectionType<Map<K, V>>
             if (key.startsWith("\""))
                 sb.append(key);
             else
-                
sb.append('"').append(Json.JSON_STRING_ENCODER.quoteAsString(key)).append('"');
+                sb.append('"').append(Json.quoteAsJsonString(key)).append('"');
 
             sb.append(": ");
             
sb.append(values.toJSONString(CollectionSerializer.readValue(buffer, 
protocolVersion), protocolVersion));

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java 
b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
index 3fd175c..3b93d9a 100644
--- a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
@@ -67,7 +67,7 @@ public class UTF8Type extends AbstractType<String>
     {
         try
         {
-            return '"' + new 
String(Json.JSON_STRING_ENCODER.quoteAsString(ByteBufferUtil.string(buffer, 
Charset.forName("UTF-8")))) + '"';
+            return '"' + Json.quoteAsJsonString(ByteBufferUtil.string(buffer, 
Charset.forName("UTF-8"))) + '"';
         }
         catch (CharacterCodingException exc)
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java 
b/src/java/org/apache/cassandra/db/marshal/UserType.java
index 23e2790..40a35b5 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -206,7 +206,7 @@ public class UserType extends TupleType
                 name = "\"" + name + "\"";
 
             sb.append('"');
-            sb.append(Json.JSON_STRING_ENCODER.quoteAsString(name));
+            sb.append(Json.quoteAsJsonString(name));
             sb.append("\": ");
 
             ByteBuffer valueBuffer = buffers[i];

http://git-wip-us.apache.org/repos/asf/cassandra/blob/6b1bd174/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
----------------------------------------------------------------------
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
index 7f8fa0b..57df1e6 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.serializers.SimpleDateSerializer;
 import org.apache.cassandra.serializers.TimeSerializer;
 import org.apache.cassandra.utils.ByteBufferUtil;
 
+import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -32,11 +33,12 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.UUID;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 public class JsonTest extends CQLTester
@@ -927,11 +929,11 @@ public class JsonTest extends CQLTester
 
         // map<set<text>, text> keys
         String innerKey1 = "[\"0\", \"1\"]";
-        String fullKey1 = String.format("{\"%s\": \"%s\"}", new 
String(Json.JSON_STRING_ENCODER.quoteAsString(innerKey1)), "a");
-        String stringKey1 = new 
String(Json.JSON_STRING_ENCODER.quoteAsString(fullKey1));
+        String fullKey1 = String.format("{\"%s\": \"%s\"}", 
Json.quoteAsJsonString(innerKey1), "a");
+        String stringKey1 = Json.quoteAsJsonString(fullKey1);
         String innerKey2 = "[\"3\", \"4\"]";
-        String fullKey2 = String.format("{\"%s\": \"%s\"}", new 
String(Json.JSON_STRING_ENCODER.quoteAsString(innerKey2)), "b");
-        String stringKey2 = new 
String(Json.JSON_STRING_ENCODER.quoteAsString(fullKey2));
+        String fullKey2 = String.format("{\"%s\": \"%s\"}", 
Json.quoteAsJsonString(innerKey2), "b");
+        String stringKey2 = Json.quoteAsJsonString(fullKey2);
         execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"nestedsetmap\": {\"" + 
stringKey1 + "\": true, \"" + stringKey2 + "\": false}}");
         assertRows(execute("SELECT JSON k, nestedsetmap FROM %s"), 
row("{\"k\": 0, \"nestedsetmap\": {\"" + stringKey1 + "\": true, \"" + 
stringKey2 + "\": false}}"));
 
@@ -955,4 +957,56 @@ public class JsonTest extends CQLTester
         execute("INSERT INTO %s JSON ?", "{\"k\": 0, \"a\": {\"a\": 0, \"b\": 
[1, 2, 3], \"c\": null}, \"b\": null}");
         assertRows(execute("SELECT k, a.a, a.b, a.c, b FROM %s"), row(0, 0, 
set(1, 2, 3), null, null));
     }
+
+    // done for CASSANDRA-11048
+    @Test
+    public void testJsonTreadSafety() throws Throwable
+    {
+        int numThreads = 10;
+        final int numRows = 10000;
+
+        createTable("CREATE TABLE %s (" +
+                "k text PRIMARY KEY, " +
+                "v text)");
+
+        for (int i = 0; i < numRows; i++)
+            execute("INSERT INTO %s (k, v) VALUES (?, ?)", "" + i, "" + i);
+
+        long seed = System.nanoTime();
+        System.out.println("Seed " + seed);
+        final Random rand = new Random(seed);
+
+        final Runnable worker = new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    for (int i = 0; i < numRows; i++)
+                    {
+                        String key = "" + rand.nextInt(numRows);
+                        assertRows(execute("SELECT JSON * FROM %s WHERE k = 
?", key),
+                                row(String.format("{\"k\": \"%s\", \"v\": 
\"%s\"}", key, key)));
+                    }
+                }
+                catch (Throwable exc)
+                {
+                    exc.printStackTrace();
+                    fail(exc.getMessage());
+                }
+            }
+        };
+
+        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+        List<Future> futures = new ArrayList<>();
+        for (int i = 0; i < numThreads; i++)
+            futures.add(executor.submit(worker));
+
+        for (Future future : futures)
+            future.get(10, TimeUnit.SECONDS);
+
+        executor.shutdown();
+        Assert.assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
+    }
 }

Reply via email to