cassandra 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +10 -0
- data/LICENSE +0 -0
- data/Manifest +12 -13
- data/README.md +344 -0
- data/Rakefile +52 -8
- data/cassandra.gemspec +5 -5
- data/conf/0.6/cassandra.in.sh +0 -0
- data/conf/0.6/log4j.properties +0 -0
- data/conf/0.6/schema.json +9 -0
- data/conf/0.6/storage-conf.xml +10 -0
- data/conf/0.7/cassandra.in.sh +0 -0
- data/conf/0.7/cassandra.yaml +0 -0
- data/conf/0.7/log4j-server.properties +0 -0
- data/conf/0.7/schema.json +9 -0
- data/conf/0.7/schema.txt +5 -16
- data/conf/0.8/cassandra.in.sh +0 -0
- data/conf/0.8/cassandra.yaml +1 -1
- data/conf/0.8/log4j-server.properties +0 -0
- data/conf/0.8/schema.json +19 -1
- data/conf/0.8/schema.txt +12 -17
- data/lib/cassandra.rb +3 -2
- data/lib/cassandra/0.6.rb +0 -0
- data/lib/cassandra/0.6/cassandra.rb +57 -6
- data/lib/cassandra/0.6/columns.rb +19 -0
- data/lib/cassandra/0.6/protocol.rb +2 -1
- data/lib/cassandra/0.7.rb +0 -0
- data/lib/cassandra/0.7/cassandra.rb +0 -270
- data/lib/cassandra/0.7/columns.rb +1 -81
- data/lib/cassandra/0.7/protocol.rb +0 -112
- data/lib/cassandra/0.8.rb +0 -0
- data/lib/cassandra/0.8/cassandra.rb +5 -267
- data/lib/cassandra/0.8/columns.rb +1 -81
- data/lib/cassandra/0.8/protocol.rb +9 -103
- data/lib/cassandra/array.rb +0 -0
- data/lib/cassandra/cassandra.rb +715 -92
- data/lib/cassandra/{0.7/column_family.rb → column_family.rb} +0 -0
- data/lib/cassandra/columns.rb +63 -6
- data/lib/cassandra/comparable.rb +0 -0
- data/lib/cassandra/constants.rb +0 -0
- data/lib/cassandra/debug.rb +0 -0
- data/lib/cassandra/helpers.rb +0 -0
- data/lib/cassandra/{0.7/keyspace.rb → keyspace.rb} +0 -0
- data/lib/cassandra/long.rb +0 -0
- data/lib/cassandra/mock.rb +45 -8
- data/lib/cassandra/ordered_hash.rb +0 -0
- data/lib/cassandra/protocol.rb +119 -0
- data/lib/cassandra/time.rb +0 -0
- data/test/cassandra_client_test.rb +0 -0
- data/test/cassandra_mock_test.rb +3 -0
- data/test/cassandra_test.rb +202 -20
- data/test/comparable_types_test.rb +0 -0
- data/test/eventmachine_test.rb +0 -0
- data/test/ordered_hash_test.rb +0 -0
- data/test/test_helper.rb +1 -1
- data/vendor/0.6/gen-rb/cassandra.rb +0 -0
- data/vendor/0.6/gen-rb/cassandra_constants.rb +0 -0
- data/vendor/0.6/gen-rb/cassandra_types.rb +0 -0
- data/vendor/0.7/gen-rb/cassandra.rb +0 -0
- data/vendor/0.7/gen-rb/cassandra_constants.rb +0 -0
- data/vendor/0.7/gen-rb/cassandra_types.rb +0 -0
- data/vendor/0.8/gen-rb/cassandra.rb +0 -0
- data/vendor/0.8/gen-rb/cassandra_constants.rb +0 -0
- data/vendor/0.8/gen-rb/cassandra_types.rb +0 -0
- metadata +27 -29
- data/README.rdoc +0 -99
- data/lib/cassandra/0.8/column_family.rb +0 -3
- data/lib/cassandra/0.8/keyspace.rb +0 -3
File without changes
|
data/lib/cassandra/columns.rb
CHANGED
@@ -5,15 +5,15 @@ class Cassandra
|
|
5
5
|
private
|
6
6
|
|
7
7
|
def is_super(column_family)
|
8
|
-
@is_super[column_family] ||= column_family_property(column_family, '
|
8
|
+
@is_super[column_family] ||= column_family_property(column_family, 'column_type') == "Super"
|
9
9
|
end
|
10
10
|
|
11
11
|
def column_name_class(column_family)
|
12
|
-
@column_name_class[column_family] ||= column_name_class_for_key(column_family, "
|
12
|
+
@column_name_class[column_family] ||= column_name_class_for_key(column_family, "comparator_type")
|
13
13
|
end
|
14
14
|
|
15
15
|
def sub_column_name_class(column_family)
|
16
|
-
@sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "
|
16
|
+
@sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "subcomparator_type")
|
17
17
|
end
|
18
18
|
|
19
19
|
def column_name_class_for_key(column_family, comparator_key)
|
@@ -28,10 +28,11 @@ class Cassandra
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def column_family_property(column_family, key)
|
31
|
-
|
31
|
+
cfdef = schema.cf_defs.find {|cfdef| cfdef.name == column_family }
|
32
|
+
unless cfdef
|
32
33
|
raise AccessError, "Invalid column family \"#{column_family}\""
|
33
34
|
end
|
34
|
-
|
35
|
+
cfdef.send(key)
|
35
36
|
end
|
36
37
|
|
37
38
|
def multi_key_slices_to_hash(column_family, array, return_empty_rows = false)
|
@@ -72,16 +73,72 @@ class Cassandra
|
|
72
73
|
def columns_to_hash_for_classes(columns, column_name_class, sub_column_name_class = nil)
|
73
74
|
hash = OrderedHash.new
|
74
75
|
Array(columns).each do |c|
|
75
|
-
c = c.super_column || c.column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
|
76
|
+
c = c.super_column || c.column || c.counter_column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
|
76
77
|
case c
|
77
78
|
when CassandraThrift::SuperColumn
|
78
79
|
hash.[]=(column_name_class.new(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_class)) # Pop the class stack, and recurse
|
79
80
|
when CassandraThrift::Column
|
80
81
|
hash.[]=(column_name_class.new(c.name), c.value, c.timestamp)
|
82
|
+
when CassandraThrift::CounterColumn
|
83
|
+
hash.[]=(column_name_class.new(c.name), c.value, 0)
|
81
84
|
end
|
82
85
|
end
|
83
86
|
hash
|
84
87
|
end
|
85
88
|
|
89
|
+
def _standard_insert_mutation(column_family, column_name, value, timestamp, ttl = nil)
|
90
|
+
CassandraThrift::Mutation.new(
|
91
|
+
:column_or_supercolumn => CassandraThrift::ColumnOrSuperColumn.new(
|
92
|
+
:column => CassandraThrift::Column.new(
|
93
|
+
:name => column_name_class(column_family).new(column_name).to_s,
|
94
|
+
:value => value,
|
95
|
+
:timestamp => timestamp,
|
96
|
+
:ttl => ttl
|
97
|
+
)
|
98
|
+
)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def _super_insert_mutation(column_family, super_column_name, sub_columns, timestamp, ttl = nil)
|
103
|
+
CassandraThrift::Mutation.new(:column_or_supercolumn =>
|
104
|
+
CassandraThrift::ColumnOrSuperColumn.new(
|
105
|
+
:super_column => CassandraThrift::SuperColumn.new(
|
106
|
+
:name => column_name_class(column_family).new(super_column_name).to_s,
|
107
|
+
:columns => sub_columns.collect { |sub_column_name, sub_column_value|
|
108
|
+
CassandraThrift::Column.new(
|
109
|
+
:name => sub_column_name_class(column_family).new(sub_column_name).to_s,
|
110
|
+
:value => sub_column_value.to_s,
|
111
|
+
:timestamp => timestamp,
|
112
|
+
:ttl => ttl
|
113
|
+
)
|
114
|
+
}
|
115
|
+
)
|
116
|
+
)
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
# General info about a deletion object within a mutation
|
121
|
+
# timestamp - required. If this is the only param, it will cause deletion of the whole key at that TS
|
122
|
+
# supercolumn - opt. If passed, the deletes will only occur within that supercolumn (only subcolumns
|
123
|
+
# will be deleted). Otherwise the normal columns will be deleted.
|
124
|
+
# predicate - opt. Defines how to match the columns to delete. if supercolumn passed, the slice will
|
125
|
+
# be scoped to subcolumns of that supercolumn.
|
126
|
+
|
127
|
+
# Deletes a single column from the containing key/CF (and possibly supercolumn), at a given timestamp.
|
128
|
+
# Although mutations (as opposed to 'remove' calls) support deleting slices and lists of columns in one shot, this is not implemented here.
|
129
|
+
# The main reason being that the batch function takes removes, but removes don't have that capability...so we'd need to change the remove
|
130
|
+
# methods to use delete mutation calls...although that might have performance implications. We'll leave that refactoring for later.
|
131
|
+
def _delete_mutation(cf, column, subcolumn, timestamp, options={})
|
132
|
+
deletion_hash = {:timestamp => timestamp}
|
133
|
+
if is_super(cf)
|
134
|
+
deletion_hash[:super_column] = column if column
|
135
|
+
deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [subcolumn]) if subcolumn
|
136
|
+
else
|
137
|
+
deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [column]) if column
|
138
|
+
end
|
139
|
+
CassandraThrift::Mutation.new(
|
140
|
+
:deletion => CassandraThrift::Deletion.new(deletion_hash)
|
141
|
+
)
|
142
|
+
end
|
86
143
|
end
|
87
144
|
end
|
data/lib/cassandra/comparable.rb
CHANGED
File without changes
|
data/lib/cassandra/constants.rb
CHANGED
File without changes
|
data/lib/cassandra/debug.rb
CHANGED
File without changes
|
data/lib/cassandra/helpers.rb
CHANGED
File without changes
|
File without changes
|
data/lib/cassandra/long.rb
CHANGED
File without changes
|
data/lib/cassandra/mock.rb
CHANGED
@@ -34,6 +34,14 @@ class Cassandra
|
|
34
34
|
@data[column_family.to_sym] = OrderedHash.new
|
35
35
|
end
|
36
36
|
|
37
|
+
def default_write_consistency=(value)
|
38
|
+
WRITE_DEFAULTS[:consistency] = value
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_read_consistency=(value)
|
42
|
+
READ_DEFAULTS[:consistency] = value
|
43
|
+
end
|
44
|
+
|
37
45
|
def insert(column_family, key, hash_or_array, options = {})
|
38
46
|
if @batch
|
39
47
|
@batch << [:insert, column_family, key, hash_or_array, options]
|
@@ -118,8 +126,11 @@ class Cassandra
|
|
118
126
|
end
|
119
127
|
end
|
120
128
|
|
121
|
-
def exists?(column_family, key,
|
122
|
-
|
129
|
+
def exists?(column_family, key, *columns_and_options)
|
130
|
+
column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, [key], columns_and_options, READ_DEFAULTS)
|
131
|
+
results = get(column_family, key, column, sub_column)
|
132
|
+
|
133
|
+
![{}, nil].include?(results)
|
123
134
|
end
|
124
135
|
|
125
136
|
def multi_get(column_family, keys, *columns_and_options)
|
@@ -258,21 +269,28 @@ class Cassandra
|
|
258
269
|
end
|
259
270
|
end
|
260
271
|
|
261
|
-
def
|
272
|
+
def create_index_expression(c_name, value, op)
|
262
273
|
{:column_name => c_name, :value => value, :comparison => op}
|
263
274
|
end
|
275
|
+
alias :create_idx_expr :create_index_expression
|
264
276
|
|
265
|
-
def
|
266
|
-
{:start => start, :index_expressions => idx_expressions}
|
277
|
+
def create_index_clause(idx_expressions, start = "", count = 100)
|
278
|
+
{:start => start, :index_expressions => idx_expressions, :count => count, :type => :index_clause}
|
267
279
|
end
|
280
|
+
alias :create_idx_clause :create_index_clause
|
268
281
|
|
269
282
|
def get_indexed_slices(column_family, idx_clause, *columns_and_options)
|
270
283
|
column_family, columns, _, options =
|
271
|
-
extract_and_validate_params_for_real(column_family, [], columns_and_options, READ_DEFAULTS)
|
284
|
+
extract_and_validate_params_for_real(column_family, [], columns_and_options, READ_DEFAULTS.merge(:key_count => 100, :key_start => ""))
|
285
|
+
|
286
|
+
unless [Hash, OrderedHash].include?(idx_clause.class) && idx_clause[:type] == :index_clause
|
287
|
+
idx_clause = create_index_clause(idx_clause, options[:key_start], options[:key_count])
|
288
|
+
end
|
272
289
|
|
273
290
|
ret = {}
|
274
291
|
cf(column_family).each do |key, row|
|
275
292
|
next if idx_clause[:start] != '' && key < idx_clause[:start]
|
293
|
+
next if ret.length == idx_clause[:count]
|
276
294
|
|
277
295
|
matches = []
|
278
296
|
idx_clause[:index_expressions].each do |expr|
|
@@ -288,6 +306,23 @@ class Cassandra
|
|
288
306
|
ret
|
289
307
|
end
|
290
308
|
|
309
|
+
def add(column_family, key, value, *columns_and_options)
|
310
|
+
column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, WRITE_DEFAULTS)
|
311
|
+
|
312
|
+
if is_super(column_family)
|
313
|
+
cf(column_family)[key] ||= OrderedHash.new
|
314
|
+
cf(column_family)[key][column] ||= OrderedHash.new
|
315
|
+
cf(column_family)[key][column][sub_column] ||= 0
|
316
|
+
cf(column_family)[key][column][sub_column] += value
|
317
|
+
else
|
318
|
+
cf(column_family)[key] ||= OrderedHash.new
|
319
|
+
cf(column_family)[key][column] ||= 0
|
320
|
+
cf(column_family)[key][column] += value
|
321
|
+
end
|
322
|
+
|
323
|
+
nil
|
324
|
+
end
|
325
|
+
|
291
326
|
def schema(load=true)
|
292
327
|
@schema
|
293
328
|
end
|
@@ -310,9 +345,11 @@ class Cassandra
|
|
310
345
|
break if ret.keys.size >= key_count
|
311
346
|
if (start_key.nil? || key >= start_key) && (finish_key.nil? || key <= finish_key)
|
312
347
|
if columns
|
313
|
-
ret[key] = columns.inject(OrderedHash.new){|hash, column_name| hash[column_name] = cf(column_family)[key][column_name]; hash;}
|
348
|
+
#ret[key] = columns.inject(OrderedHash.new){|hash, column_name| hash[column_name] = cf(column_family)[key][column_name]; hash;}
|
349
|
+
ret[key] = columns_to_hash(column_family, cf(column_family)[key].select{|k,v| columns.include?(k)})
|
314
350
|
else
|
315
|
-
ret[key] = apply_range(cf(column_family)[key], column_family, start, finish, !is_super(column_family))
|
351
|
+
#ret[key] = apply_range(cf(column_family)[key], column_family, start, finish, !is_super(column_family))
|
352
|
+
ret[key] = apply_range(columns_to_hash(column_family, cf(column_family)[key]), column_family, start, finish)
|
316
353
|
end
|
317
354
|
end
|
318
355
|
end
|
File without changes
|
@@ -0,0 +1,119 @@
|
|
1
|
+
class Cassandra
|
2
|
+
# Inner methods for actually doing the Thrift calls
|
3
|
+
module Protocol #:nodoc:
|
4
|
+
private
|
5
|
+
|
6
|
+
def _mutate(mutation_map, consistency_level)
|
7
|
+
client.batch_mutate(mutation_map, consistency_level)
|
8
|
+
end
|
9
|
+
|
10
|
+
def _remove(key, column_path, timestamp, consistency_level)
|
11
|
+
client.remove(key, column_path, timestamp, consistency_level)
|
12
|
+
end
|
13
|
+
|
14
|
+
# FIXME: Add support for start, stop, count
|
15
|
+
def _count_columns(column_family, key, super_column, consistency)
|
16
|
+
client.get_count(key,
|
17
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
|
18
|
+
CassandraThrift::SlicePredicate.new(:slice_range =>
|
19
|
+
CassandraThrift::SliceRange.new(
|
20
|
+
:start => '',
|
21
|
+
:finish => ''
|
22
|
+
)),
|
23
|
+
consistency
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# FIXME: Add support for start, stop, count
|
28
|
+
def _get_columns(column_family, key, columns, sub_columns, consistency)
|
29
|
+
result = if is_super(column_family)
|
30
|
+
if sub_columns
|
31
|
+
columns_to_hash(column_family, client.get_slice(key,
|
32
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
|
33
|
+
CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
|
34
|
+
consistency))
|
35
|
+
else
|
36
|
+
columns_to_hash(column_family, client.get_slice(key,
|
37
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
38
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
39
|
+
consistency))
|
40
|
+
end
|
41
|
+
else
|
42
|
+
columns_to_hash(column_family, client.get_slice(key,
|
43
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
44
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
45
|
+
consistency))
|
46
|
+
end
|
47
|
+
|
48
|
+
klass = column_name_class(column_family)
|
49
|
+
(sub_columns || columns).map { |name| result[klass.new(name)] }
|
50
|
+
end
|
51
|
+
|
52
|
+
def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
|
53
|
+
# Single values; count and range parameters have no effect
|
54
|
+
if is_super(column_family) and sub_column
|
55
|
+
predicate = CassandraThrift::SlicePredicate.new(:column_names => [sub_column])
|
56
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
|
57
|
+
column_hash = multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
|
58
|
+
|
59
|
+
klass = sub_column_name_class(column_family)
|
60
|
+
keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(sub_column)]; hash}
|
61
|
+
elsif !is_super(column_family) and column
|
62
|
+
predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
|
63
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
64
|
+
column_hash = multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
|
65
|
+
|
66
|
+
klass = column_name_class(column_family)
|
67
|
+
keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(column)]; hash}
|
68
|
+
|
69
|
+
# Slices
|
70
|
+
else
|
71
|
+
predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
|
72
|
+
CassandraThrift::SliceRange.new(
|
73
|
+
:reversed => reversed,
|
74
|
+
:count => count,
|
75
|
+
:start => start,
|
76
|
+
:finish => finish))
|
77
|
+
|
78
|
+
if is_super(column_family) and column
|
79
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
|
80
|
+
multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
|
81
|
+
else
|
82
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
83
|
+
multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency)
|
89
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
90
|
+
predicate = if columns
|
91
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns)
|
92
|
+
else
|
93
|
+
CassandraThrift::SlicePredicate.new(:slice_range =>
|
94
|
+
CassandraThrift::SliceRange.new(
|
95
|
+
:start => start,
|
96
|
+
:finish => finish,
|
97
|
+
:count => count))
|
98
|
+
end
|
99
|
+
range = CassandraThrift::KeyRange.new(:start_key => start_key, :end_key => finish_key, :count => key_count)
|
100
|
+
client.get_range_slices(column_parent, predicate, range, consistency)
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: Supercolumn support
|
104
|
+
def _get_indexed_slices(column_family, index_clause, column, count, start, finish, reversed, consistency)
|
105
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
106
|
+
if column
|
107
|
+
predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
|
108
|
+
else
|
109
|
+
predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
|
110
|
+
CassandraThrift::SliceRange.new(
|
111
|
+
:reversed => reversed,
|
112
|
+
:count => count,
|
113
|
+
:start => start,
|
114
|
+
:finish => finish))
|
115
|
+
end
|
116
|
+
client.get_indexed_slices(column_parent, index_clause, predicate, consistency)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/cassandra/time.rb
CHANGED
File without changes
|
File without changes
|
data/test/cassandra_mock_test.rb
CHANGED
@@ -17,6 +17,9 @@ class CassandraMockTest < CassandraTest
|
|
17
17
|
@blogs_long = Cassandra::Mock.new('MultiblogLong', @test_schema)
|
18
18
|
@blogs_long.clear_keyspace!
|
19
19
|
|
20
|
+
@type_conversions = Cassandra::Mock.new('TypeConversions', @test_schema)
|
21
|
+
@type_conversions.clear_keyspace!
|
22
|
+
|
20
23
|
@uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
|
21
24
|
@longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
|
22
25
|
end
|
data/test/cassandra_test.rb
CHANGED
@@ -4,15 +4,21 @@ class CassandraTest < Test::Unit::TestCase
|
|
4
4
|
include Cassandra::Constants
|
5
5
|
|
6
6
|
def setup
|
7
|
-
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
|
7
|
+
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
|
8
8
|
@twitter.clear_keyspace!
|
9
9
|
|
10
|
-
@blogs = Cassandra.new('Multiblog', "127.0.0.1:9160"
|
10
|
+
@blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
|
11
11
|
@blogs.clear_keyspace!
|
12
12
|
|
13
|
-
@blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160"
|
13
|
+
@blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
|
14
14
|
@blogs_long.clear_keyspace!
|
15
15
|
|
16
|
+
@type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
|
17
|
+
@type_conversions.clear_keyspace!
|
18
|
+
|
19
|
+
Cassandra::WRITE_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
|
20
|
+
Cassandra::READ_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
|
21
|
+
|
16
22
|
@uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
|
17
23
|
@longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
|
18
24
|
end
|
@@ -24,13 +30,37 @@ class CassandraTest < Test::Unit::TestCase
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
def test_setting_default_consistency
|
34
|
+
assert_nothing_raised do
|
35
|
+
@twitter.default_read_consistency = Cassandra::Consistency::ALL
|
36
|
+
end
|
37
|
+
assert_equal(Cassandra::READ_DEFAULTS[:consistency], Cassandra::Consistency::ALL)
|
38
|
+
|
39
|
+
assert_nothing_raised do
|
40
|
+
@twitter.default_write_consistency = Cassandra::Consistency::ALL
|
41
|
+
end
|
42
|
+
assert_equal(Cassandra::WRITE_DEFAULTS[:consistency], Cassandra::Consistency::ALL)
|
43
|
+
end
|
44
|
+
|
27
45
|
def test_get_key
|
46
|
+
|
28
47
|
@twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
|
29
48
|
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
|
30
49
|
assert_equal(['body', 'user'].sort, @twitter.get(:Users, key).timestamps.keys.sort)
|
31
50
|
assert_equal({}, @twitter.get(:Users, 'bogus'))
|
32
51
|
end
|
33
52
|
|
53
|
+
def test_get_single_column_returns_single_value
|
54
|
+
@twitter.insert(:Users, key, {'body' => 'body_text', 'user' => 'user_name'})
|
55
|
+
assert_equal('body_text', @twitter.get(:Users, key, 'body'))
|
56
|
+
assert_equal('user_name', @twitter.get(:Users, key, 'user'))
|
57
|
+
|
58
|
+
@blogs.insert(:Blogs, key,
|
59
|
+
{@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
|
60
|
+
|
61
|
+
assert_equal('I like this cat', @blogs.get(:Blogs, key, @uuids[0]))
|
62
|
+
end
|
63
|
+
|
34
64
|
def test_get_key_preserving_order
|
35
65
|
# In-order hash is preserved
|
36
66
|
hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
|
@@ -116,9 +146,25 @@ class CassandraTest < Test::Unit::TestCase
|
|
116
146
|
assert_equal 5, @twitter.get(:Statuses, k, :start => "body-0", :finish => "body-4", :count => 7).length
|
117
147
|
end
|
118
148
|
|
119
|
-
def
|
149
|
+
def test_exists
|
120
150
|
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
121
|
-
|
151
|
+
assert_equal true, @twitter.exists?(:Statuses, key)
|
152
|
+
assert_equal false, @twitter.exists?(:Statuses, 'bogus')
|
153
|
+
|
154
|
+
columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
|
155
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
|
156
|
+
|
157
|
+
# verify return value when searching by key
|
158
|
+
assert_equal true, @twitter.exists?(:StatusRelationships, key)
|
159
|
+
assert_equal false, @twitter.exists?(:StatusRelationships, 'bogus')
|
160
|
+
|
161
|
+
# verify return value when searching by key and column
|
162
|
+
assert_equal true, @twitter.exists?(:StatusRelationships, key, 'user_timelines')
|
163
|
+
assert_equal false, @twitter.exists?(:StatusRelationships, key, 'bogus')
|
164
|
+
|
165
|
+
# verify return value when searching by key and column and subcolumn
|
166
|
+
assert_equal true, @twitter.exists?(:StatusRelationships, key, 'user_timelines', @uuids[1])
|
167
|
+
assert_equal false, @twitter.exists?(:StatusRelationships, key, 'user_timelines', @uuids[3])
|
122
168
|
end
|
123
169
|
|
124
170
|
def test_get_super_key
|
@@ -180,7 +226,7 @@ class CassandraTest < Test::Unit::TestCase
|
|
180
226
|
assert_equal(columns.keys.sort, @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps.keys.sort)
|
181
227
|
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
|
182
228
|
# FIXME Not sure if this is valid
|
183
|
-
|
229
|
+
assert_equal false, @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
|
184
230
|
end
|
185
231
|
|
186
232
|
def test_get_super_value
|
@@ -190,16 +236,16 @@ class CassandraTest < Test::Unit::TestCase
|
|
190
236
|
assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
|
191
237
|
end
|
192
238
|
|
193
|
-
def test_get_range_with_key_range
|
194
|
-
skip('This test requires the use of OrderPreservingPartitioner on the cluster to work properly.')
|
195
|
-
k = key
|
196
|
-
@twitter.insert(:Statuses, k + '2', {'body' => '1'})
|
197
|
-
@twitter.insert(:Statuses, k + '3', {'body' => '1'})
|
198
|
-
@twitter.insert(:Statuses, k + '4', {'body' => '1'})
|
199
|
-
@twitter.insert(:Statuses, k + '5', {'body' => '1'})
|
200
|
-
@twitter.insert(:Statuses, k + '6', {'body' => '1'})
|
201
|
-
assert_equal([k + '3', k + '4', k + '5'], @twitter.get_range(:Statuses, :start_key => k + '3', :finish_key => k + '5').keys)
|
202
|
-
end
|
239
|
+
# def test_get_range_with_key_range
|
240
|
+
# skip('This test requires the use of OrderPreservingPartitioner on the cluster to work properly.')
|
241
|
+
# k = key
|
242
|
+
# @twitter.insert(:Statuses, k + '2', {'body' => '1'})
|
243
|
+
# @twitter.insert(:Statuses, k + '3', {'body' => '1'})
|
244
|
+
# @twitter.insert(:Statuses, k + '4', {'body' => '1'})
|
245
|
+
# @twitter.insert(:Statuses, k + '5', {'body' => '1'})
|
246
|
+
# @twitter.insert(:Statuses, k + '6', {'body' => '1'})
|
247
|
+
# assert_equal([k + '3', k + '4', k + '5'], @twitter.get_range(:Statuses, :start_key => k + '3', :finish_key => k + '5').keys)
|
248
|
+
# end
|
203
249
|
|
204
250
|
def test_get_range
|
205
251
|
# make sure that deleted rows are not included in the iteration
|
@@ -524,6 +570,73 @@ class CassandraTest < Test::Unit::TestCase
|
|
524
570
|
assert_equal({'user' => 'user'}, @twitter.get(:Users, k))
|
525
571
|
end
|
526
572
|
|
573
|
+
def test_each_key
|
574
|
+
num_users = rand(60)
|
575
|
+
num_users.times do |twit_counter|
|
576
|
+
@twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
|
577
|
+
end
|
578
|
+
counter = 0
|
579
|
+
@twitter.each_key(:Users) do |_, _|
|
580
|
+
counter += 1
|
581
|
+
end
|
582
|
+
assert_equal num_users, counter
|
583
|
+
end
|
584
|
+
|
585
|
+
def test_each_with_column_predicate
|
586
|
+
num_users = rand(60)
|
587
|
+
num_users.times do |twit_counter|
|
588
|
+
@twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
|
589
|
+
end
|
590
|
+
counter = 0
|
591
|
+
@twitter.each(:Users, :batch_size => 10, :start => 'body', :finish => 'body') do |key, columns|
|
592
|
+
assert_equal 1, columns.length
|
593
|
+
counter += 1
|
594
|
+
end
|
595
|
+
assert_equal num_users, counter
|
596
|
+
end
|
597
|
+
|
598
|
+
def test_each_with_super_column
|
599
|
+
num_users = rand(50)
|
600
|
+
block_name = key
|
601
|
+
num_users.times do |twit_counter|
|
602
|
+
@twitter.insert(:StatusRelationships, block_name + twit_counter.to_s, {
|
603
|
+
'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'},
|
604
|
+
'mentions_timelines' => {@uuids[3] => 'v3'}})
|
605
|
+
end
|
606
|
+
|
607
|
+
counter = 0
|
608
|
+
# Restrict to one super column ::
|
609
|
+
@twitter.each(:StatusRelationships, :batch_size => 10, :start => 'user_timelines', :finish => 'user_timelines') do |key, columns|
|
610
|
+
columns.each do |_, column_value|
|
611
|
+
assert_equal 2, column_value.length
|
612
|
+
end
|
613
|
+
counter += 1
|
614
|
+
end
|
615
|
+
|
616
|
+
#Both super columns
|
617
|
+
@twitter.each(:StatusRelationships, :batch_size => 10, :start => 'mentions_timelines', :finish => 'user_timelines') do |key,columns|
|
618
|
+
assert_equal 2, columns.length
|
619
|
+
counter += 1
|
620
|
+
end
|
621
|
+
|
622
|
+
assert_equal num_users*2, counter
|
623
|
+
|
624
|
+
end
|
625
|
+
|
626
|
+
def test_each_column_types
|
627
|
+
num_users = rand(60)
|
628
|
+
num_users.times do |twit_counter|
|
629
|
+
@type_conversions.insert(:UUIDColumnConversion, twit_counter.to_s, {@uuids[1] => 'v1'})
|
630
|
+
end
|
631
|
+
counter = 0
|
632
|
+
@type_conversions.each(:UUIDColumnConversion) do |_, columns|
|
633
|
+
counter += 1
|
634
|
+
columns.keys.each {|column_name| assert_equal SimpleUUID::UUID, column_name.class}
|
635
|
+
end
|
636
|
+
assert_equal num_users, counter
|
637
|
+
end
|
638
|
+
|
639
|
+
|
527
640
|
if CASSANDRA_VERSION.to_f >= 0.7
|
528
641
|
def test_creating_and_dropping_new_index
|
529
642
|
@twitter.create_index('Twitter', 'Statuses', 'column_name', 'LongType')
|
@@ -541,14 +654,83 @@ class CassandraTest < Test::Unit::TestCase
|
|
541
654
|
@twitter.create_index('Twitter', 'Statuses', 'x', 'LongType')
|
542
655
|
|
543
656
|
@twitter.insert(:Statuses, 'row1', { 'x' => [0,10].pack("NN") })
|
544
|
-
|
657
|
+
|
658
|
+
(2..10).to_a.each do |i|
|
659
|
+
@twitter.insert(:Statuses, 'row' + i.to_s, { 'x' => [0,20].pack("NN"), 'non_indexed' => [i].pack('N*') })
|
660
|
+
end
|
661
|
+
|
662
|
+
@twitter.insert(:Statuses, 'row11', { 'x' => [0,30].pack("NN") })
|
663
|
+
|
664
|
+
expressions = [{:column_name => 'x', :value => [0,20].pack("NN"), :comparison => "=="}]
|
665
|
+
|
666
|
+
# verify multiples will be returned
|
667
|
+
assert_equal 9, @twitter.get_indexed_slices(:Statuses, expressions).length
|
668
|
+
|
669
|
+
# verify that GT and LT queries perform properly
|
670
|
+
expressions = [
|
671
|
+
{:column_name => 'x', :value => [0,20].pack("NN"), :comparison => "=="},
|
672
|
+
{:column_name => 'non_indexed', :value => [5].pack("N*"), :comparison => ">"}
|
673
|
+
]
|
674
|
+
assert_equal(5, @twitter.get_indexed_slices(:Statuses, expressions).length)
|
675
|
+
end
|
676
|
+
|
677
|
+
def test_old_get_indexed_slices
|
678
|
+
@twitter.create_index('Twitter', 'Statuses', 'x', 'LongType')
|
679
|
+
|
680
|
+
@twitter.insert(:Statuses, 'row1', { 'x' => [0,10].pack("NN") })
|
681
|
+
|
682
|
+
(2..10).to_a.each do |i|
|
683
|
+
@twitter.insert(:Statuses, 'row' + i.to_s, { 'x' => [0,20].pack("NN"), 'non_indexed' => [i].pack('N*') })
|
684
|
+
end
|
685
|
+
|
686
|
+
@twitter.insert(:Statuses, 'row11', { 'x' => [0,30].pack("NN") })
|
545
687
|
|
546
688
|
idx_expr = @twitter.create_idx_expr('x', [0,20].pack("NN"), "==")
|
689
|
+
|
690
|
+
# verify count is observed
|
691
|
+
idx_clause = @twitter.create_idx_clause([idx_expr], "", 1)
|
692
|
+
assert_equal 1, @twitter.get_indexed_slices(:Statuses, idx_clause).length
|
693
|
+
|
694
|
+
# verify multiples will be returned
|
547
695
|
idx_clause = @twitter.create_idx_clause([idx_expr])
|
696
|
+
assert_equal 9, @twitter.get_indexed_slices(:Statuses, idx_clause).length
|
697
|
+
|
698
|
+
# verify that GT and LT queries perform properly
|
699
|
+
idx_expr = [
|
700
|
+
@twitter.create_idx_expr('x', [0,20].pack("NN"), "=="),
|
701
|
+
@twitter.create_idx_expr('non_indexed', [5].pack("N*"), ">")
|
702
|
+
]
|
703
|
+
idx_clause = @twitter.create_idx_clause(idx_expr)
|
704
|
+
assert_equal(5, @twitter.get_indexed_slices(:Statuses, idx_clause).length)
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
if CASSANDRA_VERSION.to_f >= 0.8
|
709
|
+
def test_adding_getting_value_in_counter
|
710
|
+
assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
|
711
|
+
assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
|
712
|
+
assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
|
713
|
+
end
|
714
|
+
|
715
|
+
def test_get_counter_slice
|
716
|
+
assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
|
717
|
+
assert_equal({'tweet_count' => 5}, @twitter.get(:UserCounters, 'bob', :start => "tweet_count", :finish => "tweet_count"))
|
718
|
+
end
|
719
|
+
|
720
|
+
def test_adding_getting_value_in_multiple_counters
|
721
|
+
assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
|
722
|
+
assert_nil @twitter.add(:UserCounters, 'bob', 7, 'follower_count')
|
723
|
+
assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
|
724
|
+
assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
|
725
|
+
assert_equal([5, 7], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count']))
|
726
|
+
assert_equal([5, 7, nil], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count', 'bogus']))
|
727
|
+
end
|
548
728
|
|
549
|
-
|
550
|
-
|
551
|
-
|
729
|
+
def test_adding_getting_value_in_multiple_counters_with_super_columns
|
730
|
+
assert_nil @twitter.add(:UserCounterAggregates, 'bob', 1, 'DAU', 'today')
|
731
|
+
assert_nil @twitter.add(:UserCounterAggregates, 'bob', 2, 'DAU', 'tomorrow')
|
732
|
+
assert_equal(1, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'today'))
|
733
|
+
assert_equal(2, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'tomorrow'))
|
552
734
|
end
|
553
735
|
end
|
554
736
|
|