Cassandra Object

Build Status Code Climate

Cassandra Object uses ActiveModel to mimic much of the behavior in ActiveRecord. Use cql3 provided by ruby-driver gem and uses the old thrift structure with the possible option at this link:


CREATE TABLE keyspace.table (
    key text,
    column1 text,
    value blob,
    PRIMARY KEY (key, column1)
) WITH bloom_filter_fp_chance = 0.01
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'}
    AND comment = ''
    AND compaction = {'class': 'SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND crc_check_chance = 1.0
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

You can also use the a custom schema structure with the possible options at this link:


CREATE TABLE keyspace.table (
    key text,
    field1 text,
    field2 varchar,
    field3 float,
    PRIMARY KEY (key)
) WITH bloom_filter_fp_chance = 0.01
    AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'}
    AND comment = ''
    AND compaction = {'class': 'SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND crc_check_chance = 1.0
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

Installation

Add the following to your Gemfile:

gem 'extendi-cassandra_object'

Change the version of Cassandra accordingly. Recent versions have not been backward compatible.

Defining Models

Schemaless model:

class Widget < CassandraObject::BaseSchemaless
  string :name
  string :description
  integer :price
  array :colors, unique: true

  validates :name, presence: :true

  before_create do
    self.description = "#{name} is the best product ever"
  end
end

Schemaless with dynamic attributes model:

class Widget < CassandraObject::BaseSchemalessDynamic
  string :name
  string :description
  integer :price
  array :colors, unique: true

  validates :name, presence: :true

  before_create do
    self.description = "#{name} is the best product ever"
  end
end

Schema model:

class Widget < CassandraObject::BaseSchema
  string :name
  string :description
  integer :price
  array :colors, unique: true

  validates :name, presence: :true

  before_create do
    self.description = "#{name} is the best product ever"
  end
end

Custom config

You can define a custom configuration for the cassandra connection, allowing you to have multiple cassandra endpoints fromt he same application

class Widget < CassandraObject::BaseSchema
  string :name

  def self.custom_config
    #return custom cassandra configuration
    { }
  end
end

Using with Cassandra

Add a config/cassandra.yml:

development:
  keyspace: my_app_development
  hosts: ["127.0.0.1"]
  compression: :lz4,
  connect_timeout: 0.1,
  request_timeout: 0.1,
  consistency: :any/:one/:two/:three/:quorum/:all/:local_quorum/:each_quorum/:serial/:local_serial/:local_one,
  write_consistency: :any/:one/:two/:three/:quorum/:all/:local_quorum/:each_quorum/:serial/:local_serial/:local_one,
  protocol_version: 3,
  page_size: 10000,
  trace: true/false

Creating and updating records

Cassandra Object has equivalent methods as ActiveRecord:

widget = Widget.new
widget.valid?
widget = Widget.create(name: 'Acme', price: 100)
widget.update_attribute(:price, 1200)
widget.update_attributes(price: 1200, name: 'Acme Corporation')
widget.attributes = {price: 300}
widget.price_was
widget.save
widget.save!

Finding records

  widget = Widget.find(uuid)
  widget = Widget.first
  widgets = Widget.all
  Widget.find_each do |widget|
  # Codez
end

Scoping

Some lightweight scoping features are available:

  Widget.where(color: :red)
  Widget.select([:name, :color])
  Widget.limit(10)

Plain response scoping

cql_response return an hash where the key is the model key and values is an hash where key is the column name and the value is the column value.

  Widget.cql_response.where(color: :red)
  Widget.cql_response([:name, :color])
  Widget.cql_response.limit(10)

Running tests on MacOS

  • Run a cassandra node on localhost (i.e. ccm start if CCM is used)
  • Increase the limit of open files with ulimit -Sn 2048
  • Run the tests by running the default rake task or bundle exec rake test