couchbase 0.9.8 → 1.0.0

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.
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