mcmire-cassandra 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG +108 -0
  2. data/LICENSE +202 -0
  3. data/Manifest +63 -0
  4. data/README.md +352 -0
  5. data/Rakefile +169 -0
  6. data/bin/cassandra_helper +16 -0
  7. data/conf/0.6/cassandra.in.sh +47 -0
  8. data/conf/0.6/log4j.properties +38 -0
  9. data/conf/0.6/schema.json +57 -0
  10. data/conf/0.6/storage-conf.xml +352 -0
  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 +113 -0
  22. data/lib/cassandra/0.6/columns.rb +78 -0
  23. data/lib/cassandra/0.6/protocol.rb +90 -0
  24. data/lib/cassandra/0.6.rb +7 -0
  25. data/lib/cassandra/0.7/cassandra.rb +2 -0
  26. data/lib/cassandra/0.7/columns.rb +4 -0
  27. data/lib/cassandra/0.7/protocol.rb +5 -0
  28. data/lib/cassandra/0.7.rb +7 -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 +21 -0
  32. data/lib/cassandra/0.8.rb +7 -0
  33. data/lib/cassandra/array.rb +8 -0
  34. data/lib/cassandra/cassandra.rb +1070 -0
  35. data/lib/cassandra/column_family.rb +3 -0
  36. data/lib/cassandra/columns.rb +144 -0
  37. data/lib/cassandra/comparable.rb +28 -0
  38. data/lib/cassandra/constants.rb +11 -0
  39. data/lib/cassandra/debug.rb +9 -0
  40. data/lib/cassandra/helpers.rb +41 -0
  41. data/lib/cassandra/keyspace.rb +3 -0
  42. data/lib/cassandra/long.rb +58 -0
  43. data/lib/cassandra/mock.rb +511 -0
  44. data/lib/cassandra/ordered_hash.rb +192 -0
  45. data/lib/cassandra/protocol.rb +120 -0
  46. data/lib/cassandra/time.rb +11 -0
  47. data/lib/cassandra.rb +38 -0
  48. data/mcmire-cassandra.gemspec +43 -0
  49. data/test/cassandra_client_test.rb +20 -0
  50. data/test/cassandra_mock_test.rb +116 -0
  51. data/test/cassandra_test.rb +863 -0
  52. data/test/comparable_types_test.rb +45 -0
  53. data/test/eventmachine_test.rb +42 -0
  54. data/test/ordered_hash_test.rb +386 -0
  55. data/test/test_helper.rb +15 -0
  56. data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
  57. data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
  58. data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
  59. data/vendor/0.7/gen-rb/cassandra.rb +1936 -0
  60. data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
  61. data/vendor/0.7/gen-rb/cassandra_types.rb +681 -0
  62. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  63. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  64. data/vendor/0.8/gen-rb/cassandra_types.rb +824 -0
  65. metadata +200 -0
