cassandra 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGELOG +12 -0
  2. data/Manifest +31 -12
  3. data/README.rdoc +3 -2
  4. data/Rakefile +53 -23
  5. data/cassandra.gemspec +6 -8
  6. data/conf/{cassandra.in.sh → 0.6/cassandra.in.sh} +0 -0
  7. data/conf/{log4j.properties → 0.6/log4j.properties} +0 -0
  8. data/conf/0.6/schema.json +48 -0
  9. data/conf/{storage-conf.xml → 0.6/storage-conf.xml} +5 -5
  10. data/conf/0.7/cassandra.in.sh +46 -0
  11. data/conf/0.7/cassandra.yaml +336 -0
  12. data/conf/0.7/log4j-server.properties +41 -0
  13. data/conf/0.7/schema.json +48 -0
  14. data/conf/0.7/schema.txt +56 -0
  15. data/conf/0.8/cassandra.in.sh +41 -0
  16. data/conf/0.8/cassandra.yaml +61 -0
  17. data/conf/0.8/log4j-server.properties +40 -0
  18. data/conf/0.8/schema.json +48 -0
  19. data/conf/0.8/schema.txt +56 -0
  20. data/lib/cassandra.rb +1 -1
  21. data/lib/cassandra/0.6/cassandra.rb +1 -0
  22. data/lib/cassandra/0.6/columns.rb +5 -7
  23. data/lib/cassandra/0.6/protocol.rb +1 -1
  24. data/lib/cassandra/0.7/cassandra.rb +5 -5
  25. data/lib/cassandra/0.7/columns.rb +5 -6
  26. data/lib/cassandra/0.7/protocol.rb +12 -3
  27. data/lib/cassandra/0.8.rb +7 -0
  28. data/lib/cassandra/0.8/cassandra.rb +272 -0
  29. data/lib/cassandra/0.8/column_family.rb +3 -0
  30. data/lib/cassandra/0.8/columns.rb +84 -0
  31. data/lib/cassandra/0.8/keyspace.rb +3 -0
  32. data/lib/cassandra/0.8/protocol.rb +120 -0
  33. data/lib/cassandra/cassandra.rb +6 -11
  34. data/lib/cassandra/helpers.rb +1 -0
  35. data/lib/cassandra/mock.rb +107 -64
  36. data/lib/cassandra/ordered_hash.rb +1 -6
  37. data/test/cassandra_mock_test.rb +7 -27
  38. data/test/cassandra_test.rb +41 -15
  39. data/test/eventmachine_test.rb +30 -30
  40. data/test/test_helper.rb +2 -1
  41. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  42. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  43. data/vendor/0.8/gen-rb/cassandra_types.rb +814 -0
  44. metadata +50 -27
  45. data/conf/cassandra.yaml +0 -113
