cassilds 0.9.2 → 0.12.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.
Files changed (67) hide show
  1. data/CHANGELOG +51 -1
  2. data/LICENSE +0 -0
  3. data/Manifest +25 -7
  4. data/README.md +352 -0
  5. data/Rakefile +169 -1
  6. data/cassilds.gemspec +45 -0
  7. data/conf/{cassandra.in.sh → 0.6/cassandra.in.sh} +0 -0
  8. data/conf/{log4j.properties → 0.6/log4j.properties} +0 -0
  9. data/conf/0.6/schema.json +57 -0
  10. data/conf/{storage-conf.xml → 0.6/storage-conf.xml} +15 -5
  11. data/conf/0.7/cassandra.in.sh +46 -0
  12. data/conf/0.7/cassandra.yaml +336 -0
  13. data/conf/0.7/log4j-server.properties +41 -0
  14. data/conf/0.7/schema.json +57 -0
  15. data/conf/0.7/schema.txt +45 -0
  16. data/conf/0.8/cassandra.in.sh +41 -0
  17. data/conf/0.8/cassandra.yaml +61 -0
  18. data/conf/0.8/log4j-server.properties +40 -0
  19. data/conf/0.8/schema.json +66 -0
  20. data/conf/0.8/schema.txt +51 -0
  21. data/lib/cassandra/0.6/cassandra.rb +58 -13
  22. data/lib/cassandra/0.6/columns.rb +43 -0
  23. data/lib/cassandra/0.6/protocol.rb +16 -18
  24. data/lib/cassandra/0.6.rb +0 -0
  25. data/lib/cassandra/0.7/cassandra.rb +0 -270
  26. data/lib/cassandra/0.7/columns.rb +1 -64
  27. data/lib/cassandra/0.7/protocol.rb +0 -134
  28. data/lib/cassandra/0.7.rb +0 -0
  29. data/lib/cassandra/0.8/cassandra.rb +10 -0
  30. data/lib/cassandra/0.8/columns.rb +4 -0
  31. data/lib/cassandra/0.8/protocol.rb +23 -0
  32. data/lib/cassandra/0.8.rb +7 -0
  33. data/lib/cassandra/array.rb +0 -0
  34. data/lib/cassandra/cassandra.rb +877 -111
  35. data/lib/cassandra/{0.7/column_family.rb → column_family.rb} +0 -0
  36. data/lib/cassandra/columns.rb +72 -6
  37. data/lib/cassandra/comparable.rb +0 -0
  38. data/lib/cassandra/constants.rb +0 -0
  39. data/lib/cassandra/debug.rb +0 -0
  40. data/lib/cassandra/helpers.rb +1 -0
  41. data/lib/cassandra/{0.7/keyspace.rb → keyspace.rb} +0 -0
  42. data/lib/cassandra/long.rb +5 -0
  43. data/lib/cassandra/mock.rb +259 -85
  44. data/lib/cassandra/ordered_hash.rb +10 -18
  45. data/lib/cassandra/protocol.rb +120 -0
  46. data/lib/cassandra/time.rb +0 -0
  47. data/lib/cassandra.rb +6 -7
  48. data/test/cassandra_client_test.rb +0 -0
  49. data/test/cassandra_mock_test.rb +52 -28
  50. data/test/cassandra_test.rb +465 -44
  51. data/test/comparable_types_test.rb +0 -0
  52. data/test/eventmachine_test.rb +30 -30
  53. data/test/ordered_hash_test.rb +6 -0
  54. data/test/test_helper.rb +3 -2
  55. data/vendor/0.6/gen-rb/cassandra.rb +0 -0
  56. data/vendor/0.6/gen-rb/cassandra_constants.rb +0 -0
  57. data/vendor/0.6/gen-rb/cassandra_types.rb +0 -0
  58. data/vendor/0.7/gen-rb/cassandra.rb +0 -0
  59. data/vendor/0.7/gen-rb/cassandra_constants.rb +0 -0
  60. data/vendor/0.7/gen-rb/cassandra_types.rb +4 -2
  61. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  62. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  63. data/vendor/0.8/gen-rb/cassandra_types.rb +816 -0
  64. metadata +50 -27
  65. data/README.rdoc +0 -83
  66. data/cassandra.gemspec +0 -46
  67. data/conf/cassandra.yaml +0 -113
