AlexPikalov / cdrs

Cassandra DB native client written in Rust language. Find 1.x versions on https://github.com/AlexPikalov/cdrs/tree/v.1.x Looking for an async version? - Check WIP https://github.com/AlexPikalov/cdrs-async

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Updating a set of udt results with cluster error

krojew opened this issue · comments

If a table contains a set of frozen UDT and we execute update table set column = column + ? where ... and pass a UDT using query_values, the cluster responds with Server error: CString { string: "java.lang.IllegalArgumentException" }. Executing the same query using text, results in a success.

The stack trace in Cassandra is:

java.lang.IllegalArgumentException: null
cassandra_1         |   at java.nio.Buffer.limit(Buffer.java:275) ~[na:1.8.0_222]
cassandra_1         |   at org.apache.cassandra.utils.ByteBufferUtil.readBytes(ByteBufferUtil.java:651) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.serializers.CollectionSerializer.readValue(CollectionSerializer.java:105) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.serializers.SetSerializer.deserializeForNativeProtocol(SetSerializer.java:101) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.Sets$Value.fromSerialized(Sets.java:158) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.Sets$Marker.bind(Sets.java:251) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.Sets$Adder.execute(Sets.java:285) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.statements.UpdateStatement.addUpdateForKey(UpdateStatement.java:94) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.statements.ModificationStatement.addUpdates(ModificationStatement.java:694) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.statements.ModificationStatement.getMutations(ModificationStatement.java:635) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.statements.ModificationStatement.executeWithoutCondition(ModificationStatement.java:437) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.statements.ModificationStatement.execute(ModificationStatement.java:425) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.QueryProcessor.processStatement(QueryProcessor.java:225) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.QueryProcessor.processPrepared(QueryProcessor.java:532) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.cql3.QueryProcessor.processPrepared(QueryProcessor.java:509) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.transport.messages.ExecuteMessage.execute(ExecuteMessage.java:146) ~[apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.transport.Message$Dispatcher.channelRead0(Message.java:566) [apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.transport.Message$Dispatcher.channelRead0(Message.java:410) [apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [netty-all-4.0.44.Final.jar:4.0.44.Final]
cassandra_1         |   at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:357) [netty-all-4.0.44.Final.jar:4.0.44.Final]
cassandra_1         |   at io.netty.channel.AbstractChannelHandlerContext.access$600(AbstractChannelHandlerContext.java:35) [netty-all-4.0.44.Final.jar:4.0.44.Final]
cassandra_1         |   at io.netty.channel.AbstractChannelHandlerContext$7.run(AbstractChannelHandlerContext.java:348) [netty-all-4.0.44.Final.jar:4.0.44.Final]
cassandra_1         |   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_222]
cassandra_1         |   at org.apache.cassandra.concurrent.AbstractLocalAwareExecutorService$FutureTask.run(AbstractLocalAwareExecutorService.java:162) [apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at org.apache.cassandra.concurrent.SEPWorker.run(SEPWorker.java:114) [apache-cassandra-3.11.4.jar:3.11.4]
cassandra_1         |   at java.lang.Thread.run(Thread.java:748) [na:1.8.0_222]

Hello @krojew

Would you be able to provide a full code snippet including table schemas that may help to reproduce the issue?

Thanks

DB preparation:

CREATE TYPE udt1 (
    id uuid,
    col1 text,
    col2 tinyint,
    col3 tinyint
);

CREATE TABLE table1 (
    id uuid PRIMARY KEY,
    udts set<frozen<udt1>>
);

INSERT INTO table1 (id, udts) VALUES (5bd8877a-e2b2-4d6f-aafd-c3f72a6964cf, {{
  id: 08f49fa5-934b-4aff-8a87-f3a3287296ba,
  col1: 'text',
  col2: 1,
  col3: 2
}});

Rust:

use cdrs::authenticators::StaticPasswordAuthenticator;
use cdrs::cluster::session::{new as new_session};
use cdrs::cluster::{ClusterTcpConfig, NodeTcpConfigBuilder};
use cdrs::consistency::Consistency;
use cdrs::frame::traits::IntoBytes;
use cdrs::load_balancing::RoundRobin;
use cdrs::query::*;
use cdrs::query_values;
use cdrs::types::prelude::*;
use cdrs_helpers_derive::*;
use uuid::Uuid;

#[derive(IntoCDRSValue)]
struct Model {
  id: Uuid,
  col1: String,
  col2: i8,
  col3: i8,
}

fn main() {
    log4rs::init_file("log4rs.yml", Default::default()).expect("Error initializing log4rs");

    let node = NodeTcpConfigBuilder::new(
        "localhost:9042",
        StaticPasswordAuthenticator::new("cassandra", "cassandra")
    ).build();
    let cluster_config = ClusterTcpConfig(vec![node]);
    let session = new_session(&cluster_config, RoundRobin::new()).expect("session should be created");
    let query = session.prepare("UPDATE bug_284.table1 SET udts = udts + ? WHERE id = ?").unwrap();

    let row = Model {
      id: Uuid::parse_str("68f49fa5-934b-4aff-8a87-f3a32872a6ba").unwrap(),
      col1: "abc".into(),
      col2: 1,
      col3: 2,
    };

    let params = QueryParamsBuilder::new().consistency(Consistency::Quorum).values(query_values!(row, Uuid::parse_str("5bd8877a-e2b2-4d6f-aafd-c3f72a6964cf").unwrap()));
    session.exec_with_params(&query, params.finalize()).unwrap();
}

Hi @krojew,

Looks like the problem is with values you pass for execution.
The query that was prepared is UPDATE bug_284.table1 SET udts = udts + ? WHERE id = ?. When we do some_set = some_set + X, X is an another set of the values of the same type as some_set, not a single item. It allows to "push" few values in the same query.

So, what you need to do in order to get your issues fixed is to change

let params = QueryParamsBuilder::new().consistency(Consistency::Quorum).values(query_values!(row, Uuid::parse_str("5bd8877a-e2b2-4d6f-aafd-c3f72a6964cf").unwrap()));

with

let params = QueryParamsBuilder::new().consistency(Consistency::Quorum).values(query_values!(vec![row], Uuid::parse_str("5bd8877a-e2b2-4d6f-aafd-c3f72a6964cf").unwrap()));

In other words, wrap a value/values that should be added by a vec![]

See, for instance #285. It has an 2e2 test that does such push in similar way.

That might be it. It would be good to add such example to the documentation.

I've created an issue to provide examples for set, list and map (#286)