Mapper, Protocol V1 and TTL

27 views
Skip to first unread message

Ken Hancock

unread,
Jul 5, 2016, 11:23:35 AM7/5/16
to DataStax Java Driver for Apache Cassandra User Mailing List
What's the reason that the Options.ttl is only available on the Mapper.save() and Mapper.saveAsync() methods?

If I want to vary my TTL depending on circumstances, must I jettison the Mapper interface if my client needs to support Cassandra 1.2?


Olivier Michallat

unread,
Jul 5, 2016, 5:19:17 PM7/5/16
to java-dri...@lists.datastax.com
Hi,

What's the reason that the Options.ttl is only available on the Mapper.save() and Mapper.saveAsync() methods?

Which other methods would you want to use it with? TTL does not make sense for get or delete operations.

If I want to vary my TTL depending on circumstances, must I jettison the Mapper interface if my client needs to support Cassandra 1.2?

 Internally, the mapper uses prepared statements. When the TTL option is specified, it will generate a query like "UPDATE ... USING TTL ?". However Cassandra 1.2 doesn't allow you to use a bind marker for TIMESTAMP, TTL or LIMIT, this was added in 2.0 (see CASSANDRA-4450).
We could hardcode the TTL but this would create a different prepared statement for each value.

--

Olivier Michallat

Driver & tools engineer, DataStax


On Tue, Jul 5, 2016 at 8:23 AM, Ken Hancock <hanc...@gmail.com> wrote:
What's the reason that the Options.ttl is only available on the Mapper.save() and Mapper.saveAsync() methods?

If I want to vary my TTL depending on circumstances, must I jettison the Mapper interface if my client needs to support Cassandra 1.2?


--
You received this message because you are subscribed to the Google Groups "DataStax Java Driver for Apache Cassandra User Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-driver-us...@lists.datastax.com.

Ken Hancock

unread,
Jul 5, 2016, 6:41:04 PM7/5/16
to DataStax Java Driver for Apache Cassandra User Mailing List


On Tuesday, July 5, 2016 at 5:19:17 PM UTC-4, Olivier Michallat wrote:
Hi,

What's the reason that the Options.ttl is only available on the Mapper.save() and Mapper.saveAsync() methods?

Which other methods would you want to use it with? TTL does not make sense for get or delete operations.

Sorry...didn't proofread...forgot the "only available with ProtocolV1" phrase.  CASSANDRA-4450 answer my question.
 
If I want to vary my TTL depending on circumstances, must I jettison the Mapper interface if my client needs to support Cassandra 1.2?

 Internally, the mapper uses prepared statements. When the TTL option is specified, it will generate a query like "UPDATE ... USING TTL ?". However Cassandra 1.2 doesn't allow you to use a bind marker for TIMESTAMP, TTL or LIMIT, this was added in 2.0 (see CASSANDRA-4450).
We could hardcode the TTL but this would create a different prepared statement for each value.

Is TTL usable with the Mapper class either through default options or Table annotation?

Ken Hancock

unread,
Jul 6, 2016, 3:22:49 PM7/6/16
to DataStax Java Driver for Apache Cassandra User Mailing List


On Tuesday, July 5, 2016 at 5:19:17 PM UTC-4, Olivier Michallat wrote:
 Internally, the mapper uses prepared statements. When the TTL option is specified, it will generate a query like "UPDATE ... USING TTL ?". However Cassandra 1.2 doesn't allow you to use a bind marker for TIMESTAMP, TTL or LIMIT, this was added in 2.0 (see CASSANDRA-4450).
We could hardcode the TTL but this would create a different prepared statement for each value.

So in order to do this I could subclass Mapper, however it doesn't seem to be easily done because of the private EntityManager (didn't look for other blockers.) We're using driver 2.1 right now as we have Guava dependencies currently preventing upgrading to 3.x.

The other option would to create my own cache of prepared statements in which I have to the essentially clone the functionality of makePreparedQueryString (same EntityManager issue).  For those of us having to support Cassandra 1.2, it seems if we had a version of mapper.saveQuery() that would return the statement without binding, be able to append any items covered by CASSANDRA-4450 and then save into a prepared statement cache, then I still need to implement the bind functionality or have Mapper make available a mapper.saveBind(PreparedStatement).  Not exactly elegant.

Ideas or suggestions?



Olivier Michallat

unread,
Jul 12, 2016, 6:31:47 PM7/12/16
to java-dri...@lists.datastax.com
Sorry but I can't think of a good solution for this. As you've noted the Mapper is not designed for extension in this way, and internally it relies on expressing all parameters as bound variables.

--

Olivier Michallat

Driver & tools engineer, DataStax


Ken Hancock

unread,
Jul 13, 2016, 6:16:47 PM7/13/16
to DataStax Java Driver for Apache Cassandra User Mailing List
So, for the benefit of the zero other people who need to still support Cassandra 1.2.x, here's what I ended up with.  I have a similar wrapper around mapper.saveAsync()

    public void save(T c, Integer ttl) throws Exception
    {
        if (ttl == null || ttl == 0)
        {
            mapper.save(c, saveNullFields(saveNullFields));
        }
        else if (protocolVersion > 1)
        {
             mapper.save(c, ttl(ttl), saveNullFields(saveNullFields));
        }
        else
        {
            BoundStatement bs = getInsertBoundStatementWithTtl(c, ttl);
            session.execute(bs);
        }
    }
    public BoundStatement getSaveBoundStatementWithTtl(T c, int ttl) throws Exception
    {
        Statement statement = mapper.saveQuery(c, saveNullFields(saveNullFields));
        PreparedStatement ps = ttlInserts.get(ttl); // protected map which is part of an abstract class
        BoundStatement bs = null;
        if (statement instanceof BoundStatement)
        {
            bs = (BoundStatement) statement;
        }
        else
        {
            throw new IllegalArgumentException("Could not get bound statement for saveQuery");
        }
        if (ps == null)
        {
            synchronized(ttlInserts)
            {
                ps = ttlInserts.get(ttl);
                if (ps == null)
                {
                    String insert = bs.preparedStatement().getQueryString();
                    
                    // append the USING TTL onto the query
                    insert = insert.substring(0, insert.length() - 1) + " USING TTL " + ttl + ";";
                    ps = session.prepare(insert);
                    ttlInserts.put(ttl, ps);
                }
            }
        }
        
        // Grab the existing member values out of the old prepared statement
        // and insert them into our new (with TTL) prepared statement.
        // Would have been nice to have a getAll(), but...
        // NOTE: mapper cql field order is non-deterministic
        List<Object> binds = new ArrayList<Object>();
        try
        {
            int i = 0;
            Object o;
            while (true)
            {
                o = bs.getObject(i++);
                binds.add(o);
            }
        }
        catch (ArrayIndexOutOfBoundsException e) 
        {
            ; // nothing to do
        }

        return bs = ps.bind(binds.toArray(new Object[binds.size()]));    
    }
Reply all
Reply to author
Forward
0 new messages