@@ -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 => 0.1, :exception_classes => [])
8
8
  @twitter.clear_keyspace!
9
9
 
10
- @blogs = Cassandra.new('Multiblog')
10
+ @blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
11
11
  @blogs.clear_keyspace!
12
12
 
13
- @blogs_long = Cassandra.new('MultiblogLong')
13
+ @blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.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 => 0.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', '',]
@@ -43,7 +73,7 @@ class CassandraTest < Test::Unit::TestCase
43
73
  hash = OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
44
74
  @twitter.insert(:Users, key, hash)
45
75
  assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
46
- assert_equal(hash.timestamps.keys.sort, @twitter.get(:Users, key).timestamps.keys.sort)
76
+ assert_equal(hash.timestamps.keys.sort, @twitter.get(:Users, key).timestamps.keys)
47
77
  assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
48
78
  end
49
79
 
@@ -102,9 +132,39 @@ class CassandraTest < Test::Unit::TestCase
102
132
  assert !@twitter.exists?(:Statuses, 'bogus', 'body')
103
133
  end
104
134
 
105
- def test_exists_with_only_key
135
+ def test_get_value_with_range
136
+ k = key
137
+
138
+ 10.times do |i|
139
+ @twitter.insert(:Statuses, k, {"body-#{i}" => 'v'})
140
+ end
141
+
142
+ assert_equal 5, @twitter.get(:Statuses, k, :count => 5).length
143
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-5").length
144
+ assert_equal 5, @twitter.get(:Statuses, k, :finish => "body-4").length
145
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-1", :count => 5).length
146
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-0", :finish => "body-4", :count => 7).length
147
+ end
148
+
149
+ def test_exists
106
150
  @twitter.insert(:Statuses, key, {'body' => 'v'})
107
- assert @twitter.exists?(:Statuses, key)
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])
108
168
  end
109
169
 
110
170
  def test_get_super_key
@@ -116,9 +176,11 @@ class CassandraTest < Test::Unit::TestCase
116
176
  end
117
177
 
118
178
  def test_get_several_super_keys
119
- columns = {
120
- 'user_timelines' => {@uuids[1] => 'v1'},
121
- 'mentions_timelines' => {@uuids[2] => 'v2'}}
179
+ columns = OrderedHash[
180
+ 'mentions_timelines', {@uuids[2] => 'v2'},
181
+ 'user_timelines', {@uuids[1] => 'v1'}
182
+ ]
183
+
122
184
  @twitter.insert(:StatusRelationships, key, columns)
123
185
 
124
186
  assert_equal(columns, @twitter.get(:StatusRelationships, key))
@@ -165,7 +227,7 @@ class CassandraTest < Test::Unit::TestCase
165
227
  assert_equal(columns.keys.sort, @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps.keys.sort)
166
228
  assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
167
229
  # FIXME Not sure if this is valid
168
- # assert_nil @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
230
+ assert_equal false, @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
169
231
  end
170
232
 
171
233
  def test_get_super_value
@@ -175,24 +237,129 @@ class CassandraTest < Test::Unit::TestCase
175
237
  assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
176
238
  end
177
239
 
