cassandra 0.11.0 → 0.11.1
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/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
|
|