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
data/.gitignore CHANGED
@@ -1,4 +1,12 @@
1
1
  *.gem
2
+ *.so
2
3
  .bundle
4
+ .yardoc
3
5
  Gemfile.lock
6
+ core*
7
+ doc/
8
+ ext/couchbase/Makefile
9
+ ext/couchbase/mkmf.log
4
10
  pkg/*
11
+ test/CouchbaseMock.jar
12
+ tmp
@@ -0,0 +1,5 @@
1
+ --protected
2
+ --no-private
3
+ -
4
+ README.markdown
5
+ HISTORY.markdown
@@ -1,15 +1,20 @@
1
- === 0.9.8 / 2011-12-16
1
+ ## 1.0.0 / 2011-12-23
2
+
3
+ * Implement all operations using libcouchbase as backend
4
+ * Remove views code. It will be re-added in 1.1 version
5
+
6
+ ## 0.9.8 / 2011-12-16
2
7
 
3
8
  * Fix RCBC-10: It was impossible to store data in non-default buckets
4
9
 
5
- === 0.9.7 / 2011-10-04
10
+ ## 0.9.7 / 2011-10-04
6
11
 
7
12
  * Fix design doc removing
8
13
  * Fix 'set' method signature: add missing options argument
9
14
  * Rename gem to 'couchbase' for easy of use. The github project still
10
15
  is 'couchbase-ruby-client'
11
16
 
12
- === 0.9.6 / 2011-10-04
17
+ ## 0.9.6 / 2011-10-04
13
18
 
14
19
  * Fix bug with decoding multiget result
15
20
  * Allow create design documents from IO and String
@@ -18,11 +23,11 @@
18
23
  * Remove dependency on libyajl library: it bundled with yaji now
19
24
  * Update rake tasks: create zip- and tar-balls
20
25
 
21
- === 0.9.5 / 2011-08-24
26
+ ## 0.9.5 / 2011-08-24
22
27
 
23
28
  * Update installation notes in README
24
29
 
25
- === 0.9.4 / 2011-08-24
30
+ ## 0.9.4 / 2011-08-24
26
31
 
27
32
  * Use streaming json parser to iterate over view results
28
33
  * Update memcached gem dependency to v1.3
@@ -32,23 +37,22 @@
32
37
  * Fix bug with unicode parsing in config listener
33
38
  * Add more unit tests
34
39
 
35
- === 0.9.3 / 2011-07-29
40
+ ## 0.9.3 / 2011-07-29
36
41
 
37
42
  * Use Latch (via Mutex and ConditionVariable) to wait until initial
38
43
  setup will be finished.
39
44
  * Update prefix for development views (from '$dev_' to 'dev_')
40
45
 
41
- === 0.9.2 / 2011-07-29
46
+ ## 0.9.2 / 2011-07-29
42
47
 
43
48
  * Use zero TTL by default to store records forever
44
49
  * Update documentation
45
50
  * Sleep if setup isn't done
46
51
 
47
-
48
- === 0.9.1 / 2011-07-25
52
+ ## 0.9.1 / 2011-07-25
49
53
 
50
54
  * Minor bugfix for RestClient initialization
51
55
 
52
- === 0.9.0 / 2011-07-25
56
+ ## 0.9.0 / 2011-07-25
53
57
 
54
58
  * Initial public release
@@ -1,135 +1,330 @@
1
- Couchbase Ruby Client
2
- =====================
1
+ # Couchbase Ruby Client
3
2
 
4
3
  This is the official client library for use with Couchbase Server.
5
4
 
6
- INSTALL
7
- =======
5
+ ## INSTALL
8
6
 
9
7
  This gem depends on a couple of external libraries to work with JSON and
10
- HTTP. For JSON it uses [yajl-ruby][1] and [yaji][2] which are built atop
11
- of [yajl][3]. For HTTP iteraction it uses [curb][4], the curl bindings for
12
- ruby. To install it use your package manager. On debian you can install
13
- it, use `aptitude`:
8
+ Couchbase Server. For JSON it uses [yajl-ruby][1] which is built atop
9
+ [yajl][2]. For Couchbase iteraction it uses [libcouchbase][3]. The first
10
+ dependency shouldn't cause any issues because it will bundle yajl in the c
11
+ extensions. To install yajl-ruby use following command:
14
12
 
15
- $ sudo aptitude install libcurl3-dev
13
+ $ gem install yajl-ruby
16
14
 
17
- After that you can install the gem via rubygems:
15
+ In most cases installing libcouchbase is just as simple.
16
+
17
+ ### MacOS (Homebrew)
18
+
19
+ $ brew install libcouchbase
20
+
21
+ Or if our pull request isn't yet merged:
22
+
23
+ $ brew install http://packages.couchbase.com/clients/c/homebrew/libvbucket.rb
24
+ $ brew install http://packages.couchbase.com/clients/c/homebrew/libcouchbase.rb
25
+
26
+ ### Debian (Ubuntu)
27
+
28
+ Download packages depending on your architecture:
29
+
30
+ $ wget http://packages.couchbase.com/clients/c/libvbucket{1,1-dbg,-dev}_1.8.0.1-1_amd64.deb
31
+ $ wget http://packages.couchbase.com/clients/c/libcouchbase{1,1-dbg,-dev}_1.0.0-1_amd64.deb
32
+
33
+ or
34
+
35
+ $ wget http://packages.couchbase.com/clients/c/libvbucket{1,1-dbg,-dev}_1.8.0.1-1_i386.deb
36
+ $ wget http://packages.couchbase.com/clients/c/libcouchbase{1,1-dbg,-dev}_1.0.0-1_i386.deb
37
+
38
+ Then install them using dpkg tool
39
+
40
+ $ sudo dpkg -i lib{vbucket,couchbase}*.deb
41
+
42
+ ### Centos (Redhat and rpm-based systems)
43
+
44
+ Download packages depending on your architecture:
45
+
46
+ $ wget http://packages.couchbase.com/clients/c/libvbucket{1,-debuginfo,-devel}-1.8.0.1-1.x86_64.rpm
47
+ $ wget http://packages.couchbase.com/clients/c/libcouchbase{1,-debuginfo,-devel}-1.0.0-1.x86_64.rpm
48
+
49
+ or
50
+
51
+ $ wget http://packages.couchbase.com/clients/c/libvbucket{1,-debuginfo,-devel}-1.8.0.1-1.i386.rpm
52
+ $ wget http://packages.couchbase.com/clients/c/libcouchbase{1,-debuginfo,-devel}-1.0.0-1.i386.rpm
53
+
54
+ Then install them using rpm tool
55
+
56
+ $ sudo rpm -ivh lib{vbucket,couchbase}*.rpm
57
+
58
+ ### Couchbase gem
59
+
60
+ Now install the couchbase gem itself
18
61
 
19
62
  $ gem install couchbase
20
63
 
21
- USAGE
22
- =====
64
+
65
+ ## USAGE
23
66
 
24
67
  First of all you need to load library:
25
68
 
26
69
  require 'couchbase'
27
70
 
28
- To establish connection with couchbase you need to specify at least pool
29
- URI. Also you can choose custom bucket name and memcached SASL
30
- authentication and Couchbase REST API.
71
+ There are several ways to establish new connection to Couchbase Server.
72
+ By default it uses the `http://localhost:8091/pools/default/buckets/default`
73
+ as the endpoint. The client will automatically adjust configuration when
74
+ the cluster will rebalance its nodes when nodes are added or deleted
75
+ therefore this client is "smart".
76
+
77
+ c = Couchbase.new
31
78
 
79
+ This is equivalent to following forms:
80
+
81
+ c = Couchbase.new("http://localhost:8091/pools/default/buckets/default")
32
82
  c = Couchbase.new("http://localhost:8091/pools/default")
33
- c = Couchbase.new("http://localhost:8091/pools/default", :bucket_name => 'blog')
34
- c = Couchbase.new("http://localhost:8091/pools/default",
35
- :bucket_name => 'blog', :bucket_password => 'secret')
36
-
37
- This gem supports memcached API accessible for [memcached][5] client
38
- with a bit syntax sugar:
39
-
40
- c.set('password', 'secret')
41
- c.get('password') #=> "secret"
42
- c['password'] = 'secret'
43
- c['password'] #=> "secret"
44
- c['counter'] = 1 #=> 1
45
- c['counter'] += 1 #=> 2
46
- c['counter'] #=> 2
47
- c.increment('counter', 10)
48
- c.flush
83
+ c = Couchbase.new("http://localhost:8091")
84
+ c = Couchbase.new(:host => "localhost")
85
+ c = Couchbase.new(:host => "localhost", :port => 8091)
86
+ c = Couchbase.new(:pool => "default", :bucket => "default")
49
87
 
50
- But if you store structured data, they will be treated as documents and
51
- you can handle them in map/reduce function from CouchDB views. For
52
- example, store a couple of posts using memcached API:
88
+ The hash parameters take precedence on string URL.
53
89
 
54
- c['biking'] = {:title => 'Biking',
55
- :body => 'I went to the the pet store earlier and brought home a little kitty...',
56
- :date => '2009/01/30 18:04:11'}
57
- c['bought-a-cat'] = {:title => 'Biking',
58
- :body => 'My biggest hobby is mountainbiking. The other day...',
59
- :date => '2009/01/30 18:04:11'}
60
- c['hello-world'] = {:title => 'Hello World',
61
- :body => 'Well hello and welcome to my new blog...',
62
- :date => '2009/01/15 15:52:20'}
63
- c.all_docs.count #=> 3
90
+ The library supports both synchronous and asynchronous mode. You can
91
+ choose either using the `:async` option or attribute.
64
92
 
65
- Now let's create design doc with sample view and save it in file
66
- 'blog.json':
93
+ c = Couchbase.new(:async => true)
94
+ # ... asynchronous mode
95
+ c.async = false
96
+ # ... synchronous mode
67
97
 
68
- {
69
- "_id": "_design/blog",
70
- "language": "javascript",
71
- "views": {
72
- "recent_posts": {
73
- "map": "function(doc){if(doc.date && doc.title){emit(doc.date, doc.title);}}"
74
- }
75
- }
76
- }
98
+ In asynchronous mode all operations will return control to caller
99
+ without blocking current thread. You can pass the block to method and it
100
+ will be called with result when the operation will be completed. You
101
+ need to run event loop when you scheduled your operations:
77
102
 
78
- This design document could be loaded into the database like this (also you can
79
- pass the ruby Hash or String with JSON encoded document):
103
+ c = Couchbase.new(:async => true)
104
+ c.run do |conn|
105
+ conn.get("foo") {|ret| puts ret.value}
106
+ conn.set("bar", "baz")
107
+ end
80
108
 
81
- c.save_design_doc(File.open('blog.json'))
109
+ The handlers could be nested
82
110
 
83
- To execute view you need to fetch it from design document `_design/blog`:
111
+ c.run do |conn|
112
+ conn.get("foo") do |ret|
113
+ conn.incr(ret.value, :initial => 0)
114
+ end
115
+ end
84
116
 
85
- blog = c.design_docs['blog']
86
- blog.views #=> ["recent_posts"]
87
- blog.recent_posts #=> [#<Couchbase::Document:14244860 {"id"=>"hello-world", "key"=>"2009/01/15 15:52:20", "value"=>"Hello World"}>,...]
117
+ The asynchronous callback receives instance of `Couchbase::Result` which
118
+ responds to several methods to figure out what was happened:
88
119
 
89
- Gem uses streaming parser to access view results so you can iterate them
90
- easily and if your code won't keep links to the documents GC might free
91
- them as soon as it decide they are unreachable, because parser doesn't
92
- store global JSON tree.
120
+ * `success?`. Returns `true` if operation succed.
93
121
 
94
- posts = blog.recent_posts.each do |doc|
95
- # do something
96
- # with doc object
97
- end
122
+ * `error`. Returns `nil` or exception object (subclass of
123
+ `Couchbase::Error::Base`) if something went wrong.
98
124
 
99
- You can also use Enumerator to iterate view results
125
+ * `key`
100
126
 
101
- require 'date'
102
- posts_by_date = Hash.new{|h,k| h[k] = []}
103
- enum = c.all_docs.each # request hasn't issued yet
104
- enum.inject(posts_by_date) do |acc, doc|
105
- acc[date] = Date.strptime(doc['date'], '%Y/%m/%d')
106
- acc
107
- end
127
+ * `value`
108
128
 
109
- The Couchbase server could generate errors during view execution with
110
- `200 OK` and partial results. By default the library raises exception as
111
- soon as errors detected in the result stream, but you can define the
112
- callback `on_error` to intercept these errors and do something more
113
- useful.
129
+ * `flags`
114
130
 
115
- view = blog.recent_posts
116
- logger = Logger.new(STDOUT)
131
+ * `cas`. The CAS version tag.
132
+
133
+ * `node`. Node address. It is used in flush and stats commands.
134
+
135
+ * `operation`. The symbol, representing an operation.
117
136
 
118
- view.on_error do |from, reason|
119
- logger.warn("#{view.inspect} received the error '#{reason}' from #{from}")
120
- end
121
137
 
122
- posts = view.each do |doc|
123
- # do something
124
- # with doc object
138
+ To handle global errors in async mode `#on_error` callback should be
139
+ used. It can be set in following fashions:
140
+
141
+ c.on_error do |opcode, key, exc|
142
+ ...
125
143
  end
126
144
 
127
- Note that errors object in view results usually goes *after* the rows,
128
- so you will likely receive a number of view results successfully before
129
- the error is detected.
145
+ handler = lambda {|opcode, key, exc| ...}
146
+ c.on_error = handler
147
+
148
+ By default connection uses `:quiet` mode. This mean it won't raise
149
+ exceptions when the given key is not exists:
150
+
151
+ c.get("missing-key") #=> nil
152
+
153
+ It could be useful when you are trying to make you code a bit efficient
154
+ by avoiding exception handling. (See `#add` and `#replace` operations).
155
+ You can turn on these exception by passing `:quiet => false` when you
156
+ are instantiating the connection or change corresponding attribute:
157
+
158
+ c.quiet = false
159
+ c.get("missing-key") #=> raise Couchbase::Error::NotFound
160
+ c.get("missing-key", :quiet => true) #=> nil
161
+
162
+ The library supports three different formats for representing values:
163
+
164
+ * `:document` (default) format supports most of ruby types which could
165
+ be mapped to JSON data (hashes, arrays, string, numbers). A future
166
+ version will be able to run map/reduce queries on the values in the
167
+ document form (hashes)
168
+
169
+ * `:marshal` This format avoids any conversions to be applied to your
170
+ data, but your data should be passed as String. This is useful for
171
+ building custom algorithms or formats. For example to implement a set:
172
+ http://dustin.github.com/2011/02/17/memcached-set.html
173
+
174
+ * `:plain` Use this format if you'd like to transparently serialize your
175
+ ruby object with standard `Marshal.dump` and `Marshal.load` methods
176
+
177
+ The couchbase API is the superset of [Memcached binary protocol][4], so
178
+ you can use its operations.
179
+
180
+ ### Get
181
+
182
+ val = c.get("foo")
183
+ val, flags, cas = c.get("foo", :extended => true)
184
+
185
+ Get and touch
186
+
187
+ val = c.get("foo", :ttl => 10)
188
+
189
+ Get multiple values. In quiet mode will put `nil` values on missing
190
+ positions:
191
+
192
+ vals = c.get("foo", "bar", "baz")
193
+ c.get("foo"){|val, key| ... }
194
+ c.get("foo", :extended => true){|val, key, flags, cas| ... }
195
+
196
+ Get multiple values with extended information. The result will
197
+ represented by hash with tuples `[value, flags, cas]` as a value.
198
+
199
+ vals = c.get("foo", "bar", "baz", :extended => true)
200
+ vals.inspect #=> {"baz"=>["3", 0, 4784582192793125888],
201
+ "foo"=>["1", 0, 8835713818674332672],
202
+ "bar"=>["2", 0, 10805929834096100352]}
203
+
204
+ Hash-like syntax
205
+
206
+ c["foo"]
207
+ c["foo", "bar", "baz"]
208
+ c["foo", {:extended => true}]
209
+ c["foo", :extended => true] # for ruby 1.9.x only
210
+
211
+ ### Touch
212
+
213
+ c.touch("foo") # use :default_ttl
214
+ c.touch("foo", 10)
215
+ c.touch("foo", :ttl => 10)
216
+ c.touch("foo" => 10, "bar" => 20)
217
+ c.touch("foo" => 10, "bar" => 20){|key, success| ... }
218
+
219
+ ### Set
220
+
221
+ c.set("foo", "bar")
222
+ c.set("foo", "bar", :flags => 0x1000, :ttl => 30, :format => :plain)
223
+ c["foo"] = "bar"
224
+ c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
225
+ c["foo", :flags => 0x1000] = "bar" # for ruby 1.9.x only
226
+ c.set("foo", "bar", :cas => 8835713818674332672)
227
+ c.set("foo", "bar"){|cas, key, operation| ... }
228
+
229
+ ### Add
230
+
231
+ Add command will fail if the key already exists. It accepts the same
232
+ options as set command above.
233
+
234
+ c.add("foo", "bar")
235
+ c.add("foo", "bar", :flags => 0x1000, :ttl => 30, :format => :plain)
236
+
237
+ ### Replace
238
+
239
+ The replace command will fail if the key already exists. It accepts the same
240
+ options as set command above.
241
+
242
+ c.replace("foo", "bar")
243
+
244
+ ### Prepend/Append
245
+
246
+ These commands are meaningful when you are using the `:plain` value format,
247
+ because the concatenation is performed by server which has no idea how
248
+ to merge to JSON values or values in ruby Marshal format. You may receive
249
+ an `Couchbase::Error::ValueFormat` error.
250
+
251
+ c.set("foo", "world")
252
+ c.append("foo", "!")
253
+ c.prepend("foo", "Hello, ")
254
+ c.get("foo") #=> "Hello, world!"
255
+
256
+ ### Increment/Decrement
257
+
258
+ These commands increment the value assigned to the key. It will raise
259
+ Couchbase::Error::DeltaBadval if the delta or value is not a number.
260
+
261
+ c.set("foo", 1)
262
+ c.incr("foo") #=> 2
263
+ c.incr("foo", :delta => 2) #=> 4
264
+ c.incr("foo", 4) #=> 8
265
+ c.incr("foo", -1) #=> 7
266
+ c.incr("foo", -100) #=> 0
267
+ c.incr("foo"){|val, cas| ... }
268
+
269
+ c.set("foo", 10)
270
+ c.decr("foo", 1) #=> 9
271
+ c.decr("foo", 100) #=> 0
272
+ c.decr("foo"){|val, cas| ... }
273
+
274
+ c.incr("missing1", :initial => 10) #=> 10
275
+ c.incr("missing1", :initial => 10) #=> 11
276
+ c.incr("missing2", :create => true) #=> 0
277
+ c.incr("missing2", :create => true) #=> 1
278
+
279
+ Note that it isn't the same as increment/decrement in ruby, which is
280
+ performed on client side with following `set` operation:
281
+
282
+ c["foo"] = 10
283
+ c["foo"] -= 20 #=> -10
284
+
285
+ ### Delete
286
+
287
+ c.delete("foo")
288
+ c.delete("foo", :cas => 8835713818674332672)
289
+ c.delete("foo", 8835713818674332672)
290
+ c.delete{|key, success| ... }
291
+
292
+ ### Flush
293
+
294
+ Flush the items in the cluster.
295
+
296
+ c.flush
297
+ c.flush{|node, success| ... }
298
+
299
+ ### Stats
300
+
301
+ Return statistics from each node in the cluster
302
+
303
+ c.stats
304
+ c.stats{|node, key, value| ... }
305
+
306
+ The result is represented as a hash with the server node address as
307
+ the key and stats as key-value pairs.
308
+
309
+ {
310
+ "172.16.16.76:12008"=>
311
+ {
312
+ "threads"=>"4",
313
+ "connection_structures"=>"22",
314
+ "ep_max_txn_size"=>"10000",
315
+ ...
316
+ },
317
+ "172.16.16.76:12000"=>
318
+ {
319
+ "threads"=>"4",
320
+ "connection_structures"=>"447",
321
+ "ep_max_txn_size"=>"10000",
322
+ ...
323
+ },
324
+ ...
325
+ }
130
326
 
131
327
  [1]: https://github.com/brianmario/yajl-ruby/
132
- [2]: https://github.com/avsej/yaji/
133
- [3]: http://lloyd.github.com/yajl/
134
- [4]: https://rubygems.org/gems/curb/
135
- [5]: https://github.com/fauna/memcached/
328
+ [2]: http://lloyd.github.com/yajl/
329
+ [3]: http://www.couchbase.com/develop/c/current
330
+ [4]: http://code.google.com/p/memcached/wiki/BinaryProtocolRevamped