@@ -0,0 +1,66 @@
1
+ {"Twitter":{
2
+ "Users":{
3
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
4
+ "column_type":"Standard"},
5
+ "UserAudits":{
6
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
7
+ "column_type":"Standard"},
8
+ "UserCounters":{
9
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
10
+ "column_type":"Standard",
11
+ "default_validation_class":"CounterColumnType"},
12
+ "UserCounterAggregates":{
13
+ "subcomparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
14
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
15
+ "column_type":"Super",
16
+ "default_validation_class":"CounterColumnType"},
17
+ "UserRelationships":{
18
+ "subcomparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
19
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
20
+ "column_type":"Super"},
21
+ "Usernames":{
22
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
23
+ "column_type":"Standard"},
24
+ "Statuses":{
25
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
26
+ "column_type":"Standard"},
27
+ "StatusAudits":{
28
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
29
+ "column_type":"Standard"},
30
+ "StatusRelationships":{
31
+ "subcomparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
32
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
33
+ "column_type":"Super"},
34
+ "Indexes":{
35
+ "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
36
+ "column_type":"Super"},
37
+ "TimelinishThings":{
38
+ "comparator_type":"org.apache.cassandra.db.marshal.BytesType",
39
+ "column_type":"Standard"}
40
+ },
41
+ "Multiblog":{
42
+ "Blogs":{
43
+ "comparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
44
+ "column_type":"Standard"},
45
+ "Comments":{
46
+ "comparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
47
+ "column_type":"Standard"}
48
+ },
49
+ "MultiblogLong":{
50
+ "Blogs":{
51
+ "comparator_type":"org.apache.cassandra.db.marshal.LongType",
52
+ "column_type":"Standard"},
53
+ "Comments":{
54
+ "comparator_type":"org.apache.cassandra.db.marshal.LongType",
55
+ "column_type":"Standard"}
56
+ },
57
+ "TypeConversions":{
58
+ "UUIDColumnConversion":{
59
+ "comparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
60
+ "column_type":"Standard"},
61
+ "SuperUUID":{
62
+ "subcomparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
63
+ "comparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
64
+ "column_type":"Super"}
65
+ }
66
+ }
@@ -0,0 +1,51 @@
1
+ create keyspace Twitter with
2
+ placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' AND
3
+ strategy_options = [{replication_factor:1}];
4
+ use Twitter;
5
+ create column family Users with comparator = 'UTF8Type';
6
+ create column family UserAudits with comparator = 'UTF8Type';
7
+ create column family UserCounters with comparator = 'UTF8Type' and
8
+ default_validation_class = CounterColumnType;
9
+ create column family UserCounterAggregates with column_type = 'Super'
10
+ and comparator = 'UTF8Type' and
11
+ subcomparator = 'UTF8Type' and
12
+ default_validation_class = CounterColumnType;
13
+ create column family UserRelationships with
14
+ comparator = 'UTF8Type' and
15
+ column_type = 'Super' and
16
+ subcomparator = 'TimeUUIDType';
17
+ create column family Usernames with comparator = 'UTF8Type';
18
+ create column family Statuses with comparator = 'UTF8Type';
19
+ create column family StatusAudits with comparator = 'UTF8Type';
20
+ create column family StatusRelationships with
21
+ comparator = 'UTF8Type' and
22
+ column_type = 'Super' and
23
+ subcomparator = 'TimeUUIDType';
24
+ create column family Indexes with
25
+ comparator = 'UTF8Type' and
26
+ column_type = 'Super';
27
+ create column family TimelinishThings with
28
+ comparator = 'BytesType';
29
+
30
+ create keyspace Multiblog with
31
+ placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' AND
32
+ strategy_options = [{replication_factor:1}];
33
+ use Multiblog;
34
+ create column family Blogs with comparator = 'TimeUUIDType';
35
+ create column family Comments with comparator = 'TimeUUIDType';
36
+
37
+
38
+ create keyspace MultiblogLong with
39
+ placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' AND
40
+ strategy_options = [{replication_factor:1}];
41
+ use MultiblogLong;
42
+ create column family Blogs with comparator = 'LongType';
43
+ create column family Comments with comparator = 'LongType';
44
+
45
+ create keyspace TypeConversions with
46
+ placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy' AND
47
+ strategy_options = [{replication_factor:1}];
48
+ use TypeConversions;
49
+ create column family UUIDColumnConversion with comparator = TimeUUIDType;
50
+ create column family SuperUUID with comparator = TimeUUIDType and column_type = Super;
51
+
@@ -0,0 +1,113 @@
1
+ class Cassandra
2
+ def self.DEFAULT_TRANSPORT_WRAPPER
3
+ Thrift::BufferedTransport
4
+ end
5
+
6
+ ##
7
+ # Issues a login attempt using the username and password specified.
8
+ #
9
+ # * username
10
+ # * password
11
+ #
12
+ def login!(username, password)
13
+ @auth_request = CassandraThrift::AuthenticationRequest.new
14
+ @auth_request.credentials = {'username' => username, 'password' => password}
15
+ client.login(@keyspace, @auth_request)
16
+ end
17
+
18
+ def inspect
19
+ "#<Cassandra:#{object_id}, @keyspace=#{keyspace.inspect}, @schema={#{
20
+ schema(false).map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')
21
+ }}, @servers=#{servers.inspect}>"
22
+ end
23
+
24
+ ##
25
+ # Returns an array of available keyspaces.
26
+ #
27
+ def keyspaces
28
+ @keyspaces ||= client.describe_keyspaces()
29
+ end
30
+
31
+ ##
32
+ # Remove all rows in the column family you request.
33
+ #
34
+ # * column_family
35
+ # * options
36
+ # * consitency
37
+ # * timestamp
38
+ #
39
+ def clear_column_family!(column_family, options = {})
40
+ each_key(column_family) do |key|
41
+ remove(column_family, key, options)
42
+ end
43
+ end
44
+ alias truncate! clear_column_family!
45
+
46
+ # Remove all rows in the keyspace. Supports options <tt>:consistency</tt> and
47
+ # <tt>:timestamp</tt>.
48
+ # FIXME May not currently delete all records without multiple calls. Waiting
49
+ # for ranged remove support in Cassandra.
50
+ def clear_keyspace!(options = {})
51
+ schema.keys.each { |column_family| clear_column_family!(column_family, options) }
52
+ end
53
+
54
+ # Open a batch operation and yield self. Inserts and deletes will be queued
55
+ # until the block closes, and then sent atomically to the server. Supports
56
+ # the <tt>:consistency</tt> option, which overrides the consistency set in
57
+ # the individual commands.
58
+ def batch(options = {})
59
+ _, _, _, options =
60
+ extract_and_validate_params(schema.keys.first, "", [options], WRITE_DEFAULTS)
61
+
62
+ @batch = []
63
+ yield(self)
64
+ compacted_map,seen_clevels = compact_mutations!
65
+ clevel = if options[:consistency] != nil # Override any clevel from individual mutations if
66
+ options[:consistency]
67
+ elsif seen_clevels.length > 1 # Cannot choose which CLevel to use if there are several ones
68
+ raise "Multiple consistency levels used in the batch, and no override...cannot pick one"
69
+ else # if no consistency override has been provided but all the clevels in the batch are the same: use that one
70
+ seen_clevels.first
71
+ end
72
+
73
+ _mutate(compacted_map,clevel)
74
+ ensure
75
+ @batch = nil
76
+ end
77
+
78
+ protected
79
+
80
+ def schema(load=true)
81
+ if !load && !@schema
82
+ []
83
+ else
84
+ @schema ||= client.describe_keyspace(@keyspace)
85
+ end
86
+ end
87
+
88
+ def client
89
+ reconnect! if @client.nil?
90
+ @client
91
+ end
92
+
93
+ def reconnect!
94
+ @servers = all_nodes
95
+ @client = new_client
96
+ end
97
+
98
+ def all_nodes
99
+ if @auto_discover_nodes
100
+ temp_client = new_client
101
+ begin
102
+ ips = ::JSON.parse(temp_client.get_string_property('token map')).values
103
+ port = @servers.first.split(':').last
104
+ ips.map{|ip| "#{ip}:#{port}" }
105
+ ensure
106
+ temp_client.disconnect!
107
+ end
108
+ else
109
+ @servers
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,78 @@
1
+ class Cassandra
2
+ # A bunch of crap, mostly related to introspecting on column types
3
+ module Columns #:nodoc:
4
+ private
5
+
6
+ def is_super(column_family)
7
+ @is_super[column_family] ||= column_family_property(column_family, 'Type') == "Super"
8
+ end
9
+
10
+ def column_name_class(column_family)
11
+ @column_name_class[column_family] ||= column_name_class_for_key(column_family, "CompareWith")
12
+ end
13
+
14
+ def sub_column_name_class(column_family)
15
+ @sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "CompareSubcolumnsWith")
16
+ end
17
+
18
+ def column_family_property(column_family, key)
19
+ unless schema[column_family]
20
+ raise AccessError, "Invalid column family \"#{column_family}\""
21
+ end
22
+ schema[column_family][key]
23
+ end
24
+
25
+ def _standard_insert_mutation(column_family, column_name, value, timestamp, _=nil)
26
+ CassandraThrift::Mutation.new(
27
+ :column_or_supercolumn => CassandraThrift::ColumnOrSuperColumn.new(
28
+ :column => CassandraThrift::Column.new(
29
+ :name => column_name_class(column_family).new(column_name).to_s,
30
+ :value => value,
31
+ :timestamp => timestamp
32
+ )
33
+ )
34
+ )
35
+ end
36
+
37
+ def _super_insert_mutation(column_family, super_column_name, sub_columns, timestamp, _=nil)
38
+ CassandraThrift::Mutation.new(:column_or_supercolumn =>
39
+ CassandraThrift::ColumnOrSuperColumn.new(
40
+ :super_column => CassandraThrift::SuperColumn.new(
41
+ :name => column_name_class(column_family).new(super_column_name).to_s,
42
+ :columns => sub_columns.collect { |sub_column_name, sub_column_value|
43
+ CassandraThrift::Column.new(
44
+ :name => sub_column_name_class(column_family).new(sub_column_name).to_s,
45
+ :value => sub_column_value.to_s,
46
+ :timestamp => timestamp
47
+ )
48
+ }
49
+ )
50
+ )
51
+ )
52
+ end
53
+
54
+ # General info about a deletion object within a mutation
55
+ # timestamp - required. If this is the only param, it will cause deletion of the whole key at that TS
56
+ # supercolumn - opt. If passed, the deletes will only occur within that supercolumn (only subcolumns
57
+ # will be deleted). Otherwise the normal columns will be deleted.
58
+ # predicate - opt. Defines how to match the columns to delete. if supercolumn passed, the slice will
59
+ # be scoped to subcolumns of that supercolumn.
60
+
61
+ # Deletes a single column from the containing key/CF (and possibly supercolumn), at a given timestamp.
62
+ # Although mutations (as opposed to 'remove' calls) support deleting slices and lists of columns in one shot, this is not implemented here.
63
+ # 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
64
+ # methods to use delete mutation calls...although that might have performance implications. We'll leave that refactoring for later.
65
+ def _delete_mutation(cf, column, subcolumn, timestamp, options={})
66
+ deletion_hash = {:timestamp => timestamp}
67
+ if is_super(cf)
68
+ deletion_hash[:super_column] = column if column
69
+ deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [subcolumn]) if subcolumn
70
+ else
71
+ deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [column]) if column
72
+ end
73
+ CassandraThrift::Mutation.new(
74
+ :deletion => CassandraThrift::Deletion.new(deletion_hash)
75
+ )
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,90 @@
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(@keyspace, mutation_map, consistency_level)
9
+ end
10
+
11
+ def _remove(key, column_path, timestamp, consistency_level)
12
+ client.remove(@keyspace, key, column_path, timestamp, consistency_level)
13
+ end
14
+
15
+ def _count_columns(column_family, key, super_column, consistency)
16
+ client.get_count(@keyspace, key,
17
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
18
+ consistency
19
+ )
20
+ end
21
+
22
+ # FIXME: add support for start, stop, count functionality
23
+ def _get_columns(column_family, key, columns, sub_columns, consistency)
24
+ result = if is_super(column_family)
25
+ if sub_columns
26
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
27
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
28
+ CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
29
+ consistency))
30
+ else
31
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
32
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
33
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
34
+ consistency))
35
+ end
36
+ else
37
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
38
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
39
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
40
+ consistency))
41
+ end
42
+
43
+ klass = column_name_class(column_family)
44
+ (sub_columns || columns).map { |name| result[klass.new(name)] }
45
+ end
46
+
47
+ def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
48
+ # Single values; count and range parameters have no effect
49
+ if is_super(column_family) and sub_column
50
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
51
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
52
+ elsif !is_super(column_family) and column
53
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
54
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
55
+
56
+ # Slices
57
+ else
58
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
59
+ CassandraThrift::SliceRange.new(
60
+ :reversed => reversed,
61
+ :count => count,
62
+ :start => start,
63
+ :finish => finish))
64
+
65
+ if is_super(column_family) and column
66
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
67
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
68
+ else
69
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
70
+ multi_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
71
+ end
72
+ end
73
+ end
74
+
75
+ def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency)
76
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
77
+ predicate = if columns
78
+ CassandraThrift::SlicePredicate.new(:column_names => columns)
79
+ else
80
+ CassandraThrift::SlicePredicate.new(:slice_range =>
81
+ CassandraThrift::SliceRange.new(
82
+ :start => start,
83
+ :finish => finish,
84
+ :count => count))
85
+ end
86
+ range = CassandraThrift::KeyRange.new(:start_key => start_key, :end_key => finish_key, :count => key_count)
87
+ client.get_range_slices(@keyspace, column_parent, predicate, range, consistency)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,7 @@
1
+ class Cassandra
2
+ def self.VERSION
3
+ "0.6"
4
+ end
5
+ end
6
+
7
+ require "#{File.expand_path(File.dirname(__FILE__))}/../cassandra"
@@ -0,0 +1,2 @@
1
+ class Cassandra
2
+ end
@@ -0,0 +1,4 @@
1
+ class Cassandra
2
+ module Columns #:nodoc:
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ class Cassandra
2
+ # Inner methods for actually doing the Thrift calls
3
+ module Protocol #:nodoc:
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Cassandra
2
+ def self.VERSION
3
+ "0.7"
4
+ end
5
+ end
6
+
7
+ require "#{File.expand_path(File.dirname(__FILE__))}/../cassandra"
@@ -0,0 +1,10 @@
1
+ class Cassandra
2
+
3
+ ## Counters
4
+
5
+ # Add a value to the counter in cf:key:super column:column
6
+ def add(column_family, key, value, *columns_and_options)
7
+ column_family, column, sub_column, options = extract_and_validate_params(column_family, key, columns_and_options, WRITE_DEFAULTS)
8
+ _add(column_family, key, column, sub_column, value, options[:consistency])
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ class Cassandra
2
+ module Columns #:nodoc:
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ class Cassandra
2
+ # Inner methods for actually doing the Thrift calls
3
+ module Protocol #:nodoc:
4
+ private
5
+
6
+ def _remove_counter(key, column_path, consistency_level)
7
+ client.remove_counter(key, column_path, consistency_level)
8
+ end
9
+
10
+ def _add(column_family, key, column, sub_column, value, consistency)
11
+ if is_super(column_family)
12
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
13
+ counter_column = CassandraThrift::CounterColumn.new(:name => sub_column, :value => value)
14
+ else
15
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
16
+ counter_column = CassandraThrift::CounterColumn.new(:name => column, :value => value)
17
+ end
18
+ client.add(key, column_parent, counter_column, consistency)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ class Cassandra
2
+ def self.VERSION
3
+ "0.8"
4
+ end
5
+ end
6
+
7
+ require "#{File.expand_path(File.dirname(__FILE__))}/../cassandra"
@@ -0,0 +1,8 @@
1
+
2
+ class Array
3
+ def _flatten_once
4
+ result = []
5
+ each { |el| result.concat(Array(el)) }
6
+ result
7
+ end
8
+ end