couchbase 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGJmOThlNjRhNTU4NDU4N2ZmODAwY2E5NmIxYjRjYmM0MThlNzZkZA==
5
+ data.tar.gz: !binary |-
6
+ OTJhNWUyM2JhNTNmOTJmYzVkYmFlNWRkMzljNWVlZmFiNzFmOGNjNg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTkxNTAxMTA2YmNjOGU4ZjJjMTk0N2NkNGVlZDlkNDQyNjMyYTNjMjk2MjRj
10
+ YjdhZDM1MjcxYTgxZGUwYTliMDk5NTIzMWY4ZDRlYmZlOWUwMDg0Yjg1MTU0
11
+ MjQyMjA0NWIxYmZlZmM5NGY0YTg4OTUyOGJkZTNmNjc0YTYyMGM=
12
+ data.tar.gz: !binary |-
13
+ MTQ4ZjJkNDhiNGRmNmIzZjIwNjAxNzQ1ZGU3MDFiY2ZmMDg4ZDA0NTUyNzMy
14
+ YjZjZDFhNWNkYTQ1Y2VlMTkxYzRlMmM3OWVhMTdmYTg4ZGQ2M2NkY2EwY2Vi
15
+ MTVkYjk4ZWUwMWRjYzBjZTQ3YTkyNWRlM2ZmNjUxNWQ2MjBjOGE=
@@ -0,0 +1,75 @@
1
+ We've decided to use "gerrit" for our code review system, making it
2
+ easier for all of us to contribute with code and comments.
3
+
4
+ 1. Visit http://review.couchbase.org and "Register" for an account
5
+ 2. Review http://review.couchbase.org/static/individual_agreement.html
6
+ 3. Agree to agreement by visiting http://review.couchbase.org/#/settings/agreements
7
+ 4. If you do not receive an email, please contact us
8
+ 5. Check out the `couchbase-ruby-client` area http://review.couchbase.org/#/q/status:open+project:couchbase-ruby-client,n,z
9
+ 6. Join us on IRC at #libcouchbase on Freenode :-)
10
+
11
+ We normally don't go looking for stuff in gerrit, so you should add at
12
+ least me `"Sergey Avseyev" <sergey.avseyev@gmail.com>` as a reviewer
13
+ for your patch (and I'll know who else to add and add them for you).
14
+
15
+ ## Contributing Using Repo Tool
16
+
17
+ Follow ["Uploading Changes" guide][1] on the site if you have some code to contribute.
18
+
19
+ All you should need to set up your development environment should be:
20
+
21
+ ~ % mkdir couchbase-ruby
22
+ ~ % cd couchbase-ruby
23
+ ~/couchbase-ruby % repo init -u git://github.com/trondn/manifests.git -m ruby.xml
24
+ ~/couchbase-ruby % repo sync
25
+ ~/couchbase-ruby % repo start my-branch-name --all
26
+ ~/couchbase-ruby % make
27
+
28
+ This will build the latest version of `libcouchbase`,
29
+ `couchbase-ruby-client` and `couchbase-ruby-client` libraries. You must
30
+ have a C and C++ compiler installed, automake, autoconf.
31
+
32
+ If you have to make any changes just commit them before you upload
33
+ them to gerrit with the following command:
34
+
35
+ ~/couchbase-ruby/client % repo upload
36
+
37
+ You might experience a problem trying to upload the patches if you've
38
+ selected a different login name at http://review.couchbase.org than
39
+ your login name. Don't worry, all you need to do is to add the
40
+ following to your ~/.gitconfig file:
41
+
42
+ [review "review.couchbase.org"]
43
+ username = YOURNAME
44
+
45
+ ## Contributing Using Plain Git
46
+
47
+ If you not so familiar with repo tool and its workflow there is
48
+ alternative way to do the same job. Lets assume you have installed
49
+ couchbase gem and libcouchbase from official packages and would you to
50
+ contribute to couchbase-client gem only. Then you just need to complete
51
+ gerrit registration steps above and clone the source repository
52
+ (remember the repository on github.com is just a mirror):
53
+
54
+ ~ % git clone ssh://YOURNAME@review.couchbase.org:29418/couchbase-ruby-client.git
55
+
56
+ Install [`commit-msg` hook][2]:
57
+
58
+ ~/couchbase-ruby-client % scp -p -P 29418 YOURNAME@review.couchbase.org:hooks/commit-msg .git/hooks/
59
+
60
+ Make your changes and upload them for review:
61
+
62
+ ~/couchbase-ruby-client % git commit
63
+ ~/couchbase-ruby-client % git push origin HEAD:refs/for/master
64
+
65
+ If you need to fix or add something to your patch, do it and re-upload
66
+ the changes (all you need is to keep `Change-Id:` line the same to
67
+ allow gerrit to track the patch.
68
+
69
+ ~/couchbase-ruby-client % git commit --amend
70
+ ~/couchbase-ruby-client % git push origin HEAD:refs/for/master
71
+
72
+ Happy hacking!
73
+
74
+ [1]: http://review.couchbase.org/Documentation/user-upload.html
75
+ [2]: http://review.couchbase.org/Documentation/user-changeid.html
@@ -3,6 +3,14 @@
3
3
  This document is a list of user visible feature changes and important
4
4
  bugfixes. Do not forget to update this doc in every important patch.
5
5
 
6
+ ## 1.2.3 (2013-04-02)
7
+
8
+ * [major] Make ActiveSupport::Cache::CouchbaseStore threadsafe
9
+
10
+ * [minor] Check for gethrtime. Needed for solaris/smartos
11
+
12
+ * [minor] Update documentation bits regarding SET operations
13
+
6
14
  ## 1.2.2 (2013-02-11)
7
15
 
8
16
  * [minor] Bucket#design_docs will return a Hash with DesignDoc
@@ -135,6 +135,7 @@ have_type("st_index_t")
135
135
  have_func("clock_gettime")
136
136
  have_func("gettimeofday")
137
137
  have_func("QueryPerformanceCounter")
138
+ have_func("gethrtime")
138
139
  have_func("rb_hash_lookup2")
139
140
  have_func("rb_thread_fd_select")
140
141
  have_func("rb_thread_blocking_region")
@@ -16,6 +16,9 @@
16
16
  */
17
17
 
18
18
  #include "couchbase_ext.h"
19
+
20
+ #ifndef HAVE_GETHRTIME
21
+
19
22
  #include <stdlib.h>
20
23
  #include <time.h>
21
24
  #include <assert.h>
@@ -122,3 +125,5 @@ hrtime_t gethrtime(void)
122
125
  #error "I don't know how to build a highres clock..."
123
126
  #endif
124
127
  }
128
+
129
+ #endif
@@ -228,6 +228,22 @@ cb_bucket_store(lcb_storage_t cmd, int argc, VALUE *argv, VALUE self)
228
228
  * @example Store the key which will be expired in 2 seconds using relative TTL.
229
229
  * c.set("foo", "bar", :ttl => 2)
230
230
  *
231
+ * @example Perform multi-set operation. It takes a Hash store its keys/values into the bucket
232
+ * c.set("foo1" => "bar1", "foo2" => "bar2")
233
+ * #=> {"foo1" => cas1, "foo2" => cas2}
234
+ *
235
+ * @example More advanced multi-set using asynchronous mode
236
+ * c.run do
237
+ * # fire and forget
238
+ * c.set("foo1", "bar1", :ttl => 10)
239
+ * # receive result into the callback
240
+ * c.set("foo2", "bar2", :ttl => 10) do |ret|
241
+ * if ret.success?
242
+ * puts ret.cas
243
+ * end
244
+ * end
245
+ * end
246
+ *
231
247
  * @example Store the key which will be expired in 2 seconds using absolute TTL.
232
248
  * c.set("foo", "bar", :ttl => Time.now.to_i + 2)
233
249
  *
@@ -235,7 +251,7 @@ cb_bucket_store(lcb_storage_t cmd, int argc, VALUE *argv, VALUE self)
235
251
  * c.set("foo", {"bar" => "baz}, :format => :document)
236
252
  *
237
253
  * @example Use hash-like syntax to store the value
238
- * c.set["foo"] = {"bar" => "baz}
254
+ * c["foo"] = {"bar" => "baz}
239
255
  *
240
256
  * @example Use extended hash-like syntax
241
257
  * c["foo", {:flags => 0x1000, :format => :plain}] = "bar"
@@ -19,6 +19,7 @@ require 'couchbase'
19
19
  require 'securerandom'
20
20
  require 'active_support/core_ext/array/extract_options'
21
21
  require 'active_support/cache'
22
+ require 'monitor'
22
23
 
23
24
  module ActiveSupport
24
25
  module Cache
@@ -56,6 +57,7 @@ module ActiveSupport
56
57
  options[:key_prefix] ||= options.delete(:namespace)
57
58
  args.push(options)
58
59
  @data = ::Couchbase::Bucket.new(*args)
60
+ @lock = Monitor.new
59
61
  end
60
62
 
61
63
  # Fetches data from the cache, using the given key.
@@ -182,7 +184,9 @@ module ActiveSupport
182
184
  options[:format] = :plain
183
185
  end
184
186
  instrument(:read_multi, names, options) do
185
- @data.get(names, options)
187
+ @lock.synchronize do
188
+ @data.get(names, options)
189
+ end
186
190
  end
187
191
  rescue ::Couchbase::Error::Base => e
188
192
  logger.error("#{e.class}: #{e.message}") if logger
@@ -243,7 +247,9 @@ module ActiveSupport
243
247
  options[:create] = true
244
248
  instrument(:increment, name, options) do |payload|
245
249
  payload[:amount] = amount if payload
246
- @data.incr(name, amount, options)
250
+ @lock.synchronize do
251
+ @data.incr(name, amount, options)
252
+ end
247
253
  end
248
254
  rescue ::Couchbase::Error::Base => e
249
255
  logger.error("#{e.class}: #{e.message}") if logger
@@ -275,7 +281,9 @@ module ActiveSupport
275
281
  options[:create] = true
276
282
  instrument(:decrement, name, options) do |payload|
277
283
  payload[:amount] = amount if payload
278
- @data.decr(name, amount, options)
284
+ @lock.synchronize do
285
+ @data.decr(name, amount, options)
286
+ end
279
287
  end
280
288
  rescue ::Couchbase::Error::Base => e
281
289
  logger.error("#{e.class}: #{e.message}") if logger
@@ -289,14 +297,18 @@ module ActiveSupport
289
297
  #
290
298
  # @return [Hash]
291
299
  def stats(*arg)
292
- @data.stats(*arg)
300
+ @lock.synchronize do
301
+ @data.stats(*arg)
302
+ end
293
303
  end
294
304
 
295
305
  protected
296
306
 
297
307
  # Read an entry from the cache.
298
308
  def read_entry(key, options) # :nodoc:
299
- @data.get(key, options)
309
+ @lock.synchronize do
310
+ @data.get(key, options)
311
+ end
300
312
  rescue ::Couchbase::Error::Base => e
301
313
  logger.error("#{e.class}: #{e.message}") if logger
302
314
  raise if @raise_errors
@@ -313,7 +325,9 @@ module ActiveSupport
313
325
  if ttl = options.delete(:expires_in)
314
326
  options[:ttl] ||= ttl
315
327
  end
316
- @data.send(method, key, value, options)
328
+ @lock.synchronize do
329
+ @data.send(method, key, value, options)
330
+ end
317
331
  rescue ::Couchbase::Error::Base => e
318
332
  logger.error("#{e.class}: #{e.message}") if logger
319
333
  raise if @raise_errors
@@ -322,7 +336,9 @@ module ActiveSupport
322
336
 
323
337
  # Delete an entry from the cache.
324
338
  def delete_entry(key, options) # :nodoc:
325
- @data.delete(key, options)
339
+ @lock.synchronize do
340
+ @data.delete(key, options)
341
+ end
326
342
  rescue ::Couchbase::Error::Base => e
327
343
  logger.error("#{e.class}: #{e.message}") if logger
328
344
  raise if @raise_errors
@@ -73,7 +73,18 @@ module Couchbase
73
73
 
74
74
  # @private the thread local storage
75
75
  def thread_storage
76
- Thread.current[:couchbase] ||= {}
76
+ Thread.current[:couchbase] ||= { :pid => Process.pid }
77
+ end
78
+
79
+ # @private resets thread local storage if process ids don't match
80
+ # see 13.3.1: http://www.modrails.com/documentation/Users%20guide%20Apache.html
81
+ def verify_connection!
82
+ reset_thread_storage! if thread_storage[:pid] != Process.pid
83
+ end
84
+
85
+ # @private resets thread local storage
86
+ def reset_thread_storage!
87
+ Thread.current[:couchbase] = nil
77
88
  end
78
89
 
79
90
  # The connection instance for current thread
@@ -87,6 +98,7 @@ module Couchbase
87
98
  #
88
99
  # @return [Bucket]
89
100
  def bucket
101
+ verify_connection!
90
102
  thread_storage[:bucket] ||= connect(connection_options)
91
103
  end
92
104
 
@@ -96,6 +108,7 @@ module Couchbase
96
108
  #
97
109
  # @return [Bucket]
98
110
  def bucket=(connection)
111
+ verify_connection!
99
112
  thread_storage[:bucket] = connection
100
113
  end
101
114
 
@@ -17,5 +17,5 @@
17
17
 
18
18
  # Couchbase ruby client
19
19
  module Couchbase
20
- VERSION = "1.2.2"
20
+ VERSION = "1.2.3"
21
21
  end
@@ -16,6 +16,6 @@
16
16
  #
17
17
 
18
18
  desc 'Start an irb session and load the library.'
19
- task :console => :compile do
19
+ task :console do
20
20
  exec "irb -I lib -rcouchbase"
21
21
  end
@@ -16,13 +16,62 @@
16
16
  #
17
17
 
18
18
  require File.join(File.dirname(__FILE__), 'setup')
19
+ require 'minitest/mock'
19
20
 
20
21
  class TestCouchbase < MiniTest::Unit::TestCase
21
22
 
23
+ def teardown
24
+ Couchbase.reset_thread_storage!
25
+ end
26
+
22
27
  def test_that_it_create_instance_of_bucket
23
28
  with_mock do |mock|
24
29
  assert_instance_of Couchbase::Bucket, Couchbase.new("http://#{mock.host}:#{mock.port}/pools/default")
25
30
  end
26
31
  end
27
32
 
33
+ def test_verify_connection
34
+ pid = Process.pid
35
+ assert_equal pid, Couchbase.thread_storage[:pid]
36
+ Couchbase.verify_connection!
37
+ assert_equal pid, Couchbase.thread_storage[:pid]
38
+ end
39
+
40
+ def test_verify_connection_when_process_forks
41
+ pid = Process.pid
42
+ assert_equal pid, Couchbase.thread_storage[:pid]
43
+
44
+ # stub a simulated Kernel#fork
45
+ Process.stub(:pid, Process.pid + 1) do
46
+ Couchbase.verify_connection!
47
+ refute_equal pid, Couchbase.thread_storage[:pid]
48
+ end
49
+ end
50
+
51
+ def test_new_connection_when_process_forks
52
+ with_mock do |mock|
53
+ connection_options = "http://#{mock.host}:#{mock.port}/pools/default"
54
+ Couchbase.connection_options = connection_options
55
+ old_bucket_id = Couchbase.bucket.object_id
56
+
57
+ Process.stub(:pid, Process.pid + 1) do
58
+ refute_equal old_bucket_id, Couchbase.bucket.object_id
59
+ end
60
+ end
61
+ end
62
+
63
+ def test_new_connection_has_same_configuration_options
64
+ with_mock do |mock|
65
+ connection_options = "http://#{mock.host}:#{mock.port}/pools/default"
66
+ Couchbase.connection_options = connection_options
67
+ old_bucket = Couchbase.bucket
68
+
69
+ Process.stub(:pid, Process.pid + 1) do
70
+ new_bucket = Couchbase.bucket
71
+ assert_equal old_bucket.name, new_bucket.name
72
+ assert_equal old_bucket.hostname, new_bucket.hostname
73
+ end
74
+ end
75
+ end
76
+
28
77
  end
@@ -280,6 +280,29 @@ class TestCouchbaseRailsCacheStore < MiniTest::Unit::TestCase
280
280
  assert_equal({:key => uniq_id, :amount => 1, :create => true}, decrement.payload)
281
281
  end
282
282
 
283
+ # Inspiration: https://github.com/mperham/dalli/blob/master/test/test_dalli.rb#L416
284
+ def test_it_is_threadsafe
285
+ workers = []
286
+
287
+ # Have a bunch of threads perform a bunch of operations at the same time.
288
+ # Verify the result of each operation to ensure the request and response
289
+ # are not intermingled between threads.
290
+ 10.times do
291
+ workers << Thread.new do
292
+ 100.times do
293
+ store.write('a', 9)
294
+ store.write('b', 11)
295
+ assert_equal 9, store.read('a')
296
+ assert_equal({ 'a' => 9, 'b' => 11 }, store.read_multi('a', 'b'))
297
+ assert_equal 11, store.read('b')
298
+ assert_equal %w(a b), store.read_multi('a', 'b', 'c').keys.sort
299
+ end
300
+ end
301
+ end
302
+
303
+ workers.each { |w| w.join }
304
+ end
305
+
283
306
  private
284
307
 
285
308
  def collect_notifications
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchbase
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
5
- prerelease:
4
+ version: 1.2.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Couchbase
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-02-11 00:00:00.000000000 Z
11
+ date: 2013-04-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: yaji
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: multi_json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: minitest
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ! '>='
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ! '>='
76
67
  - !ruby/object:Gem::Version
@@ -78,7 +69,6 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: rake-compiler
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ! '>='
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ! '>='
92
81
  - !ruby/object:Gem::Version
@@ -94,7 +83,6 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: mini_portile
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ! '>='
100
88
  - !ruby/object:Gem::Version
@@ -102,7 +90,6 @@ dependencies:
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ! '>='
108
95
  - !ruby/object:Gem::Version
@@ -110,7 +97,6 @@ dependencies:
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: yajl-ruby
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
101
  - - ~>
116
102
  - !ruby/object:Gem::Version
@@ -118,7 +104,6 @@ dependencies:
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
108
  - - ~>
124
109
  - !ruby/object:Gem::Version
@@ -126,7 +111,6 @@ dependencies:
126
111
  - !ruby/object:Gem::Dependency
127
112
  name: active_support
128
113
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
114
  requirements:
131
115
  - - ! '>='
132
116
  - !ruby/object:Gem::Version
@@ -134,7 +118,6 @@ dependencies:
134
118
  type: :development
135
119
  prerelease: false
136
120
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
121
  requirements:
139
122
  - - ! '>='
140
123
  - !ruby/object:Gem::Version
@@ -142,7 +125,6 @@ dependencies:
142
125
  - !ruby/object:Gem::Dependency
143
126
  name: eventmachine
144
127
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
128
  requirements:
147
129
  - - ! '>='
148
130
  - !ruby/object:Gem::Version
@@ -150,7 +132,6 @@ dependencies:
150
132
  type: :development
151
133
  prerelease: false
152
134
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
135
  requirements:
155
136
  - - ! '>='
156
137
  - !ruby/object:Gem::Version
@@ -165,6 +146,7 @@ files:
165
146
  - .gitignore
166
147
  - .travis.yml
167
148
  - .yardopts
149
+ - CONTRIBUTING.markdown
168
150
  - Gemfile
169
151
  - LICENSE
170
152
  - Makefile
@@ -241,26 +223,25 @@ files:
241
223
  homepage: http://couchbase.org
242
224
  licenses:
243
225
  - ASL-2
226
+ metadata: {}
244
227
  post_install_message:
245
228
  rdoc_options: []
246
229
  require_paths:
247
230
  - lib
248
231
  required_ruby_version: !ruby/object:Gem::Requirement
249
- none: false
250
232
  requirements:
251
233
  - - ! '>='
252
234
  - !ruby/object:Gem::Version
253
235
  version: '0'
254
236
  required_rubygems_version: !ruby/object:Gem::Requirement
255
- none: false
256
237
  requirements:
257
238
  - - ! '>='
258
239
  - !ruby/object:Gem::Version
259
240
  version: '0'
260
241
  requirements: []
261
242
  rubyforge_project:
262
- rubygems_version: 1.8.23
243
+ rubygems_version: 2.0.3
263
244
  signing_key:
264
- specification_version: 3
245
+ specification_version: 4
265
246
  summary: Couchbase ruby driver
266
247
  test_files: []