eurydice 1.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|