240
+ # def test_get_range_with_key_range
241
+ # skip('This test requires the use of OrderPreservingPartitioner on the cluster to work properly.')
242
+ # k = key
243
+ # @twitter.insert(:Statuses, k + '2', {'body' => '1'})
244
+ # @twitter.insert(:Statuses, k + '3', {'body' => '1'})
245
+ # @twitter.insert(:Statuses, k + '4', {'body' => '1'})
246
+ # @twitter.insert(:Statuses, k + '5', {'body' => '1'})
247
+ # @twitter.insert(:Statuses, k + '6', {'body' => '1'})
248
+ # assert_equal([k + '3', k + '4', k + '5'], @twitter.get_range(:Statuses, :start_key => k + '3', :finish_key => k + '5').keys)
249
+ # end
250
+
251
+ def test_get_range
252
+ # make sure that deleted rows are not included in the iteration
253
+ 10.times do |i|
254
+ @twitter.insert(:Statuses, i.to_s, {'body' => '1'})
255
+ @twitter.insert(:Statuses, i.to_s + '_delete_me', {'test' => 'value'})
256
+ @twitter.remove(:Statuses, i.to_s + '_delete_me')
257
+ end
178
258
 
179
- #TODO: add a OPP keyspace for this
180
- # def test_get_range
181
- # @twitter.insert(:Statuses, '2', {'body' => '1'})
182
- # @twitter.insert(:Statuses, '3', {'body' => '1'})
183
- # @twitter.insert(:Statuses, '4', {'body' => '1'})
184
- # @twitter.insert(:Statuses, '5', {'body' => '1'})
185
- # @twitter.insert(:Statuses, '6', {'body' => '1'})
186
- # assert_equal(['3', '4', '5'], @twitter.get_range(:Statuses, :start => '3', :finish => '5'))
187
- # end
259
+ assert_equal(4, @twitter.get_range_keys(:Statuses, :key_count => 4).size)
260
+ end
261
+
262
+ def test_get_range_with_count
263
+ @twitter.insert(:Statuses, key + '1', {'test_column1' => '1', 'test_column2' => '2', 'test_column3' => '2', 'deleted_column' => '1'})
264
+ @twitter.insert(:Statuses, key + '2', {'test_column4' => '3', 'test_column5' => '4', 'test_column6' => '2', 'deleted_column' => '2'})
265
+
266
+ @twitter.get_range(:Statuses, :count => 3) do |key, columns|
267
+ assert_equal columns.count, 3
268
+ end
269
+
270
+ assert_equal 2, @twitter.get_range(:Statuses, :start_key => key + '1', :finish_key => key + '1', :count => 2)[key + '1'].count
271
+
272
+ @twitter.remove(:Statuses, key + '1', 'deleted_column')
273
+ @twitter.remove(:Statuses, key + '2', 'deleted_column')
274
+
275
+ @twitter.get_range(:Statuses, :count => 2) do |key, columns|
276
+ assert_equal columns.count, 2
277
+ end
188
278
 