@@ -0,0 +1,3 @@
1
+ class Cassandra
2
+ class Keyspace < CassandraThrift::KsDef ; end
3
+ end
@@ -0,0 +1,120 @@
1
+
2
+ class Cassandra
3
+ # Inner methods for actually doing the Thrift calls
4
+ module Protocol #:nodoc:
5
+ private
6
+
7
+ def _mutate(mutation_map, consistency_level)
8
+ client.batch_mutate(mutation_map, consistency_level)
9
+ end
10
+
11
+ def _remove(key, column_path, timestamp, consistency_level)
12
+ client.remove(key, column_path, timestamp, consistency_level)
13
+ end
14
+
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
+ def _get_columns(column_family, key, columns, sub_columns, consistency)
28
+ result = if is_super(column_family)
29
+ if sub_columns
30
+ columns_to_hash(column_family, client.get_slice(key,
31
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
32
+ CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
33
+ consistency))
34
+ else
35
+ columns_to_hash(column_family, client.get_slice(key,
36
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
37
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
38
+ consistency))
39
+ end
40
+ else
41
+ columns_to_hash(column_family, client.get_slice(key,
42
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
43
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
44
+ consistency))
45
+ end
46
+
47
+ klass = column_name_class(column_family)
48
+ (sub_columns || columns).map { |name| result[klass.new(name)] }
49
+ end
50
+
51
+ def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
52
+ # Single values; count and range parameters have no effect
53
+ if is_super(column_family) and sub_column
54
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [sub_column])
55
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
56
+ column_hash = multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
57
+
58
+ klass = sub_column_name_class(column_family)
59
+ keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(sub_column)]; hash}
60
+ elsif !is_super(column_family) and column
61
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
62
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
63
+ column_hash = multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
64
+
65
+ keys.inject({}){|hash, key| hash[key] = column_hash[key][column]; hash}
66
+
67
+ # Slices
68
+ else
69
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
70
+ CassandraThrift::SliceRange.new(
71
+ :reversed => reversed,
72
+ :count => count,
73
+ :start => start,
74
+ :finish => finish))
75
+
76
+ if is_super(column_family) and column
77
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
78
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
79
+ else
80
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
81
+ multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
82
+ end
83
+ end
84
+ end
85
+
86
+ def _get_range(column_family, start, finish, count, consistency)
87
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
88
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range => CassandraThrift::SliceRange.new(:start => '', :finish => ''))
89
+ range = CassandraThrift::KeyRange.new(:start_key => start, :end_key => finish, :count => count)
90
+ client.get_range_slices(column_parent, predicate, range, 1)
91
+ end
92
+
93
+ def _get_range_keys(column_family, start, finish, count, consistency)
94
+ _get_range(column_family, start, finish, count, consistency).collect{|i| i.key }
95
+ end
96
+
97
+ # TODO: Supercolumn support
98
+ def _get_indexed_slices(column_family, idx_clause, column, count, start, finish, reversed, consistency)
99
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
100
+ if column
101
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
102
+ else
103
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
104
+ CassandraThrift::SliceRange.new(
105
+ :reversed => reversed,
106
+ :count => count,
107
+ :start => start,
108
+ :finish => finish))
109
+ end
110
+ client.get_indexed_slices(column_parent, idx_clause, predicate, consistency)
111
+ end
112
+
113
+ def each_key(column_family)
114
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family.to_s)
115
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [])
116
+ range = CassandraThrift::KeyRange.new(:start_key => '', :end_key => '')
117
+ client.get_range_slices(column_parent, predicate, range, 1).each{|i| yield i.key }
118
+ end
119
+ end
120
+ end
@@ -146,9 +146,9 @@ class Cassandra
146
146
  mutation_map =
147
147
  {
148
148
  key => {
149
- column_family => [ _delete_mutation(column_family , is_super(column_family)? column : nil , sub_column , options[:timestamp]|| Time.stamp) ]
149
+ column_family => [ _delete_mutation(column_family, column, sub_column, options[:timestamp]|| Time.stamp) ]
150
150
  }
151
- }
151
+ }
152
152
  @batch << [mutation_map, options[:consistency]]
153
153
  else
154
154
  # Let's continue using the 'remove' thrift method...not sure about the implications/performance of using the mutate instead
@@ -276,7 +276,7 @@ class Cassandra
276
276
  # Roll up queued mutations, to improve atomicity (and performance).
277
277
  def compact_mutations!
278
278
  used_clevels = {} # hash that lists the consistency levels seen in the batch array. key is the clevel, value is true
279
- by_key = {}
279
+ by_key = Hash.new{|h,k | h[k] = {}}
280
280
  # @batch is an array of mutation_ops.
281
281
  # A mutation op is a 2-item array containing [mutationmap, consistency_number]
282
282
  # a mutation map is a hash, by key (string) that has a hash by CF name, containing a list of column_mutations)
@@ -291,18 +291,13 @@ class Cassandra
291
291
  # }, # [0]
292
292
  # consistency # [1]
293
293
  #]
294
- # For a remove:
295
- # [ :remove, # [0]
296
- # [key, CassThrift:ColPath, timestamp, consistency ] # [1]
297
- # ]
298
294
  mmap = mutation_op[0] # :remove OR a hash like {"key"=> {"CF"=>[mutationclass1,...] } }
