Ilios
Ilios that Cassandra driver written by C language for Ruby using DataStax C/C++ Driver.
Installation
Install the gem and add to the application's Gemfile by executing:
$ bundle add ilios
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install ilios
This gem's installer will install the DataStax C/C++ Driver to the appropriate location automatically. However, if you prefer to install the DataStax C/C++ Driver manually, you can do so by executing:
$ bundle config set --local build.ilios --with-libuv-dir=/path/to/libuv-installed-dir
$ bundle config set --local build.ilios --with-cassandra-driver-dir=/path/to/cassandra-cpp-driver-installed-dir
$ bundle add ilios
or
$ gem install ilios -- --with-libuv-dir=/path/to/libuv-installed-dir --with-cassandra-driver-dir=/path/to/cassandra-cpp-driver-installed-dir
Requirements
- cmake (in order to build the DataStax C/C++ Driver and libuv)
- C/C++ compiler
- install_name_tool (macOS only)
Supported
- Ruby 3.4 or later
- Cassandra 3.0 or later
- Linux and macOS platform
Example
Basic usage
Create the keyspace in advance using the cqlsh command.
CREATE KEYSPACE IF NOT EXISTS ilios
WITH REPLICATION = {
'class' : 'SimpleStrategy',
'replication_factor' : 1
};
Then, you can run the following code.
require 'ilios'
cluster = Ilios::Cassandra::Cluster.new
cluster.keyspace('ilios')
cluster.hosts(['127.0.0.1'])
session = cluster.connect
# Create the table
statement = session.prepare(<<~CQL)
CREATE TABLE IF NOT EXISTS ilios.example (
id bigint,
message text,
created_at timestamp,
PRIMARY KEY (id)
) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }
AND gc_grace_seconds = 691200;
CQL
session.execute(statement)
# Insert the records
statement = session.prepare(<<~CQL)
INSERT INTO ilios.example (
id,
message,
created_at
) VALUES (?, ?, ?)
CQL
100.times do |i|
statement.bind({
id: i,
message: 'Hello World',
created_at: Time.now,
})
session.execute(statement)
end
# Select the records
statement = session.prepare(<<~CQL)
SELECT * FROM ilios.example
CQL
statement.idempotent = true
statement.page_size = 25
result = session.execute(statement)
result.each do |row|
p row
end
while(result.next_page)
result.each do |row|
p row
end
end
Collection types
list, set and map columns (including nested collections such as
list<frozen<list<int>>>) are supported.
statement = session.prepare(<<~CQL)
CREATE TABLE IF NOT EXISTS ilios.collection_example (
id bigint,
tags set<text>,
scores list<int>,
attributes map<text, bigint>,
PRIMARY KEY (id)
);
CQL
session.execute(statement)
statement = session.prepare(<<~CQL)
INSERT INTO ilios.collection_example (
id,
tags,
scores,
attributes
) VALUES (?, ?, ?, ?)
CQL
statement.bind({
id: 1,
tags: Set['ruby', 'cassandra'], # or an Array
scores: [85, 92],
attributes: { 'height' => 180 },
})
session.execute(statement)
statement = session.prepare(<<~CQL)
SELECT * FROM ilios.collection_example
CQL
session.execute(statement).each do |row|
row['tags'] # => Set["cassandra", "ruby"]
row['scores'] # => [85, 92]
row['attributes'] # => {"height" => 180}
end
Notes:
- A
setcolumn accepts bothSetandArrayon bind, and is always returned as aSet. - A
listcolumn also accepts bothArrayandSeton bind, but is always returned as anArray. Binding aSetto alistcolumn keeps the orderSet#to_areturns. - Cassandra stores an empty non-frozen collection as
null, so inserting[],Set.newor{}returnsnilon select. This is Cassandra's data model, not an Ilios limitation. nilis not allowed as a collection element (Cassandra collections cannot containnull).Symbolis accepted fortext(as well asasciiandvarchar) columns and collection elements, and is stored (and returned) as aString.- Because a
Symbolmap key is stored as itsStringequivalent, binding a map that contains both (for example{ k1: 1, 'k1' => 2 }) ends up as a single key on the server; the entry bound last wins. - Binding a
Stringcontaining a NUL character (\0) to atext(orascii/varchar) column raisesArgumentError(known limitation).
Synchronous API
Ilios::Cassandra::Session#prepare and Ilios::Cassandra::Session#execute are provided as synchronous API.
statement = session.prepare(<<~CQL)
SELECT * FROM ilios.example
CQL
result = session.execute(statement)
Asynchronous API
Ilios::Cassandra::Session#prepare_async and Ilios::Cassandra::Session#execute_async are provided as asynchronous API.
prepare_future = session.prepare_async(<<~CQL)
INSERT INTO ilios.example (
id,
message,
created_at
) VALUES (?, ?, ?)
CQL
prepare_future.on_success { |statement|
futures = []
10.times do |i|
statement.bind({
id: i,
message: 'Hello World',
created_at: Time.now,
})
result_future = session.execute_async(statement)
result_future.on_success { |result|
p result
p "success"
}
result_future.on_failure {
p "fail"
}
futures << result_future
end
futures.each(&:await)
}
prepare_future.await
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/Watson1978/ilios.