189
- def test_get_range_count
190
- @twitter.insert(:Statuses, '2', {'body' => '1'})
191
- @twitter.insert(:Statuses, '3', {'body' => '1'})
192
- @twitter.insert(:Statuses, '4', {'body' => '1'})
193
- @twitter.insert(:Statuses, '5', {'body' => '1'})
194
- @twitter.insert(:Statuses, '6', {'body' => '1'})
195
- assert_equal(3, @twitter.get_range(:Statuses, :count => 3).size)
279
+ end
280
+
281
+ def test_get_range_block
282
+ k = key
283
+ 5.times do |i|
284
+ @twitter.insert(:Statuses, k+i.to_s, {"body-#{i.to_s}" => 'v'})
285
+ end
286
+
287
+ values = (0..4).collect{|n| { :key => "test_get_range_block#{n}", :columns => { "body-#{n}" => "v" }} }.reverse
288
+
289
+ returned_value = @twitter.get_range(:Statuses, :start_key => k.to_s, :key_count => 5) do |key,columns|
290
+ expected = values.pop
291
+ assert_equal expected[:key], key
292
+ assert_equal expected[:columns], columns
293
+ end
294
+
295
+ assert_equal [], values
296
+ assert_nil returned_value
297
+ end
298
+
299
+ def test_get_range_reversed
300
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
301
+ hash = Cassandra::OrderedHash[data]
302
+ reversed_hash = Cassandra::OrderedHash[data.reverse]
303
+
304
+ @twitter.insert(:Statuses, "all-keys", hash)
305
+
306
+ columns = @twitter.get_range(:Statuses, :reversed => true)["all-keys"]
307
+ columns.each do |column|
308
+ assert_equal reversed_hash.shift, column
309
+ end
310
+ end
311
+
312
+ def test_each_key
313
+ k = key
314
+ keys_yielded = []
315
+
316
+ 10.times do |i|
317
+ @twitter.insert(:Statuses, k + i.to_s, {"body-#{i.to_s}" => 'v'})
318
+ end
319
+
320
+ # make sure that deleted rows are not included in the iteration
321
+ @twitter.insert(:Statuses, k + '_delete_me', {'test' => 'value'})
322
+ @twitter.remove(:Statuses, k + '_delete_me')
323
+
324
+ @twitter.each_key(:Statuses) do |key|
325
+ keys_yielded << key
326
+ end
327
+
328
+ assert_equal 10, keys_yielded.length
329
+ end
330
+
331
+ def test_each
332
+ k = key
333
+ key_columns = {}
334
+
335
+ 10.times do |i|
336
+ key_columns[k + i.to_s] = {"body-#{i.to_s}" => 'v', 'single_column_lookup' => "value = #{i.to_s}"}
337
+ @twitter.insert(:Statuses, k + i.to_s, key_columns[k + i.to_s])
338
+ end
339
+
340
+ keys_yielded = []
341
+ @twitter.each(:Statuses, :batch_size => 5) do |key, columns|
342
+ assert_equal key_columns[key], columns
343
+ keys_yielded << key
344
+ end
345
+
346
+ assert_equal 10, keys_yielded.length
347
+
348
+ keys_yielded = []
349
+ @twitter.each(:Statuses, :key_count => 7, :batch_size => 5) do |key, columns|
350
+ assert_equal key_columns[key], columns
351
+ keys_yielded << key
352
+ end
353
+
354
+ assert_equal 7, keys_yielded.length, 'each limits to specified count'
355
+
356
+ keys_yielded = []
357
+ @twitter.each(:Statuses, :columns => ['single_column_lookup'], :batch_size => 5) do |key, columns|
358
+ assert_equal key_columns[key].reject {|k2,v| k2 != 'single_column_lookup'}, columns
359
+ keys_yielded << key
360
+ end
361
+
362
+ assert_equal 10, keys_yielded.length
196
363
  end
197
364
 
198
365
  def test_multi_get
@@ -220,6 +387,13 @@ class CassandraTest < Test::Unit::TestCase
220
387
  assert_equal({}, @twitter.get(:Statuses, key))
221
388
  end
222
389
 
390
+ def test_remove_super_sub_key_errors_for_normal_column_family
391
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
392
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
393
+
394
+ assert_raise( ArgumentError) { @twitter.remove(:Statuses, key, 'body' , 'subcolumn') }
395
+ end
396
+
223
397
  def test_remove_value
224
398
  @twitter.insert(:Statuses, key, {'body' => 'v'})
225
399
  @twitter.remove(:Statuses, key, 'body')
@@ -240,11 +414,12 @@ class CassandraTest < Test::Unit::TestCase
240
414
  end
241
415
 
242
416
  def test_remove_super_value
243
- columns = {@uuids[1] => 'v1'}
417
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
418
+ column_name_to_remove = @uuids[2]
244
419
  @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
245
- @twitter.remove(:StatusRelationships, key, 'user_timelines', columns.keys.first)
246
- assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first)
247
- assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps[columns.keys.first]
420
+ @twitter.remove(:StatusRelationships, key, 'user_timelines', column_name_to_remove)
421
+ assert_equal( columns.reject{|k,v| k == column_name_to_remove}, @twitter.get(:StatusRelationships, key, 'user_timelines') )
422
+ assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps[column_name_to_remove]
248
423
  end
249
424
 
250
425
  def test_clear_column_family