299
- used_clevels[mutation_op[1]]=true #save the clevel required for this operation
295
+ used_clevels[mutation_op[1]] = true #save the clevel required for this operation
300
296
 
301
297
  mmap.keys.each do |k|
302
- by_key[k] = {} unless by_key.has_key? k #make sure the key exists
303
298
  mmap[k].keys.each do |cf| # For each CF in that key
304
- by_key[k][cf] = [] unless by_key[k][cf] != nil
305
- by_key[k][cf].concat mmap[k][cf] # Append the list of mutations for that key and CF
299
+ by_key[k][cf] ||= []
300
+ by_key[k][cf].concat(mmap[k][cf]) # Append the list of mutations for that key and CF
306
301
  end
307
302
  end
308
303
  end
@@ -17,6 +17,7 @@ class Cassandra
17
17
 
18
18
  # Ranges
19
19
  column, sub_column = args[0], args[1]
20
+ raise ArgumentError, "Invalid arguments: subcolumns specified for a non-supercolumn family" if sub_column && !is_super(column_family)
20
21
  klass, sub_klass = column_name_class(column_family), sub_column_name_class(column_family)
21
22
  range_class = column ? sub_klass : klass
22
23
 
@@ -1,5 +1,3 @@
1
- require 'nokogiri'
2
-
3
1
  class SimpleUUID::UUID
4
2
  def >=(other)
5
3
  (self <=> other) >= 0
@@ -15,11 +13,13 @@ class Cassandra
15
13
  include ::Cassandra::Helpers
16
14
  include ::Cassandra::Columns
17
15
 
18
- def initialize(keyspace, storage_xml)
16
+ def initialize(keyspace, schema)
17
+ @is_super = {}
19
18
  @keyspace = keyspace
20
19
  @column_name_class = {}
21
20
  @sub_column_name_class = {}
22
- @storage_xml = storage_xml
21
+ @indexes = {}
22
+ @schema = schema[keyspace]
23
23
  clear_keyspace!
24
24
  end
25
25
 
@@ -39,7 +39,7 @@ class Cassandra
39
39
  @batch << [:insert, column_family, key, hash_or_array, options]
40
40
  else
41
41
  raise ArgumentError if key.nil?
42
- if column_family_type(column_family) == 'Standard'
42
+ if !is_super(column_family)
43
43
  insert_standard(column_family, key, hash_or_array)
44
44
  else
45
45
  insert_super(column_family, key, hash_or_array)
@@ -77,7 +77,7 @@ class Cassandra
77
77
  def get(column_family, key, *columns_and_options)
78
78
  column_family, column, sub_column, options =
79
79
  extract_and_validate_params_for_real(column_family, [key], columns_and_options, READ_DEFAULTS)
80
- if column_family_type(column_family) == 'Standard'
80
+ if !is_super(column_family)
81
81
  get_standard(column_family, key, column, options)
82
82
  else
83
83
  get_super(column_family, key, column, sub_column, options)
@@ -85,7 +85,9 @@ class Cassandra
85
85
  end
86
86
 
87
87
  def get_standard(column_family, key, column, options)
88
- row = cf(column_family)[key] || OrderedHash.new
88
+ columns = cf(column_family)[key] || OrderedHash.new
89
+ row = columns_to_hash(column_family, columns)
90
+
89
91
  if column
90
92
  row[column]
91
93
  else
@@ -95,26 +97,24 @@ class Cassandra
95
97
  end
96
98
 
97
99
  def get_super(column_family, key, column, sub_column, options)
100
+ columns = cf(column_family)[key] || OrderedHash.new
101
+ row = columns_to_hash(column_family, columns)
102
+
98
103
  if column
99
104
  if sub_column
100
- if cf(column_family)[key] &&
101
- cf(column_family)[key][column] &&
102
- cf(column_family)[key][column][sub_column]
103
- cf(column_family)[key][column][sub_column]
105
+ if row[column] &&
106
+ row[column][sub_column]
107
+ row[column][sub_column]
104
108
  else
