couchbase 0.9.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +8 -0
  2. data/.yardopts +5 -0
  3. data/HISTORY.markdown +14 -10
  4. data/README.markdown +293 -98
  5. data/Rakefile +19 -24
  6. data/couchbase.gemspec +25 -7
  7. data/ext/couchbase_ext/couchbase_ext.c +2332 -0
  8. data/ext/couchbase_ext/extconf.rb +102 -0
  9. data/lib/couchbase.rb +20 -30
  10. data/lib/couchbase/bucket.rb +43 -112
  11. data/lib/couchbase/version.rb +3 -2
  12. data/tasks/benchmark.rake +6 -0
  13. data/tasks/compile.rake +52 -0
  14. data/tasks/doc.rake +27 -0
  15. data/tasks/test.rake +94 -0
  16. data/tasks/util.rake +21 -0
  17. data/test/profile/.gitignore +1 -0
  18. data/test/profile/Gemfile +6 -0
  19. data/test/profile/benchmark.rb +195 -0
  20. data/test/setup.rb +107 -18
  21. data/test/test_arithmetic.rb +98 -0
  22. data/test/test_async.rb +211 -0
  23. data/test/test_bucket.rb +126 -23
  24. data/test/test_cas.rb +59 -0
  25. data/test/test_couchbase.rb +22 -3
  26. data/test/test_delete.rb +63 -0
  27. data/test/test_errors.rb +82 -0
  28. data/test/test_flush.rb +49 -0
  29. data/test/test_format.rb +98 -0
  30. data/test/test_get.rb +236 -0
  31. data/test/test_stats.rb +53 -0
  32. data/test/test_store.rb +186 -0
  33. data/test/test_touch.rb +57 -0
  34. data/test/test_version.rb +17 -0
  35. metadata +72 -58
  36. data/lib/couchbase/couchdb.rb +0 -107
  37. data/lib/couchbase/document.rb +0 -71
  38. data/lib/couchbase/http_status.rb +0 -118
  39. data/lib/couchbase/latch.rb +0 -71
  40. data/lib/couchbase/memcached.rb +0 -372
  41. data/lib/couchbase/node.rb +0 -49
  42. data/lib/couchbase/rest_client.rb +0 -124
  43. data/lib/couchbase/view.rb +0 -182
  44. data/test/support/buckets-config.json +0 -843
  45. data/test/support/sample_design_doc.json +0 -9
  46. data/test/test_couchdb.rb +0 -98
  47. data/test/test_document.rb +0 -11
  48. data/test/test_latch.rb +0 -88
  49. data/test/test_memcached.rb +0 -59
  50. data/test/test_rest_client.rb +0 -14
  51. data/test/test_view.rb +0 -98