@@ -306,15 +481,21 @@ class CassandraTest < Test::Unit::TestCase
306
481
  end
307
482
 
308
483
  def test_count_keys
309
- @twitter.insert(:Statuses, key + "1", {'body' => '1'})
310
- @twitter.insert(:Statuses, key + "2", {'body' => '2'})
311
- @twitter.insert(:Statuses, key + "3", {'body' => '3'})
484
+ k = key
485
+ @twitter.insert(:Statuses, k + "1", {'body' => '1'})
486
+ @twitter.insert(:Statuses, k + "2", {'body' => '2'})
487
+ @twitter.insert(:Statuses, k + "3", {'body' => '3'})
312
488
  assert_equal 3, @twitter.count_range(:Statuses)
313
489
  end
314
490
 
315
491
  def test_count_columns
316
- @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
317
- assert_equal 2, @twitter.count_columns(:Statuses, key)
492
+ columns = (1..200).inject(Hash.new){|h,v| h['column' + v.to_s] = v.to_s; h;}
493
+
494
+ @twitter.insert(:Statuses, key, columns)
495
+ assert_equal 200, @twitter.count_columns(:Statuses, key, :count => 200)
496
+ assert_equal 100, @twitter.count_columns(:Statuses, key)
497
+ assert_equal 55, @twitter.count_columns(:Statuses, key, :count => 55)
498
+
318
499
  end
319
500
 
320
501
  def test_count_super_columns
@@ -343,24 +524,45 @@ class CassandraTest < Test::Unit::TestCase
343
524
  def test_batch_mutate
344
525
  k = key
345
526
 
527
+ @twitter.insert(:Users, k + '0', {'delete_me' => 'v0', 'keep_me' => 'v0'})
346
528
  @twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
529
+ initial_subcolumns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
530
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => initial_subcolumns, 'dummy_supercolumn' => {@uuids[5] => 'value'}})
531
+ assert_equal(initial_subcolumns, @twitter.get(:StatusRelationships, k, 'user_timelines'))
532
+ assert_equal({@uuids[5] => 'value'}, @twitter.get(:StatusRelationships, k, 'dummy_supercolumn'))
533
+ new_subcolumns = {@uuids[3] => 'v3', @uuids[4] => 'v4'}
534
+ subcolumn_to_delete = initial_subcolumns.keys.first # the first column of the initial set
347
535
 
348
536
  @twitter.batch do
537
+ # Normal Columns
349
538
  @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
350
539
  @twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
351
540
  @twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
352
541
  @twitter.insert(:Statuses, k + '3', {'body' => 'v'})
353
542
 
543
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Written
354
544
  assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
355
545
  assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
356
546
  assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
357
547
 
358
- @twitter.remove(:Users, k + '1')
548
+ @twitter.remove(:Users, k + '1') # Full row
359
549
  assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
360
550
 
551
+ @twitter.remove(:Users, k + '0', 'delete_me') # A single column of the row
552
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Not yet removed
553
+
361
554
  @twitter.remove(:Users, k + '4')
362
555
  @twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
363
556
  assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
557
+
558
+ # SuperColumns
559
+ # Add and delete new sub columns to the user timeline supercolumn
560
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => new_subcolumns })
561
+ @twitter.remove(:StatusRelationships, k, 'user_timelines' , subcolumn_to_delete ) # Delete the first of the initial_subcolumns from the user_timeline supercolumn
562
+ assert_equal(initial_subcolumns, @twitter.get(:StatusRelationships, k, 'user_timelines')) # No additions or deletes reflected yet
563
+ # Delete a complete supercolumn
564
+ @twitter.remove(:StatusRelationships, k, 'dummy_supercolumn' ) # Delete the full dummy supercolumn
565
+ assert_equal({@uuids[5] => 'value'}, @twitter.get(:StatusRelationships, k, 'dummy_supercolumn')) # dummy supercolumn not yet deleted
364
566
  end