105
109
  nil
106
110
  end
107
111
  else
108
- row = cf(column_family)[key] && cf(column_family)[key][column] ?
109
- cf(column_family)[key][column] :
110
- OrderedHash.new
112
+ row = row[column] || OrderedHash.new
111
113
  row = apply_range(row, column_family, options[:start], options[:finish], false)
112
114
  apply_count(row, options[:count], options[:reversed])
113
115
  end
114
- elsif cf(column_family)[key]
115
- cf(column_family)[key]
116
116
  else
117
- OrderedHash.new
117
+ row
118
118
  end
119
119
  end
120
120
 
@@ -137,9 +137,9 @@ class Cassandra
137
137
  else
138
138
  if column
139
139
  if sub_column
140
- cf(column_family)[key][column].delete(sub_column)
140
+ cf(column_family)[key][column].delete(sub_column.to_s)
141
141
  else
142
- cf(column_family)[key].delete(column)
142
+ cf(column_family)[key].delete(column.to_s)
143
143
  end
144
144
  else
145
145
  cf(column_family).delete(key)
@@ -151,7 +151,6 @@ class Cassandra
151
151
  column_family, columns, sub_columns, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, READ_DEFAULTS)
152
152
  d = get(column_family, key)
153
153
 
154
-
155
154
  if sub_columns
156
155
  sub_columns.collect do |sub_column|
157
156
  d[columns][sub_column]
@@ -197,16 +196,74 @@ class Cassandra
197
196
  count
198
197
  end
199
198
 
200
- def schema(load=true)
201
- if !load && !@schema
202
- []
199
+ def create_index(ks_name, cf_name, c_name, v_class)
200
+ if @indexes[ks_name] &&
201
+ @indexes[ks_name][cf_name] &&
202
+ @indexes[ks_name][cf_name][c_name]
203
+ nil
204
+
205
+ else
206
+ @indexes[ks_name] ||= {}
207
+ @indexes[ks_name][cf_name] ||= {}
208
+ @indexes[ks_name][cf_name][c_name] = true
209
+ end
210
+ end
211
+
212
+ def drop_index(ks_name, cf_name, c_name)
213
+ if @indexes[ks_name] &&
214
+ @indexes[ks_name][cf_name] &&
215
+ @indexes[ks_name][cf_name][c_name]
216
+
217
+ @indexes[ks_name][cf_name].delete(c_name)
203
218
  else
204
- @schema ||= schema_for_keyspace(@keyspace)
219
+ nil
205
220
  end
206
221
  end
207
222
 
223
+ def create_idx_expr(c_name, value, op)
224
+ {:column_name => c_name, :value => value, :comparison => op}
225
+ end
226
+
227
+ def create_idx_clause(idx_expressions, start = "")
228
+ {:start => start, :index_expressions => idx_expressions}
229
+ end
230
+
231
+ def get_indexed_slices(column_family, idx_clause, *columns_and_options)
232
+ column_family, columns, _, options =
233
+ extract_and_validate_params_for_real(column_family, [], columns_and_options, READ_DEFAULTS)
234
+
235
+ ret = {}
236
+ cf(column_family).each do |key, row|
237
+ next if idx_clause[:start] != '' && key < idx_clause[:start]
238
+
239
+ matches = []
240
+ idx_clause[:index_expressions].each do |expr|
241
+ next if row[expr[:column_name]].nil?
242
+ next unless row[expr[:column_name]].send(expr[:comparison].to_sym, expr[:value])
243
+
244
+ matches << expr
245
+ end
246
+
247
+ ret[key] = row if matches.length == idx_clause[:index_expressions].length
248
+ end
249
+
250
+ ret
251
+ end
252
+
253
+ def schema(load=true)
254
+ @schema
255
+ end
256
+
257
+ def column_family_property(column_family, key)
258
+ schema[column_family.to_s][key]
259
+ end
260
+
208
261
  private
209
262
 
263
+ def schema_for_keyspace(keyspace)
264
+ @schema
265
+ end
266
+
210
267
  def _get_range(column_family, start, finish, count)
