Cassie
Cassie provides ruby application support for Apache Cassandra. It provides components that most applications will need that are out of scope of the official cassandra-driver
, including:
- Database configuration
- Cluster/session management
- Cassandra server command helpers
- Versioned schema migrations
- Query classes and DSL
- Test harnessing
Each of these components are designed to be used independently or together in a ruby application. If you want to manage your own configuration, use Cassie for session management, and some other gem for your queries -- great!
Tested against:
- Ruby: MRI 2.2, 2.3, and JRuby 1.9
-
cassandra-driver
3.0
Installation
# Gemfile
gem 'cassie', '~> 1.1.0'
or
$ gem install cassie
See cassie-rails
for Rails integration.
Database Configuration
Cassie provies database connection configuration (e.g. cluster and session) per environment. Support for a default YAML back-end is provided.
$ cassie configuration:generate
Cassie::configurations
are loaded from this configuration file at runtime.
Cassie.confurations
=> {"development"=>{"hosts"=>["127.0.0.1"], "port"=>9042, "keyspace"=>"my_app_development"}, "test"=>{"hosts"=>["127.0.0.1"], "port"=>9042, "idle_timeout"=>"nil", "keyspace"=>"my_app_test"}, "production"=>{"hosts"=>["cass1.my_app.biz", "cass2.my_app.biz", "cass3.my_app.biz"], "port"=>9042, "keyspace"=>"my_app_production"}}
Cassie.configurationpulls the appropriate configuration from
Cassie.configurations, based on
Cassie.env`.
Cassie.env = "production"
Cassie.configuration
{"hosts"=>["cass1.my_app.biz", "cass2.my_app.biz", "cass3.my_app.biz"], "port"=>9042, "keyspace"=>"my_app_production"}
Cassie.keyspace
=> 'my_app_production'
Cassie.env
prefers ENV["CASSANDRA_ENV"]
, then ENV["RACK_ENV"]
, and falls back to development
.
See the Configuration
README for more on features and usage.
Connection Handling
Cassie provides cluster and session connection creation according to cassie-driver
best practices.
Using cached cluster and session objects
cluster
and session
objects are created, cached, and reused globally.
# continuing from above 'production' configuration
Cassie.cluster
=> #<Cassandra::Cluster:0x3fc032f7f9b8> #<= configured with production cluster `configuration` options
Cassie.session
=> #<Cassandra::Session:0x3fc084caa344> #<= session scoped to default 'my_app_production' keyspace
Cassie.session(nil)
=> #<Cassandra::Session:0x3fc084caba22> #<= session without scoped keyspace
Cassie.session('my_other_keyspace')
=> #<Cassandra::Session:0x3fc084cabf33> #<= session scoped to 'my_other_keyspace' keyspace
Cassie.session
=> #<Cassandra::Session:0x3fc084caa344> #<= cached session, scoped to default 'my_app_production' keyspace
If using Cassie Configuration as described above via cassandra.yml
, cluster configuration happens automatically. If not, assign a cluster environments hash to Cassie::configurations
before using a cluster
or session
.
Using cluster and session objects in Classes
Include Cassie::Connection
in a class for session
and keyspace
functionality in your objects.
class MyQuery
include Cassie::Connection
# An explicit keyspace that will determine the session used
# instead of falling back to the value in `Cassie.keyspace`
# for all instances of this class.
# Override `#keyspace` for per-object evaluation.
keyspace :some_other_keyspace
def find_user(id)
# `session` is a vanilla Cassandra::Session
# connected to `some_other_keyspace`
session.execute('SELECT * FROM users WHERE id = ?;', arguments: [id])
end
end
See the Connection README for more on features and usage.
Cassandra Control
Cassie provides simple commands to control Cassandra execution in *nix development. These simplify execution and reduce output to provide faster management of your Cassandra processes.
Start
$ cassie start
Starting Cassandra...
[✓] Cassandra Running
Stop
$ cassie stop
Stopping Cassandra...
[✓] Cassandra Stopped
$ cassie stop
Couldn't single out a Cassandra process.
- Is cqlsh running?
- Kill all cassandra processes with --all
- 9542 | /usr/local/apache-cassandra-3.0.8/bin/cqlsh.py
- 2832 | org.apache.cassandra.service.CassandraDaemon
$ cassie stop --all
Stopping Cassandra...
[✓] Cassandra Stopped
Restart
$ cassie restart
Stopping Cassandra...
[✓] Cassandra Stopped
Starting Cassandra...
[✓] Cassandra Running
Tail
$ cassie tail
Tailing Cassandra system log, Ctrl-C to stop...
/usr/local/cassandra/logs/system.log:
INFO [main] 2016-09-23 11:18:05,073 StorageService.java:1902 - Node localhost/127.0.0.1 state jump to NORMAL
INFO [main] 2016-09-23 11:18:05,215 NativeTransportService.java:75 - Netty using Java NIO event loop
INFO [main] 2016-09-23 11:18:05,343 Server.java:159 - Using Netty Version: [netty-buffer=netty-buffer-4.0.23.Final.208198c, netty-codec=netty-codec-4.0.23.Final.208198c, netty-codec-http=netty-codec-http-4.0.23.Final.208198c, netty-codec-socks=netty-codec-socks-4.0.23.Final.208198c, netty-common=netty-common-4.0.23.Final.208198c, netty-handler=netty-handler-4.0.23.Final.208198c, netty-transport=netty-transport-4.0.23.Final.208198c, netty-transport-rxtx=netty-transport-rxtx-4.0.23.Final.208198c, netty-transport-sctp=netty-transport-sctp-4.0.23.Final.208198c, netty-transport-udt=netty-transport-udt-4.0.23.Final.208198c]
INFO [main] 2016-09-23 11:18:05,344 Server.java:160 - Starting listening for CQL clients on localhost/127.0.0.1:9042 (unencrypted)...
INFO [main] 2016-09-23 11:18:05,407 CassandraDaemon.java:477 - Not starting RPC server as requested. Use JMX (StorageService->startRPCServer()) or nodetool (enablethrift) to start it
Versioned Schema Migrations
Cassie supports migration between schema states using semantically versioned, incremental migration files.
Schema Version information is stored in Cassandra persistence, in the cassie_schema.versions
table (configurable).
A schema file holds the current state of the schema in-repo, at db/cassandra/schema.rb
, (configurable).
Various cassie <task>
tasks are used to manage the schema version and migrations.
Tasks
Task | Description |
---|---|
migrations:import | Import existing cassandra_migrations migration files and convert to semantic versioning |
migration:create | Generates an empty migration file prefixed with the next semantic version number |
migrate | Migrates the schema by running the up methods in any migrations starting after the current schema version |
migrate:reset | runs schema:reset and migrate |
schema:init | Create versioned migrations schema, and the environment's keyspace if it doesn't exist |
schema:version | Print the current schema version information for the Cassandra cluster |
schema:history | Print the the historical version information the current Cassandra cluster state |
schema:status | Print the the migration status for each local migration (up/down) |
schema:load | Creates the schema by executing the CQL schema in the schema file (db/cassandra/schema.rb by default) |
schema:drop | drop keyspace(s) |
schema:dump | Dumps the schema for all non-system keyspaces in CQL format (db/cassandra/schema.rb by default) |
schema:reset | runs schema:drop and schema:load |
schema:import | Create an initial migration based on the current Cassandra non-system schema |
See the Migrations README for more on features and usage.
Query DSL
Cassie provides base Query Classes to manage interactions to the database. Create application specific subclasses and construct queries with a simple CQL DSL.
class UserByUsernameQuery < Cassie::Query
select_from :users_by_username
where :username, :eq
consistency :quorum
end
UserByUsernameQuery.new.fetch_first(username: "eprothro")
=> #<Struct user_id=123, username="eprothro">
See the Queries README for more on features and usage.
Test Harnessing
Avoid making queries into the persistnace layer when you can afford it.
some_query = SomeQuery.new
some_query.extend(Cassie::Testing::Fake::Query)
some_query.session.rows = [{'user_id' => 123, 'username' => 'eprothro'}]
some_query.fetch
=> [#<Struct user_id=123, username="eprothro">]
some_query.session.last_statement
=> #<Cassandra::Statements::Simple:0x3ffde09930b8 @cql="SELECT * FROM users LIMIT 500;" @params=[]>
See the Testing README for more on features and usage.
Contributing
Pull requests and issues are welcome. Please read the contributing guidelines.