365
567
 
366
568
  assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
@@ -368,11 +570,20 @@ class CassandraTest < Test::Unit::TestCase
368
570
  assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
369
571
  assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
370
572
  assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
573
+
574
+ assert_equal({ 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # 'delete_me' column removed
575
+
371
576
 
372
577
  assert_equal({'body' => 'v2', 'user' => 'v2'}.keys.sort, @twitter.get(:Users, k + '2').timestamps.keys.sort) # Written
373
578
  assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}.keys.sort, @twitter.get(:Users, k + '3').timestamps.keys.sort) # Written and compacted
374
579
  assert_equal({'body' => 'v4', 'user' => 'v4'}.keys.sort, @twitter.get(:Users, k + '4').timestamps.keys.sort) # Written
375
580
  assert_equal({'body' => 'v'}.keys.sort, @twitter.get(:Statuses, k + '3').timestamps.keys.sort) # Written
581
+
582
+ # Final result: initial_subcolumns - initial_subcolumns.first + new_subcolumns
583
+ resulting_subcolumns = initial_subcolumns.merge(new_subcolumns).reject{|k2,v| k2 == subcolumn_to_delete }
584
+ assert_equal(resulting_subcolumns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
585
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'dummy_supercolumn')) # dummy supercolumn deleted
586
+
376
587
  end
377
588
 
378
589
  def test_complain_about_nil_key
@@ -381,15 +592,8 @@ class CassandraTest < Test::Unit::TestCase
381
592
  end
382
593
  end
383
594
 
384
- def test_raise_access_error_on_nonexistent_keyspace
385
- nonexistent = Cassandra.new('Nonexistent')
386
- assert_raises(Cassandra::AccessError) do
387
- nonexistent.get "foo", "bar"
388
- end
389
- end
390
-
391
595
  def test_nil_sub_column_value
392
- @twitter.insert(:Index, 'asdf', {"thing" => {'jkl' => ''} })
596
+ @twitter.insert(:Indexes, 'asdf', {"thing" => {'jkl' => ''} })
393
597
  end
394
598
 
395
599
  def test_disconnect!
@@ -397,6 +601,13 @@ class CassandraTest < Test::Unit::TestCase
397
601
  assert_nil @twitter.instance_variable_get(:@client)
398
602
  end
399
603
 
604
+ def test_disconnect_when_not_connected!
605
+ assert_nothing_raised do
606
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
607
+ @twitter.disconnect!
608
+ end
609
+ end
610
+
400
611
  def test_super_allows_for_non_string_values_while_normal_does_not
401
612
  columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
402
613
 
@@ -404,6 +615,216 @@ class CassandraTest < Test::Unit::TestCase
404
615
  @twitter.insert(:Statuses, key, { 'body' => '1' })
405
616
  end
406
617
 
