cassandra 0.5.4 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +2 -0
- data/Manifest +1 -0
- data/README +1 -1
- data/Rakefile +32 -21
- data/cassandra.gemspec +4 -4
- data/conf/storage-conf.xml +6 -0
- data/lib/cassandra.rb +1 -0
- data/lib/cassandra/cassandra.rb +95 -102
- data/lib/cassandra/columns.rb +9 -8
- data/lib/cassandra/debug.rb +7 -0
- data/lib/cassandra/long.rb +24 -10
- data/lib/cassandra/protocol.rb +2 -14
- data/lib/cassandra/uuid.rb +28 -28
- data/test/cassandra_test.rb +47 -29
- data/test/comparable_types_test.rb +1 -0
- data/vendor/gen-rb/cassandra.rb +17 -87
- data/vendor/gen-rb/cassandra_types.rb +18 -32
- metadata +4 -2
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
data/README
CHANGED
@@ -27,7 +27,7 @@ Cassandra itself is a rapidly moving target. In order to get a working server, u
|
|
27
27
|
|
28
28
|
cassandra_helper cassandra
|
29
29
|
|
30
|
-
A server will be installed in <tt>$HOME/cassandra/
|
30
|
+
A server will be installed in <tt>$HOME/cassandra/server</tt>, and started in debug mode.
|
31
31
|
|
32
32
|
== Usage
|
33
33
|
|
data/Rakefile
CHANGED
@@ -16,26 +16,28 @@ unless ENV['FROM_BIN_CASSANDRA_HELPER']
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
REVISION = "
|
19
|
+
REVISION = "c4992f48ce9c26ce4fd028240447b4cbe85ecf26"
|
20
20
|
|
21
21
|
PATCHES = [
|
22
|
-
"http://issues.apache.org/jira/secure/attachment/
|
22
|
+
"http://issues.apache.org/jira/secure/attachment/12417533/388.patch",
|
23
|
+
"http://issues.apache.org/jira/secure/attachment/12417682/CASSANDRA-336-code.diff",
|
24
|
+
"http://issues.apache.org/jira/secure/attachment/12417683/CASSANDRA-336-thrift.diff"]
|
23
25
|
|
24
|
-
CASSANDRA_HOME = "#{ENV['HOME']}/cassandra/
|
26
|
+
CASSANDRA_HOME = "#{ENV['HOME']}/cassandra/server"
|
25
27
|
|
26
28
|
CASSANDRA_TEST = "#{ENV['HOME']}/cassandra/test"
|
27
29
|
|
30
|
+
directory CASSANDRA_TEST
|
31
|
+
|
28
32
|
desc "Start Cassandra"
|
29
|
-
task :cassandra => [:
|
33
|
+
task :cassandra => [:build, CASSANDRA_TEST] do
|
30
34
|
# Construct environment
|
31
35
|
env = ""
|
32
36
|
if !ENV["CASSANDRA_INCLUDE"]
|
33
37
|
env << "CASSANDRA_INCLUDE=#{Dir.pwd}/conf/cassandra.in.sh "
|
34
38
|
env << "CASSANDRA_HOME=#{CASSANDRA_HOME} "
|
35
|
-
env << "CASSANDRA_CONF=#{File.expand_path(File.dirname(__FILE__))}/conf"
|
36
|
-
end
|
37
|
-
# Create data dir
|
38
|
-
Dir.mkdir(CASSANDRA_TEST) if !File.exist?(CASSANDRA_TEST)
|
39
|
+
env << "CASSANDRA_CONF=#{File.expand_path(File.dirname(__FILE__))}/conf"
|
40
|
+
end
|
39
41
|
# Start server
|
40
42
|
Dir.chdir(CASSANDRA_TEST) do
|
41
43
|
exec("env #{env} #{CASSANDRA_HOME}/bin/cassandra -f")
|
@@ -53,29 +55,35 @@ task :java do
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
56
|
-
desc "
|
57
|
-
task :
|
58
|
-
# Check git version
|
58
|
+
desc "Check Git version"
|
59
|
+
task :git do
|
59
60
|
unless `git --version 2>&1` =~ /git version 1.6/
|
60
61
|
puts "You need to install git 1.6."
|
61
62
|
exit(1)
|
62
63
|
end
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "Checkout Cassandra from git"
|
67
|
+
task :checkout => [:java, :git] do
|
63
68
|
# Like a git submodule, but all in one more obvious place
|
64
69
|
unless File.exist?(CASSANDRA_HOME)
|
70
|
+
puts "Checking Cassandra out from git"
|
65
71
|
cmd = "git clone git://git.apache.org/cassandra.git #{CASSANDRA_HOME}"
|
66
72
|
if !system(cmd)
|
67
73
|
put "Checkout failed. Try:\n #{cmd}"
|
68
74
|
exit(1)
|
69
75
|
end
|
70
|
-
|
71
|
-
end
|
76
|
+
end
|
72
77
|
end
|
73
78
|
|
74
79
|
desc "Apply patches to Cassandra checkout; use RESET=1 to force"
|
75
|
-
task :patch do
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
task :patch => [:checkout] do
|
81
|
+
# Verify checkout revision and patchset
|
82
|
+
Dir.chdir(CASSANDRA_HOME) do
|
83
|
+
current_checkout = `git show HEAD~#{PATCHES.size} | head -n1`
|
84
|
+
if !current_checkout.include?(REVISION)
|
85
|
+
puts "Updating Cassandra and applying patches"
|
86
|
+
system("rm -rf #{CASSANDRA_TEST}/data")
|
79
87
|
system("ant clean && git fetch && git reset #{REVISION} --hard")
|
80
88
|
# Delete untracked files, so that the patchs can apply again
|
81
89
|
Array(`git status`[/Untracked files:(.*)$/m, 1].to_s.split("\n")[3..-1]).each do |file|
|
@@ -83,7 +91,7 @@ task :patch do
|
|
83
91
|
end
|
84
92
|
# Patch, with a handy commit for each one
|
85
93
|
PATCHES.each do |url|
|
86
|
-
raise "#{url} failed" unless system("
|
94
|
+
raise "#{url} failed" unless system("curl #{url} | patch -p1")
|
87
95
|
system("git commit -a -m 'Applied patch: #{url.inspect}'")
|
88
96
|
end
|
89
97
|
end
|
@@ -91,8 +99,9 @@ task :patch do
|
|
91
99
|
end
|
92
100
|
|
93
101
|
desc "Rebuild Cassandra"
|
94
|
-
task :build do
|
102
|
+
task :build => [:patch] do
|
95
103
|
unless File.exist?("#{CASSANDRA_HOME}/build")
|
104
|
+
puts "Building Cassandra"
|
96
105
|
cmd = "cd #{CASSANDRA_HOME} && ant"
|
97
106
|
if !system(cmd)
|
98
107
|
puts "Could not build Casssandra. Try:\n #{cmd}"
|
@@ -103,25 +112,27 @@ end
|
|
103
112
|
|
104
113
|
desc "Clean Cassandra build"
|
105
114
|
task :clean do
|
115
|
+
puts "Cleaning Cassandra"
|
106
116
|
if File.exist?(CASSANDRA_HOME)
|
107
117
|
Dir.chdir(CASSANDRA_HOME) do
|
108
118
|
system("ant clean")
|
109
119
|
end
|
110
|
-
end
|
120
|
+
end
|
111
121
|
end
|
112
122
|
|
113
123
|
namespace :data do
|
114
124
|
desc "Reset test data"
|
115
125
|
task :reset do
|
126
|
+
puts "Resetting test data"
|
116
127
|
system("rm -rf #{CASSANDRA_TEST}/data")
|
117
128
|
end
|
118
129
|
end
|
119
130
|
|
120
131
|
# desc "Regenerate thrift bindings for Cassandra" # Dev only
|
121
132
|
task :thrift do
|
133
|
+
puts "Generating Thrift bindings"
|
122
134
|
system(
|
123
135
|
"cd vendor &&
|
124
136
|
rm -rf gen-rb &&
|
125
137
|
thrift -gen rb #{CASSANDRA_HOME}/interface/cassandra.thrift")
|
126
138
|
end
|
127
|
-
|
data/cassandra.gemspec
CHANGED
@@ -2,18 +2,18 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{cassandra}
|
5
|
-
s.version = "0.5.
|
5
|
+
s.version = "0.5.5"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0.8") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Evan Weaver"]
|
9
9
|
s.cert_chain = ["/Users/eweaver/p/configuration/gem_certificates/evan_weaver-original-public_cert.pem"]
|
10
|
-
s.date = %q{2009-08-
|
10
|
+
s.date = %q{2009-08-27}
|
11
11
|
s.default_executable = %q{cassandra_helper}
|
12
12
|
s.description = %q{A Ruby client for the Cassandra distributed database.}
|
13
13
|
s.email = %q{}
|
14
14
|
s.executables = ["cassandra_helper"]
|
15
|
-
s.extra_rdoc_files = ["bin/cassandra_helper", "CHANGELOG", "lib/cassandra/array.rb", "lib/cassandra/cassandra.rb", "lib/cassandra/columns.rb", "lib/cassandra/comparable.rb", "lib/cassandra/constants.rb", "lib/cassandra/long.rb", "lib/cassandra/ordered_hash.rb", "lib/cassandra/protocol.rb", "lib/cassandra/safe_client.rb", "lib/cassandra/time.rb", "lib/cassandra/uuid.rb", "lib/cassandra.rb", "LICENSE", "README"]
|
16
|
-
s.files = ["bin/cassandra_helper", "CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra/array.rb", "lib/cassandra/cassandra.rb", "lib/cassandra/columns.rb", "lib/cassandra/comparable.rb", "lib/cassandra/constants.rb", "lib/cassandra/long.rb", "lib/cassandra/ordered_hash.rb", "lib/cassandra/protocol.rb", "lib/cassandra/safe_client.rb", "lib/cassandra/time.rb", "lib/cassandra/uuid.rb", "lib/cassandra.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/cassandra_test.rb", "test/comparable_types_test.rb", "test/test_helper.rb", "vendor/gen-rb/cassandra.rb", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb", "cassandra.gemspec"]
|
15
|
+
s.extra_rdoc_files = ["bin/cassandra_helper", "CHANGELOG", "lib/cassandra/array.rb", "lib/cassandra/cassandra.rb", "lib/cassandra/columns.rb", "lib/cassandra/comparable.rb", "lib/cassandra/constants.rb", "lib/cassandra/debug.rb", "lib/cassandra/long.rb", "lib/cassandra/ordered_hash.rb", "lib/cassandra/protocol.rb", "lib/cassandra/safe_client.rb", "lib/cassandra/time.rb", "lib/cassandra/uuid.rb", "lib/cassandra.rb", "LICENSE", "README"]
|
16
|
+
s.files = ["bin/cassandra_helper", "CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra/array.rb", "lib/cassandra/cassandra.rb", "lib/cassandra/columns.rb", "lib/cassandra/comparable.rb", "lib/cassandra/constants.rb", "lib/cassandra/debug.rb", "lib/cassandra/long.rb", "lib/cassandra/ordered_hash.rb", "lib/cassandra/protocol.rb", "lib/cassandra/safe_client.rb", "lib/cassandra/time.rb", "lib/cassandra/uuid.rb", "lib/cassandra.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/cassandra_test.rb", "test/comparable_types_test.rb", "test/test_helper.rb", "vendor/gen-rb/cassandra.rb", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb", "cassandra.gemspec"]
|
17
17
|
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/cassandra/}
|
18
18
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cassandra", "--main", "README"]
|
19
19
|
s.require_paths = ["lib"]
|
data/conf/storage-conf.xml
CHANGED
@@ -45,6 +45,12 @@
|
|
45
45
|
<ColumnFamily CompareWith="TimeUUIDType" Name="Blogs"/>
|
46
46
|
<ColumnFamily CompareWith="TimeUUIDType" Name="Comments"/>
|
47
47
|
</Keyspace>
|
48
|
+
|
49
|
+
<Keyspace Name="MultiblogLong">
|
50
|
+
<KeysCachedFraction>0.01</KeysCachedFraction>
|
51
|
+
<ColumnFamily CompareWith="LongType" Name="Blogs"/>
|
52
|
+
<ColumnFamily CompareWith="LongType" Name="Comments"/>
|
53
|
+
</Keyspace>
|
48
54
|
</Keyspaces>
|
49
55
|
|
50
56
|
<!-- Partitioner: any IPartitioner may be used, including your own
|
data/lib/cassandra.rb
CHANGED
data/lib/cassandra/cassandra.rb
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
Create a new Cassandra client instance. Accepts a keyspace name, and optional host and port.
|
4
4
|
|
5
5
|
client = Cassandra.new('twitter', '127.0.0.1', 9160)
|
6
|
-
|
6
|
+
|
7
7
|
You can then make calls to the server via the <tt>client</tt> instance.
|
8
|
-
|
8
|
+
|
9
9
|
client.insert(:UserRelationships, "5", {"user_timeline" => {UUID.new => "1"}})
|
10
10
|
client.get(:UserRelationships, "5", "user_timeline")
|
11
|
-
|
11
|
+
|
12
12
|
For read methods, valid option parameters are:
|
13
13
|
|
14
14
|
<tt>:count</tt>:: How many results to return. Defaults to 100.
|
@@ -18,9 +18,9 @@ For read methods, valid option parameters are:
|
|
18
18
|
<tt>:consistency</tt>:: The consistency level of the request. Defaults to <tt>Cassandra::Consistency::ONE</tt> (one node must respond). Other valid options are <tt>Cassandra::Consistency::ZERO</tt>, <tt>Cassandra::Consistency::QUORUM</tt>, and <tt>Cassandra::Consistency::ALL</tt>.
|
19
19
|
|
20
20
|
Note that some read options have no relevance in some contexts.
|
21
|
-
|
21
|
+
|
22
22
|
For write methods, valid option parameters are:
|
23
|
-
|
23
|
+
|
24
24
|
<tt>:timestamp </tt>:: The transaction timestamp. Defaults to the current time in milliseconds. This is used for conflict resolution by the server; you normally never need to change it.
|
25
25
|
<tt>:consistency</tt>:: See above.
|
26
26
|
|
@@ -38,18 +38,18 @@ class Cassandra
|
|
38
38
|
end
|
39
39
|
|
40
40
|
MAX_INT = 2**31 - 1
|
41
|
-
|
42
|
-
WRITE_DEFAULTS = {
|
41
|
+
|
42
|
+
WRITE_DEFAULTS = {
|
43
43
|
:count => MAX_INT,
|
44
44
|
:timestamp => nil,
|
45
|
-
:consistency => Consistency::ONE
|
45
|
+
:consistency => Consistency::ONE
|
46
46
|
}.freeze
|
47
47
|
|
48
48
|
READ_DEFAULTS = {
|
49
|
-
:count => 100,
|
50
|
-
:start => nil,
|
51
|
-
:finish => nil,
|
52
|
-
:reversed => false,
|
49
|
+
:count => 100,
|
50
|
+
:start => nil,
|
51
|
+
:finish => nil,
|
52
|
+
:reversed => false,
|
53
53
|
:consistency => Consistency::ONE
|
54
54
|
}.freeze
|
55
55
|
|
@@ -91,46 +91,49 @@ class Cassandra
|
|
91
91
|
# a nested hash for a super column family. Supports the <tt>:consistency</tt>
|
92
92
|
# and <tt>:timestamp</tt> options.
|
93
93
|
def insert(column_family, key, hash, options = {})
|
94
|
-
column_family, _, _, options =
|
94
|
+
column_family, _, _, options =
|
95
|
+
validate_params(column_family, key, [options], WRITE_DEFAULTS)
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
:cfmap => {column_family =>
|
105
|
-
hash_to_columns(column_family, hash, options[:timestamp] || Time.stamp)})
|
106
|
-
end
|
107
|
-
|
108
|
-
args = [mutation, options[:consistency]]
|
109
|
-
@batch ? @batch << args : _insert(*args)
|
97
|
+
args = [column_family, hash, options[:timestamp] || Time.stamp]
|
98
|
+
columns = is_super(column_family) ? hash_to_super_columns(*args) : hash_to_columns(*args)
|
99
|
+
mutation = CassandraThrift::BatchMutation.new(
|
100
|
+
:key => key,
|
101
|
+
:cfmap => {column_family => columns},
|
102
|
+
:column_paths => [])
|
103
|
+
|
104
|
+
@batch ? @batch << mutation : _mutate([mutation], options[:consistency])
|
110
105
|
end
|
111
106
|
|
112
107
|
## Delete
|
113
108
|
|
114
|
-
#
|
109
|
+
# _mutate the element at the column_family:key:[column]:[sub_column]
|
115
110
|
# path you request. Supports the <tt>:consistency</tt> and <tt>:timestamp</tt>
|
116
111
|
# options.
|
117
112
|
def remove(column_family, key, *columns_and_options)
|
118
|
-
column_family, column, sub_column, options =
|
119
|
-
|
120
|
-
|
113
|
+
column_family, column, sub_column, options =
|
114
|
+
validate_params(column_family, key, columns_and_options, WRITE_DEFAULTS)
|
115
|
+
|
116
|
+
args = {:column_family => column_family, :timestamp => options[:timestamp] || Time.stamp}
|
117
|
+
columns = is_super(column_family) ? {:super_column => column, :column => sub_column} : {:column => column}
|
118
|
+
mutation = CassandraThrift::BatchMutation.new(
|
119
|
+
:key => key,
|
120
|
+
:cfmap => {},
|
121
|
+
:column_paths => [CassandraThrift::ColumnPath.new(args.merge(columns))])
|
122
|
+
|
123
|
+
@batch ? @batch << mutation : _mutate([mutation], options[:consistency])
|
121
124
|
end
|
122
125
|
|
123
|
-
# Remove all rows in the column family you request. Supports options
|
126
|
+
# Remove all rows in the column family you request. Supports options
|
124
127
|
# <tt>:consistency</tt> and <tt>:timestamp</tt>.
|
125
|
-
# FIXME May not currently delete all records without multiple calls. Waiting
|
128
|
+
# FIXME May not currently delete all records without multiple calls. Waiting
|
126
129
|
# for ranged remove support in Cassandra.
|
127
130
|
def clear_column_family!(column_family, options = {})
|
128
131
|
get_range(column_family).each { |key| remove(column_family, key, options) }
|
129
132
|
end
|
130
133
|
|
131
|
-
# Remove all rows in the keyspace. Supports options <tt>:consistency</tt> and
|
134
|
+
# Remove all rows in the keyspace. Supports options <tt>:consistency</tt> and
|
132
135
|
# <tt>:timestamp</tt>.
|
133
|
-
# FIXME May not currently delete all records without multiple calls. Waiting
|
136
|
+
# FIXME May not currently delete all records without multiple calls. Waiting
|
134
137
|
# for ranged remove support in Cassandra.
|
135
138
|
def clear_keyspace!(options = {})
|
136
139
|
@schema.keys.each { |column_family| clear_column_family!(column_family, options) }
|
@@ -139,10 +142,11 @@ class Cassandra
|
|
139
142
|
### Read
|
140
143
|
|
141
144
|
# Count the elements at the column_family:key:[super_column] path you
|
142
|
-
# request. Supports options <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>,
|
145
|
+
# request. Supports options <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>,
|
143
146
|
# <tt>:reversed</tt>, and <tt>:consistency</tt>.
|
144
147
|
def count_columns(column_family, key, *columns_and_options)
|
145
|
-
column_family, super_column, _, options =
|
148
|
+
column_family, super_column, _, options =
|
149
|
+
validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
|
146
150
|
_count_columns(column_family, key, super_column, options[:consistency])
|
147
151
|
end
|
148
152
|
|
@@ -153,14 +157,15 @@ class Cassandra
|
|
153
157
|
end
|
154
158
|
|
155
159
|
# Return a list of single values for the elements at the
|
156
|
-
# column_family:key:column[s]:[sub_columns] path you request. Supports the
|
160
|
+
# column_family:key:column[s]:[sub_columns] path you request. Supports the
|
157
161
|
# <tt>:consistency</tt> option.
|
158
162
|
def get_columns(column_family, key, *columns_and_options)
|
159
|
-
column_family, columns, sub_columns, options =
|
163
|
+
column_family, columns, sub_columns, options =
|
164
|
+
validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
|
160
165
|
_get_columns(column_family, key, columns, sub_columns, options[:consistency])
|
161
166
|
end
|
162
167
|
|
163
|
-
# Multi-key version of Cassandra#get_columns. Supports the <tt>:consistency</tt>
|
168
|
+
# Multi-key version of Cassandra#get_columns. Supports the <tt>:consistency</tt>
|
164
169
|
# option.
|
165
170
|
def multi_get_columns(column_family, keys, *options)
|
166
171
|
OrderedHash[*keys.map { |key| [key, get_columns(column_family, key, *options)] }._flatten_once]
|
@@ -168,16 +173,17 @@ class Cassandra
|
|
168
173
|
|
169
174
|
# Return a hash (actually, a Cassandra::OrderedHash) or a single value
|
170
175
|
# representing the element at the column_family:key:[column]:[sub_column]
|
171
|
-
# path you request. Supports options <tt>:count</tt>, <tt>:start</tt>,
|
176
|
+
# path you request. Supports options <tt>:count</tt>, <tt>:start</tt>,
|
172
177
|
# <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
|
173
178
|
def get(column_family, key, *columns_and_options)
|
174
|
-
column_family, column, sub_column, options =
|
179
|
+
column_family, column, sub_column, options =
|
180
|
+
validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
|
175
181
|
_get(column_family, key, column, sub_column, options[:count], options[:start], options[:finish], options[:reversed], options[:consistency])
|
176
182
|
rescue CassandraThrift::NotFoundException
|
177
183
|
is_super(column_family) && !sub_column ? OrderedHash.new : nil
|
178
184
|
end
|
179
185
|
|
180
|
-
# Multi-key version of Cassandra#get. Supports options <tt>:count</tt>,
|
186
|
+
# Multi-key version of Cassandra#get. Supports options <tt>:count</tt>,
|
181
187
|
# <tt>:start</tt>, <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
|
182
188
|
def multi_get(column_family, keys, *options)
|
183
189
|
OrderedHash[*keys.map { |key| [key, get(column_family, key, *options)] }._flatten_once]
|
@@ -186,23 +192,25 @@ class Cassandra
|
|
186
192
|
# Return true if the column_family:key:[column]:[sub_column] path you
|
187
193
|
# request exists. Supports the <tt>:consistency</tt> option.
|
188
194
|
def exists?(column_family, key, *columns_and_options)
|
189
|
-
column_family, column, sub_column, options =
|
195
|
+
column_family, column, sub_column, options =
|
196
|
+
validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
|
190
197
|
_get(column_family, key, column, sub_column, 1, nil, nil, nil, options[:consistency])
|
191
198
|
true
|
192
199
|
rescue CassandraThrift::NotFoundException
|
193
200
|
end
|
194
201
|
|
195
202
|
# Return a list of keys in the column_family you request. Requires the
|
196
|
-
# table to be partitioned with OrderPreservingHash. Supports the
|
197
|
-
# <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>, and <tt>:consistency</tt>
|
203
|
+
# table to be partitioned with OrderPreservingHash. Supports the
|
204
|
+
# <tt>:count</tt>, <tt>:start</tt>, <tt>:finish</tt>, and <tt>:consistency</tt>
|
198
205
|
# options.
|
199
206
|
def get_range(column_family, options = {})
|
200
|
-
column_family, _, _, options =
|
207
|
+
column_family, _, _, options =
|
208
|
+
validate_params(column_family, "", [options], READ_DEFAULTS)
|
201
209
|
_get_range(column_family, options[:start], options[:finish], options[:count], options[:consistency])
|
202
210
|
end
|
203
211
|
|
204
212
|
# Count all rows in the column_family you request. Requires the table
|
205
|
-
# to be partitioned with OrderPreservingHash. Supports the <tt>:start</tt>,
|
213
|
+
# to be partitioned with OrderPreservingHash. Supports the <tt>:start</tt>,
|
206
214
|
# <tt>:finish</tt>, and <tt>:consistency</tt> options.
|
207
215
|
# FIXME will count only MAX_INT records
|
208
216
|
def count_range(column_family, options = {})
|
@@ -210,33 +218,42 @@ class Cassandra
|
|
210
218
|
end
|
211
219
|
|
212
220
|
# Open a batch operation and yield. Inserts and deletes will be queued until
|
213
|
-
# the block closes, and then sent atomically to the server.
|
214
|
-
#
|
215
|
-
|
221
|
+
# the block closes, and then sent atomically to the server. Supports the
|
222
|
+
# <tt>:consistency</tt> option, which overrides the consistency set in
|
223
|
+
# the individual commands.
|
224
|
+
def batch(options = {})
|
225
|
+
_, _, _, options =
|
226
|
+
validate_params(@schema.keys.first, "", [options], WRITE_DEFAULTS)
|
227
|
+
|
216
228
|
@batch = []
|
217
229
|
yield
|
218
|
-
compact_mutations
|
219
|
-
|
230
|
+
compact_mutations!
|
231
|
+
_mutate(@batch, options[:consistency])
|
220
232
|
@batch = nil
|
221
233
|
end
|
222
|
-
|
234
|
+
|
223
235
|
private
|
224
|
-
|
236
|
+
|
225
237
|
# Extract and validate options.
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
238
|
+
# FIXME Should be done as a decorator
|
239
|
+
def validate_params(column_family, key, args, options)
|
240
|
+
if !key.is_a?(String)
|
241
|
+
raise ArgumentError, "Key #{key.inspect} must be a String for #{calling_method}"
|
242
|
+
elsif args.last.is_a?(Hash)
|
243
|
+
extras = args.last.keys - options.keys
|
244
|
+
raise ArgumentError, "Invalid options #{extras.inspect[1..-2]} for #{calling_method}" if extras.any?
|
232
245
|
options = options.merge(args.pop)
|
233
|
-
end
|
246
|
+
end
|
234
247
|
|
235
248
|
column_family, column, sub_column = column_family.to_s, args[0], args[1]
|
236
|
-
assert_column_name_classes(column_family, column, sub_column)
|
249
|
+
assert_column_name_classes(column_family, column, sub_column)
|
237
250
|
[column_family, map_to_s(column), map_to_s(sub_column), options]
|
238
251
|
end
|
239
252
|
|
253
|
+
def calling_method
|
254
|
+
"#{self.class}##{caller[0].split('`').last[0..-3]}"
|
255
|
+
end
|
256
|
+
|
240
257
|
# Convert stuff to strings.
|
241
258
|
def map_to_s(el)
|
242
259
|
case el
|
@@ -247,52 +264,28 @@ class Cassandra
|
|
247
264
|
raise Comparable::TypeError, "Can't map #{el.inspect}"
|
248
265
|
end
|
249
266
|
end
|
250
|
-
|
251
|
-
# Roll up queued mutations
|
252
|
-
def compact_mutations
|
253
|
-
compact_batch = []
|
267
|
+
|
268
|
+
# Roll up queued mutations, to improve atomicity.
|
269
|
+
def compact_mutations!
|
254
270
|
mutations = {}
|
255
271
|
|
256
|
-
|
272
|
+
# Nested hash merge
|
257
273
|
@batch.each do |m|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
mutations = {}
|
263
|
-
# Insert delete operation
|
264
|
-
compact_batch << m
|
265
|
-
else # BatchMutation, BatchMutationSuper
|
266
|
-
# Do a nested hash merge
|
267
|
-
if mutation_class = mutations[m.class]
|
268
|
-
if mutation = mutation_class[m.key]
|
269
|
-
if columns = mutation.cfmap[m.cfmap.keys.first]
|
270
|
-
columns.concat(m.cfmap.values.first)
|
271
|
-
else
|
272
|
-
mutation.cfmap.merge!(m.cfmap)
|
273
|
-
end
|
274
|
-
else
|
275
|
-
mutation_class[m.key] = m
|
276
|
-
end
|
274
|
+
if mutation = mutations[m.key]
|
275
|
+
# Inserts
|
276
|
+
if columns = mutation.cfmap[m.cfmap.keys.first]
|
277
|
+
columns.concat(m.cfmap.values.first)
|
277
278
|
else
|
278
|
-
|
279
|
+
mutation.cfmap.merge!(m.cfmap)
|
279
280
|
end
|
281
|
+
# Deletes
|
282
|
+
mutation.column_paths.concat(m.column_paths)
|
283
|
+
else
|
284
|
+
mutations[m.key] = m
|
280
285
|
end
|
281
286
|
end
|
282
287
|
|
283
|
-
|
288
|
+
# FIXME Return atomic thrift thingy
|
289
|
+
@batch = mutations.values
|
284
290
|
end
|
285
|
-
|
286
|
-
# Send all the queued mutations to the server.
|
287
|
-
def dispatch_mutations
|
288
|
-
@batch.compact!
|
289
|
-
@batch.each do |args|
|
290
|
-
case args.first
|
291
|
-
when CassandraThrift::BatchMutationSuper, CassandraThrift::BatchMutation
|
292
|
-
_insert(*args)
|
293
|
-
else
|
294
|
-
_remove(*args)
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
291
|
end
|
data/lib/cassandra/columns.rb
CHANGED
@@ -63,23 +63,24 @@ class Cassandra
|
|
63
63
|
end
|
64
64
|
hash
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
def hash_to_columns(column_family, hash, timestamp)
|
68
68
|
assert_column_name_classes(column_family, hash.keys)
|
69
|
-
hash_to_columns_without_assertion(column_family, hash, timestamp)
|
70
|
-
end
|
71
|
-
|
72
|
-
def hash_to_columns_without_assertion(column_family, hash, timestamp)
|
73
69
|
hash.map do |column, value|
|
74
|
-
CassandraThrift::
|
70
|
+
CassandraThrift::ColumnOrSuperColumn.new(:column =>
|
71
|
+
CassandraThrift::Column.new(:name => column.to_s, :value => value, :timestamp => timestamp))
|
75
72
|
end
|
76
|
-
end
|
73
|
+
end
|
77
74
|
|
78
75
|
def hash_to_super_columns(column_family, hash, timestamp)
|
79
76
|
assert_column_name_classes(column_family, hash.keys)
|
80
77
|
hash.map do |column, sub_hash|
|
81
78
|
assert_column_name_classes(column_family, nil, sub_hash.keys)
|
82
|
-
|
79
|
+
sub_columns = sub_hash.map do |sub_column, value|
|
80
|
+
CassandraThrift::Column.new(:name => sub_column.to_s, :value => value, :timestamp => timestamp)
|
81
|
+
end
|
82
|
+
CassandraThrift::ColumnOrSuperColumn.new(:super_column =>
|
83
|
+
CassandraThrift::SuperColumn.new(:name => column.to_s, :columns => sub_columns))
|
83
84
|
end
|
84
85
|
end
|
85
86
|
end
|
data/lib/cassandra/long.rb
CHANGED
@@ -2,18 +2,26 @@
|
|
2
2
|
class Cassandra
|
3
3
|
# A temporally-ordered Long class for use in Cassandra column names
|
4
4
|
class Long < Comparable
|
5
|
-
|
6
|
-
def initialize(bytes = nil)
|
5
|
+
|
6
|
+
def initialize(bytes = nil)
|
7
7
|
case bytes
|
8
8
|
when String
|
9
|
-
|
10
|
-
|
9
|
+
case bytes.size
|
10
|
+
when 8 # Raw byte array
|
11
|
+
@bytes = bytes
|
12
|
+
when 18 # Human-readable UUID-like representation; inverse of #to_guid
|
13
|
+
elements = bytes.split("-")
|
14
|
+
raise TypeError, "Malformed UUID-like representation" if elements.size != 3
|
15
|
+
@bytes = elements.join.to_a.pack('H32')
|
16
|
+
else
|
17
|
+
raise TypeError, "8 bytes required for byte array, or 18 characters required for UUID-like representation"
|
18
|
+
end
|
11
19
|
when Integer
|
12
20
|
raise TypeError, "Integer must be between 0 and 2**64" if bytes < 0 or bytes > 2**64
|
13
21
|
@bytes = [bytes >> 32, bytes % 2**32].pack("NN")
|
14
|
-
when NilClass
|
22
|
+
when NilClass, Time
|
15
23
|
# Time.stamp is 52 bytes, so we have 12 bytes of entropy left over
|
16
|
-
int = (Time.stamp << 12) + rand(2**12)
|
24
|
+
int = ((bytes || Time).stamp << 12) + rand(2**12)
|
17
25
|
@bytes = [int >> 32, int % 2**32].pack("NN")
|
18
26
|
else
|
19
27
|
raise TypeError, "Can't convert from #{bytes.class}"
|
@@ -23,11 +31,15 @@ class Cassandra
|
|
23
31
|
def to_i
|
24
32
|
@to_i ||= begin
|
25
33
|
ints = @bytes.unpack("NN")
|
26
|
-
(ints[0] << 32) +
|
34
|
+
(ints[0] << 32) +
|
27
35
|
ints[1]
|
28
36
|
end
|
29
|
-
end
|
37
|
+
end
|
30
38
|
|
39
|
+
def to_guid
|
40
|
+
"%08x-%04x-%04x" % @bytes.unpack("Nnn")
|
41
|
+
end
|
42
|
+
|
31
43
|
def inspect
|
32
44
|
"<Cassandra::Long##{object_id} time: #{
|
33
45
|
Time.at((to_i >> 12) / 1_000_000).inspect
|
@@ -35,7 +47,9 @@ class Cassandra
|
|
35
47
|
(to_i >> 12) % 1_000_000
|
36
48
|
}, jitter: #{
|
37
49
|
to_i % 2**12
|
50
|
+
}, guid: #{
|
51
|
+
to_guid
|
38
52
|
}>"
|
39
|
-
end
|
40
|
-
end
|
53
|
+
end
|
54
|
+
end
|
41
55
|
end
|
data/lib/cassandra/protocol.rb
CHANGED
@@ -4,20 +4,8 @@ class Cassandra
|
|
4
4
|
module Protocol #:nodoc:
|
5
5
|
private
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
when CassandraThrift::BatchMutationSuper then @client.batch_insert_super_column(@keyspace, mutation, consistency)
|
10
|
-
when CassandraThrift::BatchMutation then @client.batch_insert(@keyspace, mutation, consistency)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def _remove(column_family, key, column, sub_column, consistency, timestamp)
|
15
|
-
column_path_or_parent = if is_super(column_family)
|
16
|
-
CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
|
17
|
-
else
|
18
|
-
CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
|
19
|
-
end
|
20
|
-
@client.remove(@keyspace, key, column_path_or_parent, timestamp, consistency)
|
7
|
+
def _mutate(mutation, consistency)
|
8
|
+
@client.batch_mutate(@keyspace, mutation, consistency)
|
21
9
|
end
|
22
10
|
|
23
11
|
def _count_columns(column_family, key, super_column, consistency)
|
data/lib/cassandra/uuid.rb
CHANGED
@@ -8,45 +8,45 @@ class Cassandra
|
|
8
8
|
end
|
9
9
|
|
10
10
|
GREGORIAN_EPOCH_OFFSET = 0x01B2_1DD2_1381_4000 # Oct 15, 1582
|
11
|
-
|
11
|
+
|
12
12
|
VARIANT = 0b1000_0000_0000_0000
|
13
13
|
|
14
14
|
def initialize(bytes = nil)
|
15
15
|
case bytes
|
16
16
|
when String
|
17
17
|
case bytes.size
|
18
|
-
when 16
|
19
|
-
@bytes = bytes
|
20
|
-
when 36
|
18
|
+
when 16 # Raw byte array
|
19
|
+
@bytes = bytes
|
20
|
+
when 36 # Human-readable UUID representation; inverse of #to_guid
|
21
21
|
elements = bytes.split("-")
|
22
22
|
raise TypeError, "Malformed UUID representation" if elements.size != 5
|
23
|
-
@bytes = elements.join.to_a.pack('H32')
|
23
|
+
@bytes = elements.join.to_a.pack('H32')
|
24
24
|
else
|
25
25
|
raise TypeError, "16 bytes required for byte array, or 36 characters required for UUID representation"
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
when Integer
|
29
29
|
raise TypeError, "Integer must be between 0 and 2**128" if bytes < 0 or bytes > 2**128
|
30
30
|
@bytes = [
|
31
|
-
(bytes >> 96) & 0xFFFF_FFFF,
|
32
|
-
(bytes >> 64) & 0xFFFF_FFFF,
|
33
|
-
(bytes >> 32) & 0xFFFF_FFFF,
|
31
|
+
(bytes >> 96) & 0xFFFF_FFFF,
|
32
|
+
(bytes >> 64) & 0xFFFF_FFFF,
|
33
|
+
(bytes >> 32) & 0xFFFF_FFFF,
|
34
34
|
bytes & 0xFFFF_FFFF
|
35
35
|
].pack("NNNN")
|
36
|
-
|
36
|
+
|
37
37
|
when NilClass, Time
|
38
38
|
time = (bytes || Time).stamp * 10 + GREGORIAN_EPOCH_OFFSET
|
39
39
|
# See http://github.com/spectra/ruby-uuid/
|
40
40
|
@bytes = [
|
41
|
-
time & 0xFFFF_FFFF,
|
42
|
-
time >> 32,
|
43
|
-
((time >> 48) & 0x0FFF) | 0x1000,
|
44
|
-
# Top 3 bytes reserved
|
41
|
+
time & 0xFFFF_FFFF,
|
42
|
+
time >> 32,
|
43
|
+
((time >> 48) & 0x0FFF) | 0x1000,
|
44
|
+
# Top 3 bytes reserved
|
45
45
|
rand(2**13) | VARIANT,
|
46
46
|
rand(2**16),
|
47
47
|
rand(2**32)
|
48
48
|
].pack("NnnnnN")
|
49
|
-
|
49
|
+
|
50
50
|
else
|
51
51
|
raise TypeError, "Can't convert from #{bytes.class}"
|
52
52
|
end
|
@@ -54,9 +54,9 @@ class Cassandra
|
|
54
54
|
|
55
55
|
def to_i
|
56
56
|
ints = @bytes.unpack("NNNN")
|
57
|
-
(ints[0] << 96) +
|
58
|
-
(ints[1] << 64) +
|
59
|
-
(ints[2] << 32) +
|
57
|
+
(ints[0] << 96) +
|
58
|
+
(ints[1] << 64) +
|
59
|
+
(ints[2] << 32) +
|
60
60
|
ints[3]
|
61
61
|
end
|
62
62
|
|
@@ -65,7 +65,7 @@ class Cassandra
|
|
65
65
|
version = (time_high & 0xF000).to_s(16)[0].chr.to_i
|
66
66
|
version > 0 and version < 6 ? version : -1
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def variant
|
70
70
|
@bytes.unpack('QnnN')[1] >> 13
|
71
71
|
end
|
@@ -76,19 +76,19 @@ class Cassandra
|
|
76
76
|
elements[-1] = '%02x%02x%02x%02x%02x%02x' % node
|
77
77
|
"%08x-%04x-%04x-%02x%02x-%s" % elements
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
def seconds
|
81
81
|
total_usecs / 1_000_000
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
def usecs
|
85
|
-
total_usecs % 1_000_000
|
85
|
+
total_usecs % 1_000_000
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
def <=>(other)
|
89
89
|
total_usecs <=> other.send(:total_usecs)
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def inspect(long = false)
|
93
93
|
"<Cassandra::UUID##{object_id} time: #{
|
94
94
|
Time.at(seconds).inspect
|
@@ -97,13 +97,13 @@ class Cassandra
|
|
97
97
|
} jitter: #{
|
98
98
|
@bytes.unpack('QQ')[1]
|
99
99
|
}" + (long ? ", version: #{version}, variant: #{variant}, guid: #{to_guid}>" : ">")
|
100
|
-
end
|
101
|
-
|
100
|
+
end
|
101
|
+
|
102
102
|
private
|
103
|
-
|
103
|
+
|
104
104
|
def total_usecs
|
105
105
|
elements = @bytes.unpack("NnnQ")
|
106
|
-
(elements[0] + (elements[1] << 32) + ((elements[2] & 0x0FFF) << 48) - GREGORIAN_EPOCH_OFFSET) / 10
|
106
|
+
(elements[0] + (elements[1] << 32) + ((elements[2] & 0x0FFF) << 48) - GREGORIAN_EPOCH_OFFSET) / 10
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
data/test/cassandra_test.rb
CHANGED
@@ -6,9 +6,15 @@ class CassandraTest < Test::Unit::TestCase
|
|
6
6
|
def setup
|
7
7
|
@twitter = Cassandra.new('Twitter', '127.0.0.1')
|
8
8
|
@twitter.clear_keyspace!
|
9
|
+
|
9
10
|
@blogs = Cassandra.new('Multiblog', '127.0.0.1')
|
10
11
|
@blogs.clear_keyspace!
|
11
|
-
|
12
|
+
|
13
|
+
@blogs_long = Cassandra.new('MultiblogLong', '127.0.0.1')
|
14
|
+
@blogs_long.clear_keyspace!
|
15
|
+
|
16
|
+
@uuids = (0..6).map {|i| UUID.new(Time.at(2**(24+i))) }
|
17
|
+
@longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
|
12
18
|
end
|
13
19
|
|
14
20
|
def test_inspect
|
@@ -19,21 +25,21 @@ class CassandraTest < Test::Unit::TestCase
|
|
19
25
|
end
|
20
26
|
|
21
27
|
def test_connection_reopens
|
22
|
-
assert_raises(
|
23
|
-
@twitter.
|
28
|
+
assert_raises(Thrift::ProtocolException) do
|
29
|
+
@twitter.send("_mutate", [], -5)
|
24
30
|
end
|
25
31
|
assert_nothing_raised do
|
26
32
|
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
30
|
-
def
|
36
|
+
def test_get_key
|
31
37
|
@twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
|
32
38
|
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
|
33
39
|
assert_equal({}, @twitter.get(:Users, 'bogus'))
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
42
|
+
def test_get_key_preserving_order
|
37
43
|
# In-order hash is preserved
|
38
44
|
hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
|
39
45
|
@twitter.insert(:Users, key, hash)
|
@@ -48,17 +54,38 @@ class CassandraTest < Test::Unit::TestCase
|
|
48
54
|
assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
|
49
55
|
end
|
50
56
|
|
51
|
-
def
|
52
|
-
@blogs.insert(:Blogs, key,
|
53
|
-
|
57
|
+
def test_get_first_time_uuid_column
|
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({@uuids[0] => 'I like this cat'}, @blogs.get(:Blogs, key, :count => 1))
|
62
|
+
assert_equal({@uuids[2] => 'I disagree'}, @blogs.get(:Blogs, key, :count => 1, :reversed => true))
|
54
63
|
assert_equal({}, @blogs.get(:Blogs, 'bogus'))
|
55
64
|
end
|
56
65
|
|
66
|
+
def test_get_first_long_column
|
67
|
+
@blogs_long.insert(:Blogs, key,
|
68
|
+
{@longs[0] => 'I like this cat', @longs[1] => 'Buttons is cuter', @longs[2] => 'I disagree'})
|
69
|
+
|
70
|
+
assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
|
71
|
+
assert_equal({@longs[2] => 'I disagree'}, @blogs_long.get(:Blogs, key, :count => 1, :reversed => true))
|
72
|
+
assert_equal({}, @blogs_long.get(:Blogs, 'bogus'))
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_long_remove_bug
|
76
|
+
@blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
|
77
|
+
@blogs_long.remove(:Blogs, key)
|
78
|
+
assert_equal({}, @blogs_long.get(:Blogs, key, :count => 1))
|
79
|
+
|
80
|
+
@blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
|
81
|
+
assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
|
82
|
+
end
|
83
|
+
|
57
84
|
def test_get_with_count
|
58
85
|
@twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
|
59
86
|
assert_equal 1, @twitter.get(:Statuses, key, :count => 1).size
|
60
87
|
assert_equal 2, @twitter.get(:Statuses, key, :count => 2).size
|
61
|
-
end
|
88
|
+
end
|
62
89
|
|
63
90
|
def test_get_value
|
64
91
|
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
@@ -68,7 +95,7 @@ class CassandraTest < Test::Unit::TestCase
|
|
68
95
|
assert @twitter.exists?(:Statuses, key, 'body')
|
69
96
|
assert_nil @twitter.exists?(:Statuses, 'bogus', 'body')
|
70
97
|
end
|
71
|
-
|
98
|
+
|
72
99
|
def test_get_super_key
|
73
100
|
columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
|
74
101
|
@twitter.insert(:StatusRelationships, key, columns)
|
@@ -87,25 +114,25 @@ class CassandraTest < Test::Unit::TestCase
|
|
87
114
|
end
|
88
115
|
|
89
116
|
def test_get_super_sub_keys_with_count
|
90
|
-
@twitter.insert(:StatusRelationships, key,
|
117
|
+
@twitter.insert(:StatusRelationships, key,
|
91
118
|
{'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2', @uuids[3] => 'v3'}})
|
92
|
-
assert_equal({@uuids[1] => 'v1'},
|
119
|
+
assert_equal({@uuids[1] => 'v1'},
|
93
120
|
@twitter.get(:StatusRelationships, key, "user_timelines", :count => 1))
|
94
|
-
assert_equal({@uuids[3] => 'v3'},
|
121
|
+
assert_equal({@uuids[3] => 'v3'},
|
95
122
|
@twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true))
|
96
123
|
end
|
97
124
|
|
98
|
-
def test_get_super_sub_keys_with_ranges
|
99
|
-
@twitter.insert(:StatusRelationships, key,
|
125
|
+
def test_get_super_sub_keys_with_ranges
|
126
|
+
@twitter.insert(:StatusRelationships, key,
|
100
127
|
{'user_timelines' => {
|
101
|
-
@uuids[1] => 'v1',
|
102
|
-
@uuids[2] => 'v2',
|
128
|
+
@uuids[1] => 'v1',
|
129
|
+
@uuids[2] => 'v2',
|
103
130
|
@uuids[3] => 'v3',
|
104
|
-
@uuids[4] => 'v4',
|
131
|
+
@uuids[4] => 'v4',
|
105
132
|
@uuids[5] => 'v5'}})
|
106
133
|
|
107
134
|
keys = @twitter.get(:StatusRelationships, key, "user_timelines").keys
|
108
|
-
assert_equal keys.sort, keys
|
135
|
+
assert_equal keys.sort, keys
|
109
136
|
assert_equal({@uuids[1] => 'v1'}, @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1))
|
110
137
|
assert_equal({@uuids[2] => 'v2'}, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1))
|
111
138
|
assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).size
|
@@ -222,15 +249,6 @@ class CassandraTest < Test::Unit::TestCase
|
|
222
249
|
@twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']))
|
223
250
|
end
|
224
251
|
|
225
|
-
# Not supported
|
226
|
-
# def test_get_columns_super_sub
|
227
|
-
# @twitter.insert(:StatusRelationships, key, {
|
228
|
-
# 'user_timelines' => {@uuids[1] => 'v1'},
|
229
|
-
# 'mentions_timelines' => {@uuids[2] => 'v2'}})
|
230
|
-
# assert_equal ['v1', 'v2'],
|
231
|
-
# @twitter.get_columns(:StatusRelationships, key, 'user_timelines', ['1', key])
|
232
|
-
# end
|
233
|
-
|
234
252
|
def test_count_keys
|
235
253
|
@twitter.insert(:Statuses, key + "1", {'body' => '1'})
|
236
254
|
@twitter.insert(:Statuses, key + "2", {'body' => '2'})
|
@@ -266,7 +284,7 @@ class CassandraTest < Test::Unit::TestCase
|
|
266
284
|
@twitter.multi_count_columns(:Users, [key + '2', 'bogus', key + '1']))
|
267
285
|
end
|
268
286
|
|
269
|
-
def
|
287
|
+
def test_batch_mutate
|
270
288
|
@twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
|
271
289
|
|
272
290
|
@twitter.batch do
|
data/vendor/gen-rb/cassandra.rb
CHANGED
@@ -78,17 +78,17 @@ require 'cassandra_types'
|
|
78
78
|
return
|
79
79
|
end
|
80
80
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
81
|
+
def batch_mutate(keyspace, batch_mutations, consistency_level)
|
82
|
+
send_batch_mutate(keyspace, batch_mutations, consistency_level)
|
83
|
+
recv_batch_mutate()
|
84
84
|
end
|
85
85
|
|
86
|
-
def
|
87
|
-
send_message('
|
86
|
+
def send_batch_mutate(keyspace, batch_mutations, consistency_level)
|
87
|
+
send_message('batch_mutate', Batch_mutate_args, :keyspace => keyspace, :batch_mutations => batch_mutations, :consistency_level => consistency_level)
|
88
88
|
end
|
89
89
|
|
90
|
-
def
|
91
|
-
result = receive_message(
|
90
|
+
def recv_batch_mutate()
|
91
|
+
result = receive_message(Batch_mutate_result)
|
92
92
|
raise result.ire unless result.ire.nil?
|
93
93
|
raise result.ue unless result.ue.nil?
|
94
94
|
return
|
@@ -110,22 +110,6 @@ require 'cassandra_types'
|
|
110
110
|
return
|
111
111
|
end
|
112
112
|
|
113
|
-
def batch_insert_super_column(keyspace, batch_mutation_super, consistency_level)
|
114
|
-
send_batch_insert_super_column(keyspace, batch_mutation_super, consistency_level)
|
115
|
-
recv_batch_insert_super_column()
|
116
|
-
end
|
117
|
-
|
118
|
-
def send_batch_insert_super_column(keyspace, batch_mutation_super, consistency_level)
|
119
|
-
send_message('batch_insert_super_column', Batch_insert_super_column_args, :keyspace => keyspace, :batch_mutation_super => batch_mutation_super, :consistency_level => consistency_level)
|
120
|
-
end
|
121
|
-
|
122
|
-
def recv_batch_insert_super_column()
|
123
|
-
result = receive_message(Batch_insert_super_column_result)
|
124
|
-
raise result.ire unless result.ire.nil?
|
125
|
-
raise result.ue unless result.ue.nil?
|
126
|
-
return
|
127
|
-
end
|
128
|
-
|
129
113
|
def get_key_range(keyspace, column_family, start, finish, count)
|
130
114
|
send_get_key_range(keyspace, column_family, start, finish, count)
|
131
115
|
return recv_get_key_range()
|
@@ -243,17 +227,17 @@ require 'cassandra_types'
|
|
243
227
|
write_result(result, oprot, 'insert', seqid)
|
244
228
|
end
|
245
229
|
|
246
|
-
def
|
247
|
-
args = read_args(iprot,
|
248
|
-
result =
|
230
|
+
def process_batch_mutate(seqid, iprot, oprot)
|
231
|
+
args = read_args(iprot, Batch_mutate_args)
|
232
|
+
result = Batch_mutate_result.new()
|
249
233
|
begin
|
250
|
-
@handler.
|
234
|
+
@handler.batch_mutate(args.keyspace, args.batch_mutations, args.consistency_level)
|
251
235
|
rescue CassandraThrift::InvalidRequestException => ire
|
252
236
|
result.ire = ire
|
253
237
|
rescue CassandraThrift::UnavailableException => ue
|
254
238
|
result.ue = ue
|
255
239
|
end
|
256
|
-
write_result(result, oprot, '
|
240
|
+
write_result(result, oprot, 'batch_mutate', seqid)
|
257
241
|
end
|
258
242
|
|
259
243
|
def process_remove(seqid, iprot, oprot)
|
@@ -269,19 +253,6 @@ require 'cassandra_types'
|
|
269
253
|
write_result(result, oprot, 'remove', seqid)
|
270
254
|
end
|
271
255
|
|
272
|
-
def process_batch_insert_super_column(seqid, iprot, oprot)
|
273
|
-
args = read_args(iprot, Batch_insert_super_column_args)
|
274
|
-
result = Batch_insert_super_column_result.new()
|
275
|
-
begin
|
276
|
-
@handler.batch_insert_super_column(args.keyspace, args.batch_mutation_super, args.consistency_level)
|
277
|
-
rescue CassandraThrift::InvalidRequestException => ire
|
278
|
-
result.ire = ire
|
279
|
-
rescue CassandraThrift::UnavailableException => ue
|
280
|
-
result.ue = ue
|
281
|
-
end
|
282
|
-
write_result(result, oprot, 'batch_insert_super_column', seqid)
|
283
|
-
end
|
284
|
-
|
285
256
|
def process_get_key_range(seqid, iprot, oprot)
|
286
257
|
args = read_args(iprot, Get_key_range_args)
|
287
258
|
result = Get_key_range_result.new()
|
@@ -504,16 +475,16 @@ require 'cassandra_types'
|
|
504
475
|
|
505
476
|
end
|
506
477
|
|
507
|
-
class
|
478
|
+
class Batch_mutate_args
|
508
479
|
include ::Thrift::Struct
|
509
480
|
KEYSPACE = 1
|
510
|
-
|
481
|
+
BATCH_MUTATIONS = 2
|
511
482
|
CONSISTENCY_LEVEL = 3
|
512
483
|
|
513
|
-
::Thrift::Struct.field_accessor self, :keyspace, :
|
484
|
+
::Thrift::Struct.field_accessor self, :keyspace, :batch_mutations, :consistency_level
|
514
485
|
FIELDS = {
|
515
486
|
KEYSPACE => {:type => ::Thrift::Types::STRING, :name => 'keyspace'},
|
516
|
-
|
487
|
+
BATCH_MUTATIONS => {:type => ::Thrift::Types::LIST, :name => 'batch_mutations', :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::BatchMutation}},
|
517
488
|
CONSISTENCY_LEVEL => {:type => ::Thrift::Types::I32, :name => 'consistency_level', :default => 0, :enum_class => CassandraThrift::ConsistencyLevel}
|
518
489
|
}
|
519
490
|
|
@@ -527,7 +498,7 @@ require 'cassandra_types'
|
|
527
498
|
|
528
499
|
end
|
529
500
|
|
530
|
-
class
|
501
|
+
class Batch_mutate_result
|
531
502
|
include ::Thrift::Struct
|
532
503
|
IRE = 1
|
533
504
|
UE = 2
|
@@ -590,47 +561,6 @@ require 'cassandra_types'
|
|
590
561
|
|
591
562
|
end
|
592
563
|
|
593
|
-
class Batch_insert_super_column_args
|
594
|
-
include ::Thrift::Struct
|
595
|
-
KEYSPACE = 1
|
596
|
-
BATCH_MUTATION_SUPER = 2
|
597
|
-
CONSISTENCY_LEVEL = 3
|
598
|
-
|
599
|
-
::Thrift::Struct.field_accessor self, :keyspace, :batch_mutation_super, :consistency_level
|
600
|
-
FIELDS = {
|
601
|
-
KEYSPACE => {:type => ::Thrift::Types::STRING, :name => 'keyspace'},
|
602
|
-
BATCH_MUTATION_SUPER => {:type => ::Thrift::Types::STRUCT, :name => 'batch_mutation_super', :class => CassandraThrift::BatchMutationSuper},
|
603
|
-
CONSISTENCY_LEVEL => {:type => ::Thrift::Types::I32, :name => 'consistency_level', :default => 0, :enum_class => CassandraThrift::ConsistencyLevel}
|
604
|
-
}
|
605
|
-
|
606
|
-
def struct_fields; FIELDS; end
|
607
|
-
|
608
|
-
def validate
|
609
|
-
unless @consistency_level.nil? || CassandraThrift::ConsistencyLevel::VALID_VALUES.include?(@consistency_level)
|
610
|
-
raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field consistency_level!')
|
611
|
-
end
|
612
|
-
end
|
613
|
-
|
614
|
-
end
|
615
|
-
|
616
|
-
class Batch_insert_super_column_result
|
617
|
-
include ::Thrift::Struct
|
618
|
-
IRE = 1
|
619
|
-
UE = 2
|
620
|
-
|
621
|
-
::Thrift::Struct.field_accessor self, :ire, :ue
|
622
|
-
FIELDS = {
|
623
|
-
IRE => {:type => ::Thrift::Types::STRUCT, :name => 'ire', :class => CassandraThrift::InvalidRequestException},
|
624
|
-
UE => {:type => ::Thrift::Types::STRUCT, :name => 'ue', :class => CassandraThrift::UnavailableException}
|
625
|
-
}
|
626
|
-
|
627
|
-
def struct_fields; FIELDS; end
|
628
|
-
|
629
|
-
def validate
|
630
|
-
end
|
631
|
-
|
632
|
-
end
|
633
|
-
|
634
564
|
class Get_key_range_args
|
635
565
|
include ::Thrift::Struct
|
636
566
|
KEYSPACE = 1
|
@@ -35,24 +35,6 @@ module CassandraThrift
|
|
35
35
|
|
36
36
|
end
|
37
37
|
|
38
|
-
class BatchMutation
|
39
|
-
include ::Thrift::Struct
|
40
|
-
KEY = 1
|
41
|
-
CFMAP = 2
|
42
|
-
|
43
|
-
::Thrift::Struct.field_accessor self, :key, :cfmap
|
44
|
-
FIELDS = {
|
45
|
-
KEY => {:type => ::Thrift::Types::STRING, :name => 'key'},
|
46
|
-
CFMAP => {:type => ::Thrift::Types::MAP, :name => 'cfmap', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::LIST, :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::Column}}}
|
47
|
-
}
|
48
|
-
|
49
|
-
def struct_fields; FIELDS; end
|
50
|
-
|
51
|
-
def validate
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
38
|
class SuperColumn
|
57
39
|
include ::Thrift::Struct
|
58
40
|
NAME = 1
|
@@ -71,15 +53,15 @@ module CassandraThrift
|
|
71
53
|
|
72
54
|
end
|
73
55
|
|
74
|
-
class
|
56
|
+
class ColumnOrSuperColumn
|
75
57
|
include ::Thrift::Struct
|
76
|
-
|
77
|
-
|
58
|
+
COLUMN = 1
|
59
|
+
SUPER_COLUMN = 2
|
78
60
|
|
79
|
-
::Thrift::Struct.field_accessor self, :
|
61
|
+
::Thrift::Struct.field_accessor self, :column, :super_column
|
80
62
|
FIELDS = {
|
81
|
-
|
82
|
-
|
63
|
+
COLUMN => {:type => ::Thrift::Types::STRUCT, :name => 'column', :class => CassandraThrift::Column, :optional => true},
|
64
|
+
SUPER_COLUMN => {:type => ::Thrift::Types::STRUCT, :name => 'super_column', :class => CassandraThrift::SuperColumn, :optional => true}
|
83
65
|
}
|
84
66
|
|
85
67
|
def struct_fields; FIELDS; end
|
@@ -163,12 +145,14 @@ module CassandraThrift
|
|
163
145
|
COLUMN_FAMILY = 3
|
164
146
|
SUPER_COLUMN = 4
|
165
147
|
COLUMN = 5
|
148
|
+
TIMESTAMP = 6
|
166
149
|
|
167
|
-
::Thrift::Struct.field_accessor self, :column_family, :super_column, :column
|
150
|
+
::Thrift::Struct.field_accessor self, :column_family, :super_column, :column, :timestamp
|
168
151
|
FIELDS = {
|
169
152
|
COLUMN_FAMILY => {:type => ::Thrift::Types::STRING, :name => 'column_family'},
|
170
153
|
SUPER_COLUMN => {:type => ::Thrift::Types::STRING, :name => 'super_column', :optional => true},
|
171
|
-
COLUMN => {:type => ::Thrift::Types::STRING, :name => 'column', :optional => true}
|
154
|
+
COLUMN => {:type => ::Thrift::Types::STRING, :name => 'column', :optional => true},
|
155
|
+
TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp', :optional => true}
|
172
156
|
}
|
173
157
|
|
174
158
|
def struct_fields; FIELDS; end
|
@@ -218,15 +202,17 @@ module CassandraThrift
|
|
218
202
|
|
219
203
|
end
|
220
204
|
|
221
|
-
class
|
205
|
+
class BatchMutation
|
222
206
|
include ::Thrift::Struct
|
223
|
-
|
224
|
-
|
207
|
+
KEY = 1
|
208
|
+
CFMAP = 2
|
209
|
+
COLUMN_PATHS = 3
|
225
210
|
|
226
|
-
::Thrift::Struct.field_accessor self, :
|
211
|
+
::Thrift::Struct.field_accessor self, :key, :cfmap, :column_paths
|
227
212
|
FIELDS = {
|
228
|
-
|
229
|
-
|
213
|
+
KEY => {:type => ::Thrift::Types::STRING, :name => 'key'},
|
214
|
+
CFMAP => {:type => ::Thrift::Types::MAP, :name => 'cfmap', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::LIST, :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::ColumnOrSuperColumn}}},
|
215
|
+
COLUMN_PATHS => {:type => ::Thrift::Types::LIST, :name => 'column_paths', :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::ColumnPath}}
|
230
216
|
}
|
231
217
|
|
232
218
|
def struct_fields; FIELDS; end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassandra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Weaver
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2009-08-
|
33
|
+
date: 2009-08-27 00:00:00 -07:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -67,6 +67,7 @@ extra_rdoc_files:
|
|
67
67
|
- lib/cassandra/columns.rb
|
68
68
|
- lib/cassandra/comparable.rb
|
69
69
|
- lib/cassandra/constants.rb
|
70
|
+
- lib/cassandra/debug.rb
|
70
71
|
- lib/cassandra/long.rb
|
71
72
|
- lib/cassandra/ordered_hash.rb
|
72
73
|
- lib/cassandra/protocol.rb
|
@@ -87,6 +88,7 @@ files:
|
|
87
88
|
- lib/cassandra/columns.rb
|
88
89
|
- lib/cassandra/comparable.rb
|
89
90
|
- lib/cassandra/constants.rb
|
91
|
+
- lib/cassandra/debug.rb
|
90
92
|
- lib/cassandra/long.rb
|
91
93
|
- lib/cassandra/ordered_hash.rb
|
92
94
|
- lib/cassandra/protocol.rb
|
metadata.gz.sig
CHANGED
Binary file
|