cassandra 0.5.6 → 0.5.6.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.tar.gz.sig +3 -2
- data/CHANGELOG +28 -0
- data/LICENSE +202 -0
- data/Manifest +28 -0
- data/README +65 -0
- data/bin/cassandra_helper +16 -0
- data/cassandra.gemspec +5 -2
- data/conf/cassandra.in.sh +51 -0
- data/conf/log4j.properties +38 -0
- data/conf/storage-conf.xml +226 -0
- data/lib/cassandra.rb +23 -0
- data/lib/cassandra/array.rb +8 -0
- data/lib/cassandra/cassandra.rb +306 -0
- data/lib/cassandra/columns.rb +101 -0
- data/lib/cassandra/comparable.rb +28 -0
- data/lib/cassandra/constants.rb +12 -0
- data/lib/cassandra/debug.rb +7 -0
- data/lib/cassandra/long.rb +58 -0
- data/lib/cassandra/ordered_hash.rb +135 -0
- data/lib/cassandra/protocol.rb +72 -0
- data/lib/cassandra/safe_client.rb +26 -0
- data/lib/cassandra/time.rb +11 -0
- data/lib/cassandra/uuid.rb +111 -0
- data/vendor/gen-rb/cassandra.rb +853 -0
- data/vendor/gen-rb/cassandra_constants.rb +10 -0
- data/vendor/gen-rb/cassandra_types.rb +238 -0
- metadata +49 -6
- metadata.gz.sig +0 -0
@@ -0,0 +1,101 @@
|
|
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, '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, "CompareWith")
|
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, "CompareSubcolumnsWith")
|
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 UUID
|
25
|
+
else
|
26
|
+
String # UTF8, Ascii, Bytes, anything else
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_family_property(column_family, key)
|
31
|
+
@schema[column_family][key]
|
32
|
+
rescue NoMethodError
|
33
|
+
raise AccessError, "Invalid column family \"#{column_family}\""
|
34
|
+
end
|
35
|
+
|
36
|
+
def multi_column_to_hash!(hash)
|
37
|
+
hash.each do |key, column_or_supercolumn|
|
38
|
+
hash[key] = (column_or_supercolumn.column.value if column_or_supercolumn.column)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def multi_columns_to_hash!(column_family, hash)
|
43
|
+
hash.each do |key, columns|
|
44
|
+
hash[key] = columns_to_hash(column_family, columns)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def multi_sub_columns_to_hash!(column_family, hash)
|
49
|
+
hash.each do |key, sub_columns|
|
50
|
+
hash[key] = sub_columns_to_hash(column_family, sub_columns)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def columns_to_hash(column_family, columns)
|
55
|
+
columns_to_hash_for_classes(columns, column_name_class(column_family), sub_column_name_class(column_family))
|
56
|
+
end
|
57
|
+
|
58
|
+
def sub_columns_to_hash(column_family, columns)
|
59
|
+
columns_to_hash_for_classes(columns, sub_column_name_class(column_family))
|
60
|
+
end
|
61
|
+
|
62
|
+
def columns_to_hash_for_classes(columns, column_name_class, sub_column_name_class = nil)
|
63
|
+
hash = OrderedHash.new
|
64
|
+
Array(columns).each do |c|
|
65
|
+
c = c.super_column || c.column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
|
66
|
+
hash[column_name_class.new(c.name)] = case c
|
67
|
+
when CassandraThrift::SuperColumn
|
68
|
+
columns_to_hash_for_classes(c.columns, sub_column_name_class) # Pop the class stack, and recurse
|
69
|
+
when CassandraThrift::Column
|
70
|
+
c.value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
|
76
|
+
def hash_to_columns(column_family, hash, timestamp)
|
77
|
+
hash.map do |column, value|
|
78
|
+
CassandraThrift::ColumnOrSuperColumn.new(:column =>
|
79
|
+
CassandraThrift::Column.new(
|
80
|
+
:name => column_name_class(column_family).new(column).to_s,
|
81
|
+
:value => value,
|
82
|
+
:timestamp => timestamp))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def hash_to_super_columns(column_family, hash, timestamp)
|
87
|
+
hash.map do |column, sub_hash|
|
88
|
+
sub_columns = sub_hash.map do |sub_column, value|
|
89
|
+
CassandraThrift::Column.new(
|
90
|
+
:name => sub_column_name_class(column_family).new(sub_column).to_s,
|
91
|
+
:value => value,
|
92
|
+
:timestamp => timestamp)
|
93
|
+
end
|
94
|
+
CassandraThrift::ColumnOrSuperColumn.new(:super_column =>
|
95
|
+
CassandraThrift::SuperColumn.new(
|
96
|
+
:name => column_name_class(column_family).new(column).to_s,
|
97
|
+
:columns => sub_columns))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
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
|
+
self.to_i == other.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
@bytes
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
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
|
+
UUID = Cassandra::UUID
|
9
|
+
Long = Cassandra::Long
|
10
|
+
OrderedHash = Cassandra::OrderedHash
|
11
|
+
end
|
12
|
+
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.to_a.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).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
|
@@ -0,0 +1,135 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# Hash is ordered in Ruby 1.9!
|
4
|
+
if RUBY_VERSION >= '1.9'
|
5
|
+
OrderedHash = ::Hash
|
6
|
+
else
|
7
|
+
# Copyright (c) 2004-2009 David Heinemeier Hansson
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
class OrderedHash < Hash #:nodoc: all
|
29
|
+
require 'enumerator'
|
30
|
+
|
31
|
+
def self.[](*array)
|
32
|
+
hash = new
|
33
|
+
array.each_slice(2) { |key, value| hash[key] = value }
|
34
|
+
hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(*args, &block)
|
38
|
+
super
|
39
|
+
@keys = []
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize_copy(other)
|
43
|
+
super
|
44
|
+
# make a deep copy of keys
|
45
|
+
@keys = other.keys
|
46
|
+
end
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
@keys << key if !has_key?(key)
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete(key)
|
54
|
+
if has_key? key
|
55
|
+
index = @keys.index(key)
|
56
|
+
@keys.delete_at index
|
57
|
+
end
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete_if
|
62
|
+
super
|
63
|
+
sync_keys!
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def reject!
|
68
|
+
super
|
69
|
+
sync_keys!
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def reject(&block)
|
74
|
+
dup.reject!(&block)
|
75
|
+
end
|
76
|
+
|
77
|
+
def keys
|
78
|
+
@keys.dup
|
79
|
+
end
|
80
|
+
|
81
|
+
def values
|
82
|
+
@keys.collect { |key| self[key] }
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_hash
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def each_key
|
90
|
+
@keys.each { |key| yield key }
|
91
|
+
end
|
92
|
+
|
93
|
+
def each_value
|
94
|
+
@keys.each { |key| yield self[key]}
|
95
|
+
end
|
96
|
+
|
97
|
+
def each
|
98
|
+
@keys.each {|key| yield [key, self[key]]}
|
99
|
+
end
|
100
|
+
|
101
|
+
alias_method :each_pair, :each
|
102
|
+
|
103
|
+
def clear
|
104
|
+
super
|
105
|
+
@keys.clear
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def shift
|
110
|
+
k = @keys.first
|
111
|
+
v = delete(k)
|
112
|
+
[k, v]
|
113
|
+
end
|
114
|
+
|
115
|
+
def merge!(other_hash)
|
116
|
+
other_hash.each {|k,v| self[k] = v }
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
def merge(other_hash)
|
121
|
+
dup.merge!(other_hash)
|
122
|
+
end
|
123
|
+
|
124
|
+
def inspect
|
125
|
+
"#<OrderedHash #{super}>"
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def sync_keys!
|
131
|
+
@keys.delete_if {|k| !has_key?(k)}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# Inner methods for actually doing the Thrift calls
|
4
|
+
module Protocol #:nodoc:
|
5
|
+
private
|
6
|
+
|
7
|
+
def _mutate(mutation, consistency)
|
8
|
+
@client.batch_mutate(@keyspace, mutation, consistency)
|
9
|
+
end
|
10
|
+
|
11
|
+
def _count_columns(column_family, key, super_column, consistency)
|
12
|
+
@client.get_count(@keyspace, key,
|
13
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
|
14
|
+
consistency
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def _get_columns(column_family, key, columns, sub_columns, consistency)
|
19
|
+
result = if is_super(column_family)
|
20
|
+
if sub_columns
|
21
|
+
columns_to_hash(column_family, @client.get_slice(@keyspace, key,
|
22
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
|
23
|
+
CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
|
24
|
+
consistency))
|
25
|
+
else
|
26
|
+
columns_to_hash(column_family, @client.get_slice(@keyspace, key,
|
27
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
28
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
29
|
+
consistency))
|
30
|
+
end
|
31
|
+
else
|
32
|
+
columns_to_hash(column_family, @client.get_slice(@keyspace, key,
|
33
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
34
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
35
|
+
consistency))
|
36
|
+
end
|
37
|
+
sub_columns || columns.map { |name| result[name] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
|
41
|
+
# Single values; count and range parameters have no effect
|
42
|
+
if is_super(column_family) and sub_column
|
43
|
+
column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
|
44
|
+
multi_column_to_hash!(@client.multiget(@keyspace, keys, column_path, consistency))
|
45
|
+
elsif !is_super(column_family) and column
|
46
|
+
column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
|
47
|
+
multi_column_to_hash!(@client.multiget(@keyspace, keys, column_path, consistency))
|
48
|
+
|
49
|
+
# Slices
|
50
|
+
else
|
51
|
+
predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
|
52
|
+
CassandraThrift::SliceRange.new(
|
53
|
+
:reversed => reversed,
|
54
|
+
:count => count,
|
55
|
+
:start => start,
|
56
|
+
:finish => finish))
|
57
|
+
|
58
|
+
if is_super(column_family) and column
|
59
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
|
60
|
+
multi_sub_columns_to_hash!(column_family, @client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
|
61
|
+
else
|
62
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
63
|
+
multi_columns_to_hash!(column_family, @client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def _get_range(column_family, start, finish, count, consistency)
|
69
|
+
@client.get_key_range(@keyspace, column_family, start, finish, count, consistency)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
module CassandraThrift #:nodoc: all
|
3
|
+
module Cassandra
|
4
|
+
|
5
|
+
class SafeClient
|
6
|
+
def initialize(client, transport, reset = false)
|
7
|
+
@client = client
|
8
|
+
@transport = transport
|
9
|
+
@reset = reset
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset_transport
|
13
|
+
@transport.close rescue nil
|
14
|
+
@transport.open
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(*args)
|
18
|
+
reset_transport if @reset
|
19
|
+
@client.send(*args)
|
20
|
+
rescue IOError, UnavailableException, Thrift::ProtocolException, Thrift::ApplicationException, Thrift::TransportException
|
21
|
+
reset_transport
|
22
|
+
@client.send(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|