@@ -0,0 +1,94 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'rake/testtask'
19
+ require 'rake/clean'
20
+
21
+ rule 'test/CouchbaseMock.jar' do |task|
22
+ download_uri = "http://files.couchbase.com/maven2/org/couchbase/mock/CouchbaseMock/0.5-SNAPSHOT/CouchbaseMock-0.5-20120103.162550-11.jar"
23
+ sh %{wget -q -O test/CouchbaseMock.jar #{download_uri}}
24
+ end
25
+
26
+ CLOBBER << 'test/CouchbaseMock.jar'
27
+
28
+ module FileUtils
29
+ alias :orig_ruby :ruby
30
+
31
+ def ruby(*args, &block)
32
+ executable = [ENV['RUBY_PREFIX'], RUBY].flatten.compact.join(' ')
33
+ options = (Hash === args.last) ? args.pop : {}
34
+ if args.length > 1 then
35
+ sh(*([executable] + args + [options]), &block)
36
+ else
37
+ sh("#{executable} #{args.first}", options, &block)
38
+ end
39
+ end
40
+ end
41
+
42
+ Rake::TestTask.new do |test|
43
+ test.libs << "test" << "."
44
+ test.ruby_opts << "-rruby-debug" if ENV['DEBUG']
45
+ test.pattern = 'test/test_*.rb'
46
+ test.options = '--verbose'
47
+ end
48
+
49
+ Rake::Task['test'].prerequisites.unshift('test/CouchbaseMock.jar')
50
+
51
+ desc "Run the test suite under Valgrind."
52
+ task "test:valgrind" do
53
+ ENV['RUBY_PREFIX'] = [
54
+ "valgrind",
55
+ "--num-callers=50",
56
+ "--error-limit=no",
57
+ "--partial-loads-ok=yes",
58
+ "--undef-value-errors=no",
59
+ "--track-fds=yes",
60
+ "--leak-check=full",
61
+ "--leak-resolution=med",
62
+ ].join(' ')
63
+ Rake::Task['test'].invoke
64
+ end
65
+
66
+ desc "Run the test suite under Valgrind with memory-fill."
67
+ task "test:valgrind:mem_fill" do
68
+ ENV['RUBY_PREFIX'] = [
69
+ "valgrind",
70
+ "--num-callers=50",
71
+ "--error-limit=no",
72
+ "--partial-loads-ok=yes",
73
+ "--undef-value-errors=no",
74
+ "--freelist-vol=100000000",
75
+ "--malloc-fill=6D",
76
+ "--free-fill=66",
77
+ ].join(' ')
78
+ Rake::Task['test'].invoke
79
+ end
80
+
81
+ desc "Run the test suite under Valgrind with memory-zero."
82
+ task "test:valgrind:mem_zero" do
83
+ ENV['RUBY_PREFIX'] = [
84
+ "valgrind",
85
+ "--num-callers=50",
86
+ "--error-limit=no",
87
+ "--partial-loads-ok=yes",
88
+ "--undef-value-errors=no",
89
+ "--freelist-vol=100000000",
90
+ "--malloc-fill=00",
91
+ "--free-fill=00",
92
+ ].join(' ')
93
+ Rake::Task['test'].invoke
94
+ end
@@ -0,0 +1,21 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ desc 'Start an irb session and load the library.'
19
+ task :console => :compile do
20
+ exec "irb -I lib -rruby-debug -rcouchbase"
21
+ end
@@ -0,0 +1 @@
1
+ benchmark*.log
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gem "memcached", "~> 1.3.6"
4
+ gem "kgio", "~> 2.7.2"
5
+ gem "dalli", "~> 1.1.4"
6
+ gem "yajl-ruby", "~> 1.1.0"
@@ -0,0 +1,195 @@
1
+ # Useful environment variables:
2
+ #
3
+ # LOOPS (50000)
4
+ # how many time run exercises
5
+ #
6
+ # HOST (127.0.0.1)
7
+ # the host where cluster is running. benchmark will use default ports to
8
+ # connect to it (11211 and 8091)
9
+ #
10
+ # STACK_DEPTH (0)
11
+ # the depth of stack where exercises are run. the benchmark will
12
+ # recursively go to given depth before run
13
+ #
14
+ # TEST ('')
15
+ # use to run specific test (possible values are: set, get, get-multi,
16
+ # append, prepend, delete, get-missing, append-missing, prepend-missing,
17
+ # set-large, get-large)
18
+ #
19
+ # CLIENT ('')
20
+ # use to run with specific client (possible values are: couchbase, dalli,
21
+ # memcached, memcached:buffer)
22
+ #
23
+ # DEBUG ('')
24
+ # show exceptions
25
+ #
26
+
27
+ require "rubygems"
28
+ require "bundler/setup"
29
+
30
+ require 'benchmark'
31
+
32
+ $LOAD_PATH << File.join(File.dirname(__FILE__), "..", "..", "lib")
33
+ require 'couchbase'
34
+ require 'memcached'
35
+ require 'dalli'
36
+
37
+ puts `uname -a`
38
+ puts File.readlines('/proc/cpuinfo').sort.uniq.grep(/model name|cpu cores/) rescue nil
39
+ puts RUBY_DESCRIPTION
40
+
41
+ class Bench
42
+
43
+ def initialize(loops = nil, stack_depth = nil)
44
+ @loops = (loops || 50000).to_i
45
+ @stack_depth = (stack_depth || 0).to_i
46
+
47
+ puts "PID is #{Process.pid}"
48
+ puts "Loops is #{@loops}"
49
+ puts "Stack depth is #{@stack_depth}"
50
+
51
+ @m_value = Marshal.dump(
52
+ @small_value = ["testing"])
53
+ @m_large_value = Marshal.dump(
54
+ @large_value = [{"test" => "1", "test2" => "2", Object.new => "3", 4 => 4, "test5" => 2**65}] * 2048)
55
+
56
+ puts "Small value size is: #{@m_value.size} bytes"
57
+ puts "Large value size is: #{@m_large_value.size} bytes"
58
+
59
+ @keys = [
60
+ @k1 = "Short",
61
+ @k2 = "Sym1-2-3::45" * 8,
62
+ @k3 = "Long" * 40,
63
+ @k4 = "Medium" * 8,
64
+ @k5 = "Medium2" * 8,
65
+ @k6 = "Long3" * 40]
66
+
67
+ reset_clients
68
+
69
+ Benchmark.bm(36) do |x|
70
+ @benchmark = x
71
+ end
72
+ end
73
+
74
+ def run(level = @stack_depth)
75
+ level > 0 ? run(level - 1) : run_without_recursion
76
+ end
77
+
78
+ private
79
+
80
+ def reset_clients
81
+ host = ENV['HOST'] || '127.0.0.1'
82
+ @clients = {
83
+ "dalli" => lambda { Dalli::Client.new("#{host}:11211", :marshal => true, :threadsafe => false) },
84
+ "memcached" => lambda { Memcached::Rails.new("#{host}:11211", :no_block => false, :buffer_requests => false, :binary_protocol => true) },
85
+ "memcached:buffer" => lambda { Memcached::Rails.new("#{host}:11211", :no_block => true, :buffer_requests => true, :binary_protocol => true) },
86
+ "couchbase" => lambda { Couchbase.new("http://#{host}:8091/pools/default/buckets/default", :default_format => :marshal) }
87
+ }
88
+ end
89
+
90
+ def benchmark_clients(test_name, populate_keys = true)
91
+ return if ENV["TEST"] and !test_name.include?(ENV["TEST"])
92
+
93
+ @clients.keys.each do |client_name|
94
+ next if ENV["CLIENT"] and !client_name.include?(ENV["CLIENT"])
95
+
96
+ kid = fork do
97
+ client = @clients[client_name].call
98
+ begin
99
+ if populate_keys
100
+ client.set @k1, @m_value
101
+ client.set @k2, @m_value
102
+ client.set @k3, @m_value
103
+ else
104
+ client.delete @k1
105
+ client.delete @k2
106
+ client.delete @k3
107
+ end
108
+
109
+ GC.disable
110
+ @benchmark.report("#{test_name}: #{client_name}") { @loops.times { yield client } }
111
+ STDOUT.flush
112
+ rescue Exception => e
113
+ puts "#{test_name}: #{client_name} => #{e.inspect}" if ENV["DEBUG"]
114
+ end
115
+ exit
116
+ end
117
+ Signal.trap("INT") { Process.kill("KILL", kid); exit }
118
+ Process.wait(kid)
119
+ end
120
+ puts
121
+ end
122
+
123
+ def run_without_recursion
124
+ benchmark_clients("set") do |c|
125
+ c.set @k1, @m_value
126
+ c.set @k2, @m_value
127
+ c.set @k3, @m_value
128
+ end
129
+
130
+ benchmark_clients("get") do |c|
131
+ c.get @k1
132
+ c.get @k2
133
+ c.get @k3
134
+ end
135
+
136
+ benchmark_clients("get_multi") do |c|
137
+ if c.respond_to?(:get_multi)
138
+ c.get_multi @keys
139
+ else
140
+ c.get @keys
141
+ end
142
+ end
143
+
144
+ benchmark_clients("append") do |c|
145
+ c.append @k1, @m_value
146
+ c.append @k2, @m_value
147
+ c.append @k3, @m_value
148
+ end
149
+
150
+ benchmark_clients("prepend") do |c|
151
+ c.prepend @k1, @m_value
152
+ c.prepend @k2, @m_value
153
+ c.prepend @k3, @m_value
154
+ end
155
+
156
+ benchmark_clients("delete") do |c|
157
+ c.delete @k1
158
+ c.delete @k2
159
+ c.delete @k3
160
+ end
161
+
162
+ benchmark_clients("get_missing", false) do |c|
163
+ c.get @k1 rescue nil
164
+ c.get @k2 rescue nil
165
+ c.get @k3 rescue nil
166
+ end
167
+
168
+ benchmark_clients("append_missing", false) do |c|
169
+ c.append @k1, @m_value rescue nil
170
+ c.append @k2, @m_value rescue nil
171
+ c.append @k3, @m_value rescue nil
172
+ end
173
+
174
+ benchmark_clients("prepend_missing", false) do |c|
175
+ c.prepend @k1, @m_value rescue nil
176
+ c.prepend @k2, @m_value rescue nil
177
+ c.prepend @k3, @m_value rescue nil
178
+ end
179
+
180
+ benchmark_clients("set_large") do |c|
181
+ c.set @k1, @m_large_value
182
+ c.set @k2, @m_large_value
183
+ c.set @k3, @m_large_value
184
+ end
185
+
186
+ benchmark_clients("get_large") do |c|
187
+ c.get @k1
188
+ c.get @k2
189
+ c.get @k3
190
+ end
191
+
192
+ end
193
+ end
194
+
195
+ Bench.new(ENV["LOOPS"], ENV["STACK_DEPTH"]).run
@@ -1,28 +1,117 @@
1
+ # Author:: Couchbase <info@couchbase.com>
2
+ # Copyright:: 2011, 2012 Couchbase, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
1
18
  require 'minitest/autorun'
2
- require 'mocha'
3
19
  require 'couchbase'
4
- require 'yajl'
5
20
 
6
- class MiniTest::Unit::TestCase
7
- # retry block +attempts+ times and fail if time is out
8
- def assert_operation_completed(attempts = 10)
9
- timeout = 1
10
- attempts.times do
11
- sleep(timeout)
12
- return if yield
13
- timeout *= 2
21
+ require 'socket'
22
+
23
+ class CouchbaseMock
24
+ Monitor = Struct.new(:pid, :client, :socket, :port)
25
+
26
+ attr_accessor :host, :port, :buckets_spec, :num_nodes, :num_vbuckets
27
+
28
+ def initialize(params = {})
29
+ @host = "127.0.0.1"
30
+ @port = 0
31
+ @num_nodes = 10
32
+ @num_vbuckets = 4096
33
+ @buckets_spec = "default:" # "default:,protected:secret,cache::memcache"
34
+ params.each do |key, value|
35
+ send("#{key}=", value)
36
+ end
37
+ yield self if block_given?
38
+ if @num_vbuckets < 1 || (@num_vbuckets & (@num_vbuckets - 1) != 0)
39
+ raise ArgumentError, "Number of vbuckets should be a power of two and greater than zero"
40
+ end
41
+ end
42
+
43
+ def start
44
+ @monitor = Monitor.new
45
+ @monitor.socket = TCPServer.new(nil, 0)
46
+ @monitor.socket.listen(10)
47
+ _, @monitor.port, _, _ = @monitor.socket.addr
48
+ trap("CLD") do
49
+ puts "CouchbaseMock.jar died unexpectedly during startup"
50
+ exit(1)
14
51
  end
15
- flunk "Time is out!"
52
+ @monitor.pid = fork
53
+ if @monitor.pid.nil?
54
+ rc = exec(command_line("--harakiri-monitor=:#{@monitor.port}"))
55
+ else
56
+ trap("CLD", "SIG_DFL")
57
+ @monitor.client, _ = @monitor.socket.accept
58
+ @port = @monitor.client.recv(100).to_i
59
+ end
60
+ end
61
+
62
+ def stop
63
+ @monitor.client.close
64
+ @monitor.socket.close
65
+ Process.kill("TERM", @monitor.pid)
66
+ Process.wait(@monitor.pid)
67
+ end
68
+
69
+ def failover_node(index, bucket = "default")
70
+ @monitor.client.send("failover,#{index},#{bucket}", 0)
16
71
  end
17
72
 
18
- # fetch list of databases and check if all vbuckets database ready
19
- def database_ready(bucket)
20
- all_dbs_uri = bucket.next_node.couch_api_base.sub(bucket.name, '_all_dbs')
21
- bucket.http_get(all_dbs_uri).grep(/#{bucket.name}\/\d+/).size == bucket.vbuckets.size
73
+ def respawn_node(index, bucket = "default")
74
+ @monitor.client.send("respawn,#{index},#{bucket}", 0)
75
+ end
76
+
77
+ protected
78
+
79
+ def command_line(extra = nil)
80
+ cmd = "java -jar #{File.dirname(__FILE__)}/CouchbaseMock.jar"
81
+ cmd << " --host #{@host}" if @host
82
+ cmd << " --port #{@port}" if @port
83
+ cmd << " --nodes #{@num_nodes}" if @num_nodes
84
+ cmd << " --vbuckets #{@num_vbuckets}" if @num_vbuckets
85
+ cmd << " --buckets #{@buckets_spec}" if @buckets_spec
86
+ cmd << " #{extra}"
87
+ cmd
88
+ end
89
+ end
90
+
91
+ class MiniTest::Unit::TestCase
92
+
93
+ def start_mock(params = {})
94
+ mock = CouchbaseMock.new(params)
95
+ mock.start
96
+ mock
97
+ end
98
+
99
+ def stop_mock(mock)
100
+ assert(mock)
101
+ mock.stop
102
+ end
103
+
104
+ def with_mock(params = {})
105
+ mock = nil
106
+ if block_given?
107
+ mock = start_mock(params)
108
+ yield mock
109
+ end
110
+ ensure
111
+ stop_mock(mock) if mock
22
112
  end
23
113
 
24
- def json_fixture(path, options = {})
25
- data = File.read(File.join(File.dirname(__FILE__), 'support', path))
26
- options[:raw] ? data : Yajl::Parser.parse(data)
114
+ def test_id(*suffixes)
115
+ [caller.first[/.*[` ](.*)'/, 1], suffixes].join("_")
27
116
  end
28
117
  end