211
268
  ret = OrderedHash.new
212
269
  start = to_compare_with_type(start, column_family)
@@ -220,30 +277,6 @@ class Cassandra
220
277
  ret
221
278
  end
222
279
 
223
- def schema_for_keyspace(keyspace)
224
- doc = read_storage_xml
225
- ret = {}
226
- doc.css("Keyspaces Keyspace[@Name='#{keyspace}']").css('ColumnFamily').each do |cf|
227
- ret[cf['Name']] = {}
228
- if cf['CompareSubcolumnsWith']
229
- ret[cf['Name']]['CompareSubcolumnsWith'] = 'org.apache.cassandra.db.marshal.' + cf['CompareSubcolumnsWith']
230
- end
231
- if cf['CompareWith']
232
- ret[cf['Name']]['CompareWith'] = 'org.apache.cassandra.db.marshal.' + cf['CompareWith']
233
- end
234
- if cf['ColumnType'] == 'Super'
235
- ret[cf['Name']]['Type'] = 'Super'
236
- else
237
- ret[cf['Name']]['Type'] = 'Standard'
238
- end
239
- end
240
- ret
241
- end
242
-
243
- def read_storage_xml
244
- @doc ||= Nokogiri::XML(open(@storage_xml))
245
- end
246
-
247
280
  def extract_and_validate_params_for_real(column_family, keys, args, options)
248
281
  column_family, columns, sub_column, options = extract_and_validate_params(column_family, keys, args, options)
249
282
  options[:start] = nil if options[:start] == ''
@@ -264,25 +297,12 @@ class Cassandra
264
297
  def to_compare_with_type(column_name, column_family, standard=true)
265
298
  return column_name if column_name.nil?
266
299
  klass = if standard
267
- schema[column_family.to_s]["CompareWith"]
300
+ column_name_class(column_family)
268
301
  else
269
- schema[column_family.to_s]["CompareSubcolumnsWith"]
302
+ sub_column_name_class(column_family)
270
303
  end
271
304
 
272
- case klass
273
- when "org.apache.cassandra.db.marshal.UTF8Type", "org.apache.cassandra.db.marshal.BytesType"
274
- column_name
275
- when "org.apache.cassandra.db.marshal.TimeUUIDType"
276
- SimpleUUID::UUID.new(column_name)
277
- when "org.apache.cassandra.db.marshal.LongType"
278
- Long.new(column_name)
279
- else
280
- raise "Unknown column family type: #{klass.inspect}"
281
- end
282
- end
283
-
284
- def column_family_type(column_family)
285
- schema[column_family.to_s]['Type']
305
+ klass.new(column_name)
286
306
  end
287
307
 
288
308
  def cf(column_family)
@@ -293,9 +313,32 @@ class Cassandra
293
313
  if new_stuff.is_a?(Array)
294
314
  new_stuff = new_stuff.inject({}){|h,k| h[k] = nil; h }
295
315
  end
316
+
317
+ new_stuff = new_stuff.to_a.inject({}){|h,k| h[k[0].to_s] = k[1]; h }
318
+
296
319
  OrderedHash[old_stuff.merge(new_stuff).sort{|a,b| a[0] <=> b[0]}]
297
320
  end
298
321
 
322
+ def columns_to_hash(column_family, columns)
323
+ column_class, sub_column_class = column_name_class(column_family), sub_column_name_class(column_family)
324
+ output = OrderedHash.new
325
+
326
+ columns.each do |column_name, value|
327
+ column = column_class.new(column_name)
328
+
329
+ if [Hash, OrderedHash].include?(value.class)
330
+ output[column] ||= OrderedHash.new
331
+ value.each do |sub_column, sub_column_value|
332
+ output[column][sub_column_class.new(sub_column)] = sub_column_value
333
+ end
334
+ else
335
+ output[column_class.new(column_name)] = value
336
+ end
337
+ end
338
+
339
+ output
340
+ end
341
+
299
342
  def apply_count(row, count, reversed=false)
300
343
  if count
301
344
  keys = row.keys.sort