eurydice 1.0.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +34 -0
- data/README.mdown +25 -0
- data/eurydice.gemspec +24 -0
- data/examples/01_connect.rb +18 -0
- data/examples/02_create_keyspace.rb +19 -0
- data/examples/03_create_column_family.rb +28 -0
- data/examples/04_storing_data.rb +37 -0
- data/examples/05_loading_data.rb +86 -0
- data/examples/06_cluster_info.rb +20 -0
- data/examples/common.rb +5 -0
- data/lib/cassandra.rb +158 -0
- data/lib/eurydice/pelops/cluster.rb +42 -0
- data/lib/eurydice/pelops/column_family.rb +217 -0
- data/lib/eurydice/pelops/keyspace.rb +75 -0
- data/lib/eurydice/pelops.rb +124 -0
- data/lib/eurydice/version.rb +6 -0
- data/lib/eurydice.rb +12 -0
- data/spec/eurydice/pelops/cluster_spec.rb +39 -0
- data/spec/eurydice/pelops/column_family_spec.rb +499 -0
- data/spec/eurydice/pelops/keyspace_spec.rb +91 -0
- data/spec/spec_helper.rb +4 -0
- metadata +99 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Eurydice
|
4
|
+
module Pelops
|
5
|
+
class ColumnFamily
|
6
|
+
include ExceptionHelpers
|
7
|
+
include ByteHelpers
|
8
|
+
|
9
|
+
attr_reader :name, :keyspace
|
10
|
+
|
11
|
+
def initialize(keyspace, name)
|
12
|
+
@keyspace, @name = keyspace, name
|
13
|
+
end
|
14
|
+
|
15
|
+
def definition(reload=true)
|
16
|
+
@definition = nil if reload
|
17
|
+
@definition ||= @keyspace.definition(true)[:column_families][@name]
|
18
|
+
end
|
19
|
+
|
20
|
+
def exists?
|
21
|
+
!!definition(true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create!(options={})
|
25
|
+
thrift_exception_handler do
|
26
|
+
@keyspace.column_family_manger.add_column_family(Cassandra::CfDef.from_h(options.merge(:keyspace => @keyspace.name, :name => @name)))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop!
|
31
|
+
thrift_exception_handler do
|
32
|
+
@keyspace.column_family_manger.drop_column_family(@name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def truncate!
|
37
|
+
thrift_exception_handler do
|
38
|
+
@keyspace.column_family_manger.truncate_column_family(@name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(row_key, options={})
|
43
|
+
thrift_exception_handler do
|
44
|
+
deletor = @keyspace.create_row_deletor
|
45
|
+
deletor.delete_row(@name, row_key, get_cl(options))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_column(row_key, column_key, options={})
|
50
|
+
thrift_exception_handler do
|
51
|
+
mutator = @keyspace.create_mutator
|
52
|
+
mutator.delete_column(@name, row_key, to_pelops_bytes(column_key))
|
53
|
+
mutator.execute(get_cl(options))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_columns(row_key, column_keys, options={})
|
58
|
+
thrift_exception_handler do
|
59
|
+
mutator = @keyspace.create_mutator
|
60
|
+
mutator.delete_columns(@name, row_key, column_keys.map { |k| to_pelops_bytes(k) })
|
61
|
+
mutator.execute(get_cl(options))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def update(row_key, properties, options={})
|
66
|
+
thrift_exception_handler do
|
67
|
+
types = options[:validations] || {}
|
68
|
+
key_type = options[:comparator]
|
69
|
+
mutator = @keyspace.create_mutator
|
70
|
+
columns = properties.map do |k, v|
|
71
|
+
key = to_pelops_bytes(k, key_type)
|
72
|
+
value = to_pelops_bytes(v, types[k])
|
73
|
+
ttl = options.fetch(:ttl, mutator.class::NO_TTL)
|
74
|
+
mutator.new_column(key, value, ttl)
|
75
|
+
end
|
76
|
+
mutator.write_columns(@name, row_key, columns)
|
77
|
+
mutator.execute(get_cl(options))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
alias_method :insert, :update
|
81
|
+
|
82
|
+
def key?(row_key, options={})
|
83
|
+
thrift_exception_handler do
|
84
|
+
selector = @keyspace.create_selector
|
85
|
+
predicate = Cassandra::SlicePredicate.new
|
86
|
+
count = selector.get_column_count(@name, row_key, get_cl(options))
|
87
|
+
count > 0
|
88
|
+
end
|
89
|
+
end
|
90
|
+
alias_method :row_exists?, :key?
|
91
|
+
|
92
|
+
def get(row_or_rows, options={})
|
93
|
+
case row_or_rows
|
94
|
+
when Array then get_multi(row_or_rows, options)
|
95
|
+
else get_single(row_or_rows, options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
alias_method :get_row, :get
|
99
|
+
alias_method :get_rows, :get
|
100
|
+
|
101
|
+
def get_column(row_key, column_key, options={})
|
102
|
+
thrift_exception_handler do
|
103
|
+
selector = @keyspace.create_selector
|
104
|
+
column = selector.get_column_from_row(@name, row_key, column_key, get_cl(options))
|
105
|
+
byte_array_to_s(column.get_value)
|
106
|
+
end
|
107
|
+
rescue NotFoundError => e
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def each_column(row_key, options={})
|
112
|
+
thrift_exception_handler do
|
113
|
+
reversed = options.fetch(:reversed, false)
|
114
|
+
batch_size = options.fetch(:batch_size, 100)
|
115
|
+
start_beyond = options.fetch(:start_beyond, nil)
|
116
|
+
start_beyond = to_pelops_bytes(start_beyond) if start_beyond
|
117
|
+
selector = @keyspace.create_selector
|
118
|
+
iterator = selector.iterate_columns_from_row(@name, to_pelops_bytes(row_key), start_beyond, reversed, batch_size, get_cl(options))
|
119
|
+
if block_given?
|
120
|
+
iterator.each do |column|
|
121
|
+
yield column_to_kv(column, options)
|
122
|
+
end
|
123
|
+
else
|
124
|
+
Enumerator.new do |y|
|
125
|
+
iterator.each do |column|
|
126
|
+
y << column_to_kv(column, options)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def get_indexed(column_key, operator, value, options={})
|
134
|
+
thrift_exception_handler do
|
135
|
+
selector = @keyspace.create_selector
|
136
|
+
op = Cassandra::INDEX_OPERATORS[operator]
|
137
|
+
max_rows = options.fetch(:max_row_count, 20)
|
138
|
+
types = options[:validations] || {}
|
139
|
+
key_type = options[:comparator]
|
140
|
+
raise ArgumentError, %(Unsupported index operator: "#{operator}") unless op
|
141
|
+
index_expression = selector.class.new_index_expression(to_pelops_bytes(column_key, key_type), op, to_pelops_bytes(value, types[column_key]))
|
142
|
+
index_clause = selector.class.new_index_clause(empty_pelops_bytes, max_rows, index_expression)
|
143
|
+
column_predicate = create_column_predicate(options)
|
144
|
+
rows = selector.get_indexed_columns(@name, index_clause, column_predicate, get_cl(options))
|
145
|
+
rows_to_h(rows, options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def get_single(row_key, options={})
|
152
|
+
thrift_exception_handler do
|
153
|
+
selector = @keyspace.create_selector
|
154
|
+
column_predicate = create_column_predicate(options)
|
155
|
+
columns = selector.get_columns_from_row(@name, to_pelops_bytes(row_key), column_predicate, get_cl(options))
|
156
|
+
columns_to_h(columns, options)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_multi(row_keys, options={})
|
161
|
+
thrift_exception_handler do
|
162
|
+
selector = @keyspace.create_selector
|
163
|
+
column_predicate = create_column_predicate(options)
|
164
|
+
byte_row_keys = row_keys.map { |rk| to_pelops_bytes(rk) }
|
165
|
+
rows = selector.get_columns_from_rows(@name, byte_row_keys, column_predicate, get_cl(options))
|
166
|
+
rows_to_h(rows, options)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def create_column_predicate(options)
|
171
|
+
max_column_count = options.fetch(:max_column_count, java.lang.Integer::MAX_VALUE)
|
172
|
+
reversed = options.fetch(:reversed, false)
|
173
|
+
case options[:columns]
|
174
|
+
when Range
|
175
|
+
::Pelops::Selector.new_columns_predicate(to_pelops_bytes(options[:columns].begin), to_pelops_bytes(options[:columns].end), reversed, max_column_count)
|
176
|
+
when Array
|
177
|
+
::Pelops::Selector.new_columns_predicate(*options[:columns].map { |col| to_pelops_bytes(col) })
|
178
|
+
else
|
179
|
+
::Pelops::Selector.new_columns_predicate_all(reversed, max_column_count)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def rows_to_h(rows, options)
|
184
|
+
rows.reduce({}) do |acc, (row_key, columns)|
|
185
|
+
columns_h = columns_to_h(columns, options)
|
186
|
+
acc[pelops_bytes_to_s(row_key)] = columns_h if columns_h && !columns_h.empty?
|
187
|
+
acc
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def columns_to_h(columns, options)
|
192
|
+
if columns.empty?
|
193
|
+
nil
|
194
|
+
else
|
195
|
+
columns.reduce({}) do |acc, column|
|
196
|
+
key, value = column_to_kv(column, options)
|
197
|
+
acc[key] = value
|
198
|
+
acc
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def column_to_kv(column, options)
|
204
|
+
types = options[:validations] || {}
|
205
|
+
key_type = options[:comparator]
|
206
|
+
key = byte_array_to_s(column.get_name, key_type)
|
207
|
+
value = byte_array_to_s(column.get_value, types[key])
|
208
|
+
return key, value
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_cl(options)
|
212
|
+
cl = options.fetch(:consistency_level, options.fetch(:cl, :one))
|
213
|
+
Cassandra::CONSISTENCY_LEVELS[cl]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Eurydice
|
4
|
+
module Pelops
|
5
|
+
class Keyspace
|
6
|
+
include ExceptionHelpers
|
7
|
+
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize(name, cluster, pool_name, driver)
|
11
|
+
@name = name
|
12
|
+
@cluster = cluster
|
13
|
+
@pool_name = pool_name
|
14
|
+
@driver = driver
|
15
|
+
end
|
16
|
+
|
17
|
+
def definition(reload=false)
|
18
|
+
thrift_exception_handler do
|
19
|
+
@definition = nil if reload
|
20
|
+
@definition ||= keyspace_manager.get_keyspace_schema(@name).to_h
|
21
|
+
@definition
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def exists?
|
26
|
+
keyspace_manager.keyspace_names.map { |ks_def| ks_def.name }.include?(@name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create!(options={})
|
30
|
+
thrift_exception_handler do
|
31
|
+
ks_def = Cassandra::KsDef.from_h({:strategy_class => 'org.apache.cassandra.locator.LocalStrategy'}.merge(options).merge(:name => @name))
|
32
|
+
keyspace_manager.add_keyspace(ks_def)
|
33
|
+
@driver.add_pool(@pool_name, @cluster, @name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def drop!
|
38
|
+
keyspace_manager.drop_keyspace(@name)
|
39
|
+
rescue Exception => e
|
40
|
+
transform_thrift_exception(e)
|
41
|
+
end
|
42
|
+
|
43
|
+
def column_families(reload=false)
|
44
|
+
definition(reload)[:column_families].keys
|
45
|
+
end
|
46
|
+
|
47
|
+
def column_family(name, options={})
|
48
|
+
create = options.fetch(:create, true)
|
49
|
+
cf = ColumnFamily.new(self, name)
|
50
|
+
cf.create! if create && !cf.exists?
|
51
|
+
cf
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_mutator
|
55
|
+
@driver.create_mutator(@pool_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_selector
|
59
|
+
@driver.create_selector(@pool_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_row_deletor
|
63
|
+
@driver.create_row_deletor(@pool_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def keyspace_manager
|
67
|
+
@keyspace_manager ||= @driver.create_keyspace_manager(@cluster)
|
68
|
+
end
|
69
|
+
|
70
|
+
def column_family_manger
|
71
|
+
@column_family_manger ||= @driver.create_column_family_manager(@cluster, @name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'pelops-jars'
|
4
|
+
require 'eurydice'
|
5
|
+
require 'cassandra'
|
6
|
+
|
7
|
+
|
8
|
+
module Pelops
|
9
|
+
import 'org.scale7.cassandra.pelops.Cluster'
|
10
|
+
import 'org.scale7.cassandra.pelops.Pelops'
|
11
|
+
import 'org.scale7.cassandra.pelops.Selector'
|
12
|
+
import 'org.scale7.cassandra.pelops.Bytes'
|
13
|
+
import 'org.scale7.cassandra.pelops.exceptions.InvalidRequestException'
|
14
|
+
import 'org.scale7.cassandra.pelops.exceptions.NotFoundException'
|
15
|
+
import 'org.scale7.cassandra.pelops.exceptions.ApplicationException'
|
16
|
+
end
|
17
|
+
|
18
|
+
module Eurydice
|
19
|
+
def self.connect
|
20
|
+
Pelops.connect
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.disconnect!
|
24
|
+
Pelops.disconnect!
|
25
|
+
end
|
26
|
+
|
27
|
+
module Pelops
|
28
|
+
def self.connect(options={})
|
29
|
+
host = options.fetch(:host, 'localhost')
|
30
|
+
port = options.fetch(:port, 9160)
|
31
|
+
pool_name = options.fetch(:pool_name, 'eurydice')
|
32
|
+
Cluster.new(::Pelops::Cluster.new(host, port))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.keyspace(keyspace_name, host='localhost', port=9160, pool_name='eurydice')
|
36
|
+
cluster = ::Pelops::Cluster.new(host, port)
|
37
|
+
::Pelops::Pelops.add_pool(pool_name, cluster, keyspace_name)
|
38
|
+
Keyspace.new(keyspace_name, cluster, pool_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.disconnect!
|
42
|
+
::Pelops::Pelops.shutdown
|
43
|
+
end
|
44
|
+
|
45
|
+
module ByteHelpers
|
46
|
+
extend self
|
47
|
+
|
48
|
+
def empty_pelops_bytes
|
49
|
+
::Pelops::Bytes::EMPTY
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_pelops_bytes(obj, type=nil)
|
53
|
+
case type
|
54
|
+
when :long
|
55
|
+
::Pelops::Bytes.from_long(obj)
|
56
|
+
else
|
57
|
+
::Pelops::Bytes.new(obj.to_s.to_java_bytes)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_nio_bytes(str)
|
62
|
+
to_pelops_bytes(str).bytes
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_byte_array(str)
|
66
|
+
str.to_java_bytes
|
67
|
+
end
|
68
|
+
|
69
|
+
def pelops_bytes_to_s(pb)
|
70
|
+
String.from_java_bytes(pb.to_byte_array)
|
71
|
+
end
|
72
|
+
|
73
|
+
def nio_bytes_to_s(nb)
|
74
|
+
pelops_bytes_to_s(::Pelops::Bytes.new(nb))
|
75
|
+
end
|
76
|
+
|
77
|
+
def byte_array_to_s(ba, type=nil)
|
78
|
+
case type
|
79
|
+
when :long
|
80
|
+
::Pelops::Bytes.new(ba).to_long
|
81
|
+
else
|
82
|
+
String.from_java_bytes(ba)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
module ExceptionHelpers
|
88
|
+
def transform_thrift_exception(e)
|
89
|
+
if e.respond_to?(:cause)
|
90
|
+
case e.cause
|
91
|
+
when Cassandra::InvalidRequestException, ::Pelops::InvalidRequestException
|
92
|
+
message = e.cause.why
|
93
|
+
backtrace = e.backtrace
|
94
|
+
error_class = begin
|
95
|
+
case message
|
96
|
+
when /Keyspace already exists/
|
97
|
+
then KeyspaceExistsError
|
98
|
+
else InvalidRequestError
|
99
|
+
end
|
100
|
+
end
|
101
|
+
raise error_class, message, backtrace
|
102
|
+
when ::Pelops::NotFoundException
|
103
|
+
raise NotFoundError, e.cause.message, e.backtrace
|
104
|
+
when ::Pelops::ApplicationException
|
105
|
+
raise EurydiceError, e.cause.message, e.backtrace
|
106
|
+
when Thrift::TTransportException
|
107
|
+
raise TimeoutError, e.cause.message, e.backtrace
|
108
|
+
end
|
109
|
+
end
|
110
|
+
raise e
|
111
|
+
end
|
112
|
+
|
113
|
+
def thrift_exception_handler
|
114
|
+
yield
|
115
|
+
rescue Exception => e
|
116
|
+
transform_thrift_exception(e)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
require_relative 'pelops/cluster'
|
123
|
+
require_relative 'pelops/keyspace'
|
124
|
+
require_relative 'pelops/column_family'
|
data/lib/eurydice.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'java'
|
4
|
+
require 'eurydice/pelops'
|
5
|
+
|
6
|
+
module Eurydice
|
7
|
+
class EurydiceError < StandardError; end
|
8
|
+
class InvalidRequestError < EurydiceError; end
|
9
|
+
class KeyspaceExistsError < InvalidRequestError; end
|
10
|
+
class NotFoundError < EurydiceError; end
|
11
|
+
class TimeoutError < EurydiceError; end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
module Eurydice
|
5
|
+
module Pelops
|
6
|
+
describe Cluster do
|
7
|
+
it 'can connect' do
|
8
|
+
@cluster = Eurydice.connect
|
9
|
+
@cluster.should be_connected
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#keyspace' do
|
13
|
+
before do
|
14
|
+
@cluster = Eurydice.connect
|
15
|
+
@keyspace_name = "eurydice_test_space_#{rand(1000)}"
|
16
|
+
if @cluster.keyspaces.include?(@keyspace_name)
|
17
|
+
@cluster.keyspace(@keyspace_name).drop!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
@keyspace.drop! rescue nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'creates a keyspace' do
|
26
|
+
@keyspace = @cluster.keyspace(@keyspace_name)
|
27
|
+
@keyspace.exists?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'defers the creation of a keyspace with :create => false' do
|
31
|
+
@keyspace = @cluster.keyspace(@keyspace_name, :create => false)
|
32
|
+
@keyspace.exists?.should be_false
|
33
|
+
@keyspace.create!
|
34
|
+
@keyspace.exists?.should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|