618
+ def test_batch_over_deletes
619
+ k = key
620
+
621
+ @twitter.batch do
622
+ @twitter.insert(:Users, k, {'body' => 'body', 'user' => 'user'})
623
+ @twitter.remove(:Users, k, 'body')
624
+ end
625
+
626
+ assert_equal({'user' => 'user'}, @twitter.get(:Users, k))
627
+ end
628
+
629
+ def test_each_key
630
+ num_users = rand(60)
631
+ num_users.times do |twit_counter|
632
+ @twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
633
+ end
634
+ counter = 0
635
+ @twitter.each_key(:Users) do |_, _|
636
+ counter += 1
637
+ end
638
+ assert_equal num_users, counter
639
+ end
640
+
641
+ def test_each_with_column_predicate
642
+ num_users = rand(60)
643
+ num_users.times do |twit_counter|
644
+ @twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
645
+ end
646
+ counter = 0
647
+ @twitter.each(:Users, :batch_size => 10, :start => 'body', :finish => 'body') do |key, columns|
648
+ assert_equal 1, columns.length
649
+ counter += 1
650
+ end
651
+ assert_equal num_users, counter
652
+ end
653
+
654
+ def test_each_with_super_column
655
+ num_users = rand(50)
656
+ block_name = key
657
+ num_users.times do |twit_counter|
658
+ @twitter.insert(:StatusRelationships, block_name + twit_counter.to_s, {
659
+ 'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'},
660
+ 'mentions_timelines' => {@uuids[3] => 'v3'}})
661
+ end
662
+
663
+ counter = 0
664
+ # Restrict to one super column ::
665
+ @twitter.each(:StatusRelationships, :batch_size => 10, :start => 'user_timelines', :finish => 'user_timelines') do |key, columns|
666
+ columns.each do |_, column_value|
667
+ assert_equal 2, column_value.length
668
+ end
669
+ counter += 1
670
+ end
671
+
672
+ #Both super columns
673
+ @twitter.each(:StatusRelationships, :batch_size => 10, :start => 'mentions_timelines', :finish => 'user_timelines') do |key,columns|
674
+ assert_equal 2, columns.length
675
+ counter += 1
676
+ end
677
+
678
+ assert_equal num_users*2, counter
679
+
680
+ end
681
+
682
+ def test_each_column_types
683
+ num_users = rand(60)
684
+ num_users.times do |twit_counter|
685
+ @type_conversions.insert(:UUIDColumnConversion, twit_counter.to_s, {@uuids[1] => 'v1'})
686
+ end
687
+ counter = 0
688
+ @type_conversions.each(:UUIDColumnConversion) do |_, columns|
689
+ counter += 1
690
+ columns.keys.each {|column_name| assert_equal SimpleUUID::UUID, column_name.class}
691
+ end
692
+ assert_equal num_users, counter
693
+ end
694
+
695
+
696
+ if CASSANDRA_VERSION.to_f >= 0.7
697
+ def test_creating_and_dropping_new_index
698
+ @twitter.create_index('Twitter', 'Statuses', 'column_name', 'LongType')
699
+ assert_nil @twitter.create_index('Twitter', 'Statuses', 'column_name', 'LongType')
700
+
701
+ @twitter.drop_index('Twitter', 'Statuses', 'column_name')
702
+ assert_nil @twitter.drop_index('Twitter', 'Statuses', 'column_name')
703
+
704
+ # Recreating and redropping the same index should not error either.
705
+ @twitter.create_index('Twitter', 'Statuses', 'column_name', 'LongType')
706
+ @twitter.drop_index('Twitter', 'Statuses', 'column_name')
707
+ end
708
+
709
+ def test_get_indexed_slices
710
+ @twitter.create_index('Twitter', 'Statuses', 'x', 'LongType')
711
+
712
+ @twitter.insert(:Statuses, 'row1', { 'x' => [0,10].pack("NN") })
713
+
714
+ (2..10).to_a.each do |i|
715
+ @twitter.insert(:Statuses, 'row' + i.to_s, { 'x' => [0,20].pack("NN"), 'non_indexed' => [i].pack('N*') })
716
+ end
717
+
718
+ @twitter.insert(:Statuses, 'row11', { 'x' => [0,30].pack("NN") })
719
+
720
+ expressions = [{:column_name => 'x', :value => [0,20].pack("NN"), :comparison => "=="}]
721
+
722
+ # verify multiples will be returned
723
+ assert_equal 9, @twitter.get_indexed_slices(:Statuses, expressions).length
724
+
725
+ # verify that GT and LT queries perform properly
726
+ expressions = [
727
+ {:column_name => 'x', :value => [0,20].pack("NN"), :comparison => "=="},
728
+ {:column_name => 'non_indexed', :value => [5].pack("N*"), :comparison => ">"}
729
+ ]
730
+ assert_equal(5, @twitter.get_indexed_slices(:Statuses, expressions).length)
731
+ end
732
+
733
+ def test_old_get_indexed_slices
734
+ @twitter.create_index('Twitter', 'Statuses', 'x', 'LongType')
735
+
736
+ @twitter.insert(:Statuses, 'row1', { 'x' => [0,10].pack("NN") })
737
+
738
+ (2..10).to_a.each do |i|
739
+ @twitter.insert(:Statuses, 'row' + i.to_s, { 'x' => [0,20].pack("NN"), 'non_indexed' => [i].pack('N*') })
740
+ end
741
+
742
+ @twitter.insert(:Statuses, 'row11', { 'x' => [0,30].pack("NN") })
743
+
744
+ idx_expr = @twitter.create_idx_expr('x', [0,20].pack("NN"), "==")
745
+
746
+ # verify count is observed
747
+ idx_clause = @twitter.create_idx_clause([idx_expr], "", 1)
748
+ assert_equal 1, @twitter.get_indexed_slices(:Statuses, idx_clause).length
749
+
750
+ # verify multiples will be returned
751
+ idx_clause = @twitter.create_idx_clause([idx_expr])
752
+ assert_equal 9, @twitter.get_indexed_slices(:Statuses, idx_clause).length
753
+
754
+ # verify that GT and LT queries perform properly
755
+ idx_expr = [
756
+ @twitter.create_idx_expr('x', [0,20].pack("NN"), "=="),
757
+ @twitter.create_idx_expr('non_indexed', [5].pack("N*"), ">")
758
+ ]
759
+ idx_clause = @twitter.create_idx_clause(idx_expr)
760
+ assert_equal(5, @twitter.get_indexed_slices(:Statuses, idx_clause).length)
761
+ end
762
+
763
+ def test_column_family_mutation
764
+ k = key
765
+
766
+ if @twitter.column_families.include?(k)
767
+ @twitter.drop_column_family(k)
768
+ end
769
+
770
+ # Verify add_column_family works as desired.
771
+ @twitter.add_column_family(
772
+ Cassandra::ColumnFamily.new(
773
+ :keyspace => 'Twitter',
774
+ :name => k
775
+ )
776
+ )
777
+ assert @twitter.column_families.include?(k)
778
+
779
+ if CASSANDRA_VERSION.to_f == 0.7
780
+ # Verify rename_column_family works properly
781
+ @twitter.rename_column_family(k, k + '_renamed')
782
+ assert @twitter.column_families.include?(k + '_renamed')
783
+
784
+ # Change it back and validate
785
+ @twitter.rename_column_family(k + '_renamed', k)
786
+ assert @twitter.column_families.include?(k)
787
+ end
788
+
789
+ temp_cf_def = @twitter.column_families[k]
790
+ temp_cf_def.comment = k
791
+ @twitter.update_column_family(temp_cf_def)
792
+ assert @twitter.column_families.include?(k)
793
+
794
+ @twitter.drop_column_family(k)
795
+ assert !@twitter.column_families.include?(k)
796
+ end
797
+ end
798
+
799
+ if CASSANDRA_VERSION.to_f >= 0.8
800
+ def test_adding_getting_value_in_counter
801
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
802
+ assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
803
+ assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
804
+ end
805
+
806
+ def test_get_counter_slice
807
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
808
+ assert_equal({'tweet_count' => 5}, @twitter.get(:UserCounters, 'bob', :start => "tweet_count", :finish => "tweet_count"))
809
+ end
810
+
811
+ def test_adding_getting_value_in_multiple_counters
812
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
813
+ assert_nil @twitter.add(:UserCounters, 'bob', 7, 'follower_count')
814
+ assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
815
+ assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
816
+ assert_equal([5, 7], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count']))
817
+ assert_equal([5, 7, nil], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count', 'bogus']))
818
+ end
819
+
820
+ def test_adding_getting_value_in_multiple_counters_with_super_columns
821
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 1, 'DAU', 'today')
822
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 2, 'DAU', 'tomorrow')
823
+ assert_equal(1, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'today'))
824
+ assert_equal(2, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'tomorrow'))
825
+ end
826
+ end
827
+
407
828
  private
408
829
 
409
830
  def key
File without changes