What's New in Cqlengine 0.7

Recently we released version 0.7 of cqlengine, the Python object mapper for CQL3. We’ve been steadily moving towards full support of all of CQL3 for both queries and for table configuration. This post will outline the new features and provide examples on how to use them.

Counters

With counter support finally included it’s now possible to create and use tables with counter columns. They are exposed to the Python application as simple integers, and changes to their values will be sent as deltas to Cassandra. Let’s take a look at an example. I’ll assume you already have Cassandra running locally.

from cqlengine import *
from cqlengine.management import sync_table
from cqlengine.connection import setup

setup(['localhost'])

class CounterExample(Model):
    __keyspace__ = 'test'
    k = Integer(primary_key=True)
    v1 = Counter()
    v2 = Counter()

tmp = CounterExample.create(k=1)
tmp.v1 += 5
tmp.v2 += 6
tmp.save()

It’s important to understand the value now contained in tmp is only viewed from the perspective of the current process. That is, the above Python code will translate to the following CQL:

update counter_example set v1 = v1 + 5, v2 = v2 + 6 WHERE k = 1

If you want to do a blind write to the database, you can also use this syntax:

blah = CounterExample(k=1)
blah.v1 += 10
blah.save()

This will give you blind writes without needing to read the row beforehand.

Compaction Strategy

Also new is the ability to set your compaction strategy. We’ve added all the compaction options present in Cassandra 1.2. Compaction may be specified with compaction. Here’s an example:

class Leveled(cqlengine.Model):
    __compaction__ = cqlengine.LeveledCompactionStrategy
    __compaction_tombstone_threshold__ = .5
    pk = cqlengine.Integer(primary_key=True)
    some_val = cqlengine.Integer()
sync_table(Leveled)

sync_table() will create or alter your table to match the compaction specifications. You’ll want to nodetool upgradesstables on any tables where you change the compaction strategy. All compaction options are supported, see the docs for details.

Laying the Groundwork for Table Polymorphism

We’ve started reworking the cqlengine internals a bit to solve the problem of model polymorphism within a single table. Since version 0.6 of cqlengine, we’ve sync’ed any new columns to cassandra via ALTER statements. So, if you were to do this:

class Animal(Model):
        __table_name__ = 'animal'
    animal_id = UUID(primary_key=True)
    name = Text()

sync_table(Animal)

class Dog(Animal):
    fierceness = Integer()

sync_table(Dog)

class Cat(Animal):
    cuteness = Integer()

sync_table(Cat)

You’ll actually end up with a single table, animal, with all the columns you provided.

cqlsh:cqlengine> desc table animal;

CREATE TABLE animal (
  animal_id uuid PRIMARY KEY,
  cuteness int,
  fierceness int,
  name text
) WITH
  bloom_filter_fp_chance=0.010000 AND
  caching='KEYS_ONLY' AND
  comment='' AND
  dclocal_read_repair_chance=0.000000 AND
  gc_grace_seconds=864000 AND
  read_repair_chance=0.100000 AND
  replicate_on_write='true' AND
  populate_io_cache_on_flush='false' AND
  compaction={'class': 'SizeTieredCompactionStrategy'} AND
  compression={'sstable_compression': 'SnappyCompressor'};

We’re working to make it possible to query the Animal class and automatically get back the subclass that was originally used to create the object.

Hopefully this has been educational. Any questions, comments? Hit me up @rustyrazorblade

If you found this post helpful, please consider sharing to your network. I'm also available to help you be successful with your distributed systems! Please reach out if you're interested in working with me, and I'll be happy to schedule a free one-hour consultation.