mcmire-cassandra 0.12.2
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 +108 -0
- data/LICENSE +202 -0
- data/Manifest +63 -0
- data/README.md +352 -0
- data/Rakefile +169 -0
- data/bin/cassandra_helper +16 -0
- data/conf/0.6/cassandra.in.sh +47 -0
- data/conf/0.6/log4j.properties +38 -0
- data/conf/0.6/schema.json +57 -0
- data/conf/0.6/storage-conf.xml +352 -0
- data/conf/0.7/cassandra.in.sh +46 -0
- data/conf/0.7/cassandra.yaml +336 -0
- data/conf/0.7/log4j-server.properties +41 -0
- data/conf/0.7/schema.json +57 -0
- data/conf/0.7/schema.txt +45 -0
- data/conf/0.8/cassandra.in.sh +41 -0
- data/conf/0.8/cassandra.yaml +61 -0
- data/conf/0.8/log4j-server.properties +40 -0
- data/conf/0.8/schema.json +66 -0
- data/conf/0.8/schema.txt +51 -0
- data/lib/cassandra/0.6/cassandra.rb +113 -0
- data/lib/cassandra/0.6/columns.rb +78 -0
- data/lib/cassandra/0.6/protocol.rb +90 -0
- data/lib/cassandra/0.6.rb +7 -0
- data/lib/cassandra/0.7/cassandra.rb +2 -0
- data/lib/cassandra/0.7/columns.rb +4 -0
- data/lib/cassandra/0.7/protocol.rb +5 -0
- data/lib/cassandra/0.7.rb +7 -0
- data/lib/cassandra/0.8/cassandra.rb +10 -0
- data/lib/cassandra/0.8/columns.rb +4 -0
- data/lib/cassandra/0.8/protocol.rb +21 -0
- data/lib/cassandra/0.8.rb +7 -0
- data/lib/cassandra/array.rb +8 -0
- data/lib/cassandra/cassandra.rb +1070 -0
- data/lib/cassandra/column_family.rb +3 -0
- data/lib/cassandra/columns.rb +144 -0
- data/lib/cassandra/comparable.rb +28 -0
- data/lib/cassandra/constants.rb +11 -0
- data/lib/cassandra/debug.rb +9 -0
- data/lib/cassandra/helpers.rb +41 -0
- data/lib/cassandra/keyspace.rb +3 -0
- data/lib/cassandra/long.rb +58 -0
- data/lib/cassandra/mock.rb +511 -0
- data/lib/cassandra/ordered_hash.rb +192 -0
- data/lib/cassandra/protocol.rb +120 -0
- data/lib/cassandra/time.rb +11 -0
- data/lib/cassandra.rb +38 -0
- data/mcmire-cassandra.gemspec +43 -0
- data/test/cassandra_client_test.rb +20 -0
- data/test/cassandra_mock_test.rb +116 -0
- data/test/cassandra_test.rb +863 -0
- data/test/comparable_types_test.rb +45 -0
- data/test/eventmachine_test.rb +42 -0
- data/test/ordered_hash_test.rb +386 -0
- data/test/test_helper.rb +15 -0
- data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
- data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
- data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
- data/vendor/0.7/gen-rb/cassandra.rb +1936 -0
- data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
- data/vendor/0.7/gen-rb/cassandra_types.rb +681 -0
- data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
- data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
- data/vendor/0.8/gen-rb/cassandra_types.rb +824 -0
- metadata +200 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# A bunch of crap, mostly related to introspecting on column types
|
4
|
+
module Columns #:nodoc:
|
5
|
+
private
|
6
|
+
|
7
|
+
def is_super(column_family)
|
8
|
+
@is_super[column_family] ||= column_family_property(column_family, 'column_type') == "Super"
|
9
|
+
end
|
10
|
+
|
11
|
+
def column_name_class(column_family)
|
12
|
+
@column_name_class[column_family] ||= column_name_class_for_key(column_family, "comparator_type")
|
13
|
+
end
|
14
|
+
|
15
|
+
def sub_column_name_class(column_family)
|
16
|
+
@sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "subcomparator_type")
|
17
|
+
end
|
18
|
+
|
19
|
+
def column_name_class_for_key(column_family, comparator_key)
|
20
|
+
property = column_family_property(column_family, comparator_key)
|
21
|
+
property =~ /.*\.(.*?)$/
|
22
|
+
case $1
|
23
|
+
when "LongType" then Long
|
24
|
+
when "LexicalUUIDType", "TimeUUIDType" then SimpleUUID::UUID
|
25
|
+
else
|
26
|
+
String # UTF8, Ascii, Bytes, anything else
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_family_property(column_family, key)
|
31
|
+
cfdef = schema.cf_defs.find {|cfdef| cfdef.name == column_family }
|
32
|
+
unless cfdef
|
33
|
+
raise AccessError, "Invalid column family \"#{column_family}\""
|
34
|
+
end
|
35
|
+
cfdef.send(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def multi_key_slices_to_hash(column_family, array, return_empty_rows = false)
|
39
|
+
ret = OrderedHash.new
|
40
|
+
array.each do |value|
|
41
|
+
next if return_empty_rows == false && value.columns.length == 0
|
42
|
+
ret[value.key] = columns_to_hash(column_family, value.columns)
|
43
|
+
end
|
44
|
+
ret
|
45
|
+
end
|
46
|
+
|
47
|
+
def multi_column_to_hash!(hash)
|
48
|
+
hash.each do |key, column_or_supercolumn|
|
49
|
+
hash[key] = (column_or_supercolumn.column.value if column_or_supercolumn.column)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def multi_columns_to_hash!(column_family, hash)
|
54
|
+
hash.each do |key, columns|
|
55
|
+
hash[key] = columns_to_hash(column_family, columns)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def multi_sub_columns_to_hash!(column_family, hash)
|
60
|
+
hash.each do |key, sub_columns|
|
61
|
+
hash[key] = sub_columns_to_hash(column_family, sub_columns)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def columns_to_hash(column_family, columns)
|
66
|
+
columns_to_hash_for_classes(columns, column_name_class(column_family), sub_column_name_class(column_family))
|
67
|
+
end
|
68
|
+
|
69
|
+
def sub_columns_to_hash(column_family, columns)
|
70
|
+
columns_to_hash_for_classes(columns, sub_column_name_class(column_family))
|
71
|
+
end
|
72
|
+
|
73
|
+
def columns_to_hash_for_classes(columns, column_name_class, sub_column_name_class = nil)
|
74
|
+
hash = OrderedHash.new
|
75
|
+
Array(columns).each do |c|
|
76
|
+
c = c.super_column || c.column || c.counter_column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
|
77
|
+
case c
|
78
|
+
when CassandraThrift::SuperColumn
|
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
|
80
|
+
when CassandraThrift::Column
|
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)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
hash
|
87
|
+
end
|
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
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# Abstract base class for comparable numeric column name types
|
4
|
+
class Comparable
|
5
|
+
class TypeError < ::TypeError #:nodoc:
|
6
|
+
end
|
7
|
+
|
8
|
+
def <=>(other)
|
9
|
+
self.to_i <=> other.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def hash
|
13
|
+
@bytes.hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(other)
|
17
|
+
other.is_a?(Comparable) and @bytes == other.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
other.respond_to?(:to_i) && self.to_i == other.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@bytes
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# A helper module you can include in your own class. Makes it easier
|
4
|
+
# to work with Cassandra subclasses.
|
5
|
+
module Constants
|
6
|
+
include Cassandra::Consistency
|
7
|
+
|
8
|
+
Long = Cassandra::Long
|
9
|
+
OrderedHash = Cassandra::OrderedHash
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Cassandra
|
2
|
+
module Helpers
|
3
|
+
def extract_and_validate_params(column_family, keys, args, options)
|
4
|
+
options = options.dup
|
5
|
+
column_family = column_family.to_s
|
6
|
+
# Keys
|
7
|
+
[keys].flatten.each do |key|
|
8
|
+
raise ArgumentError, "Key #{key.inspect} must be a String for #{caller[2].inspect}." unless key.is_a?(String)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Options
|
12
|
+
if args.last.is_a?(Hash)
|
13
|
+
extras = args.last.keys - options.keys
|
14
|
+
raise ArgumentError, "Invalid options #{extras.inspect[1..-2]} for #{caller[1]}" if extras.any?
|
15
|
+
options.merge!(args.pop)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ranges
|
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)
|
21
|
+
klass, sub_klass = column_name_class(column_family), sub_column_name_class(column_family)
|
22
|
+
range_class = column ? sub_klass : klass
|
23
|
+
|
24
|
+
[:start, :finish].each do |opt|
|
25
|
+
options[opt] = options[opt] ? range_class.new(options[opt]).to_s : ''
|
26
|
+
end
|
27
|
+
|
28
|
+
[column_family, s_map(column, klass), s_map(sub_column, sub_klass), options]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Convert stuff to strings.
|
32
|
+
def s_map(el, klass)
|
33
|
+
case el
|
34
|
+
when Array then el.map { |i| s_map(i, klass) }
|
35
|
+
when NilClass then nil
|
36
|
+
else
|
37
|
+
klass.new(el).to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# A temporally-ordered Long class for use in Cassandra column names
|
4
|
+
class Long < Comparable
|
5
|
+
|
6
|
+
# FIXME Should unify with or subclass Cassandra::UUID
|
7
|
+
def initialize(bytes = nil)
|
8
|
+
case bytes
|
9
|
+
when self.class # Long
|
10
|
+
@bytes = bytes.to_s
|
11
|
+
when String
|
12
|
+
case bytes.size
|
13
|
+
when 8 # Raw byte array
|
14
|
+
@bytes = bytes
|
15
|
+
when 18 # Human-readable UUID-like representation; inverse of #to_guid
|
16
|
+
elements = bytes.split("-")
|
17
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID-like representation)" if elements.size != 3
|
18
|
+
@bytes = [elements.join].pack('H32')
|
19
|
+
else
|
20
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
|
21
|
+
end
|
22
|
+
when Integer
|
23
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**64
|
24
|
+
@bytes = [bytes >> 32, bytes % 2**32].pack("NN")
|
25
|
+
when NilClass, Time
|
26
|
+
# Time.stamp is 52 bytes, so we have 12 bytes of entropy left over
|
27
|
+
int = ((bytes || Time).stamp << 12) + rand(2**12)
|
28
|
+
@bytes = [int >> 32, int % 2**32].pack("NN")
|
29
|
+
else
|
30
|
+
raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_i
|
35
|
+
@to_i ||= begin
|
36
|
+
ints = @bytes.unpack("NN")
|
37
|
+
(ints[0] << 32) +
|
38
|
+
ints[1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_guid
|
43
|
+
"%08x-%04x-%04x" % @bytes.unpack("Nnn")
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect
|
47
|
+
"<Cassandra::Long##{object_id} time: #{
|
48
|
+
Time.at((to_i >> 12) / 1_000_000).utc.inspect
|
49
|
+
}, usecs: #{
|
50
|
+
(to_i >> 12) % 1_000_000
|
51
|
+
}, jitter: #{
|
52
|
+
to_i % 2**12
|
53
|
+
}, guid: #{
|
54
|
+
to_guid
|
55
|
+
}>"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|