couchbase 1.2.0.beta-x86-mingw32 → 1.2.0-x86-mingw32

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 (45) hide show
  1. data/.travis.yml +1 -1
  2. data/Makefile +3 -0
  3. data/README.markdown +15 -4
  4. data/RELEASE_NOTES.markdown +513 -0
  5. data/couchbase.gemspec +0 -1
  6. data/ext/couchbase_ext/arguments.c +161 -244
  7. data/ext/couchbase_ext/arithmetic.c +29 -37
  8. data/ext/couchbase_ext/bucket.c +252 -219
  9. data/ext/couchbase_ext/couchbase_ext.c +540 -417
  10. data/ext/couchbase_ext/couchbase_ext.h +218 -191
  11. data/ext/couchbase_ext/delete.c +30 -27
  12. data/ext/couchbase_ext/extconf.rb +15 -3
  13. data/ext/couchbase_ext/get.c +45 -37
  14. data/ext/couchbase_ext/http.c +95 -74
  15. data/ext/couchbase_ext/multithread_plugin.c +1201 -0
  16. data/ext/couchbase_ext/observe.c +42 -37
  17. data/ext/couchbase_ext/result.c +17 -20
  18. data/ext/couchbase_ext/stats.c +30 -28
  19. data/ext/couchbase_ext/store.c +46 -39
  20. data/ext/couchbase_ext/timer.c +11 -11
  21. data/ext/couchbase_ext/touch.c +30 -27
  22. data/ext/couchbase_ext/unlock.c +30 -27
  23. data/ext/couchbase_ext/utils.c +166 -89
  24. data/ext/couchbase_ext/version.c +29 -26
  25. data/lib/action_dispatch/middleware/session/couchbase_store.rb +2 -2
  26. data/lib/active_support/cache/couchbase_store.rb +6 -6
  27. data/lib/couchbase.rb +1 -0
  28. data/lib/couchbase/bucket.rb +6 -11
  29. data/lib/couchbase/cluster.rb +105 -0
  30. data/lib/couchbase/utils.rb +8 -5
  31. data/lib/couchbase/version.rb +1 -1
  32. data/lib/couchbase/view.rb +51 -5
  33. data/lib/couchbase/view_row.rb +1 -1
  34. data/lib/ext/multi_json_fix.rb +13 -9
  35. data/lib/rack/session/couchbase.rb +11 -7
  36. data/tasks/compile.rake +1 -1
  37. data/tasks/test.rake +40 -34
  38. data/tasks/util.rake +1 -1
  39. data/test/setup.rb +9 -2
  40. data/test/test_arithmetic.rb +37 -0
  41. data/test/test_async.rb +22 -18
  42. data/test/test_unlock.rb +0 -1
  43. data/test/test_utils.rb +32 -0
  44. metadata +13 -23
  45. data/HISTORY.markdown +0 -215
@@ -18,31 +18,29 @@
18
18
  #include "couchbase_ext.h"
19
19
 
20
20
  void
21
- version_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_server_version_resp_t *resp)
21
+ cb_version_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_server_version_resp_t *resp)
22
22
  {
23
- struct context_st *ctx = (struct context_st *)cookie;
24
- struct bucket_st *bucket = ctx->bucket;
23
+ struct cb_context_st *ctx = (struct cb_context_st *)cookie;
24
+ struct cb_bucket_st *bucket = ctx->bucket;
25
25
  VALUE node, val, *rv = ctx->rv, exc, res;
26
26
 
27
27
  node = resp->v.v0.server_endpoint ? STR_NEW_CSTR(resp->v.v0.server_endpoint) : Qnil;
28
28
  exc = cb_check_error(error, "failed to get version", node);
29
29
  if (exc != Qnil) {
30
- rb_ivar_set(exc, id_iv_operation, sym_version);
31
- if (NIL_P(ctx->exception)) {
32
- ctx->exception = cb_gc_protect(bucket, exc);
33
- }
30
+ rb_ivar_set(exc, cb_id_iv_operation, cb_sym_version);
31
+ ctx->exception = cb_gc_protect(bucket, exc);
34
32
  }
35
33
 
36
34
  if (node != Qnil) {
37
35
  val = STR_NEW((const char*)resp->v.v0.vstring, resp->v.v0.nvstring);
38
36
  if (bucket->async) { /* asynchronous */
39
37
  if (ctx->proc != Qnil) {
40
- res = rb_class_new_instance(0, NULL, cResult);
41
- rb_ivar_set(res, id_iv_error, exc);
42
- rb_ivar_set(res, id_iv_operation, sym_version);
43
- rb_ivar_set(res, id_iv_node, node);
44
- rb_ivar_set(res, id_iv_value, val);
45
- cb_proc_call(ctx->proc, 1, res);
38
+ res = rb_class_new_instance(0, NULL, cb_cResult);
39
+ rb_ivar_set(res, cb_id_iv_error, exc);
40
+ rb_ivar_set(res, cb_id_iv_operation, cb_sym_version);
41
+ rb_ivar_set(res, cb_id_iv_node, node);
42
+ rb_ivar_set(res, cb_id_iv_value, val);
43
+ cb_proc_call(bucket, ctx->proc, 1, res);
46
44
  }
47
45
  } else { /* synchronous */
48
46
  if (NIL_P(exc)) {
@@ -52,6 +50,9 @@ version_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_
52
50
  } else {
53
51
  ctx->nqueries--;
54
52
  cb_gc_unprotect(bucket, ctx->proc);
53
+ if (bucket->async) {
54
+ free(ctx);
55
+ }
55
56
  }
56
57
 
57
58
  (void)handle;
@@ -87,26 +88,26 @@ version_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_
87
88
  VALUE
88
89
  cb_bucket_version(int argc, VALUE *argv, VALUE self)
89
90
  {
90
- struct bucket_st *bucket = DATA_PTR(self);
91
- struct context_st *ctx;
91
+ struct cb_bucket_st *bucket = DATA_PTR(self);
92
+ struct cb_context_st *ctx;
92
93
  VALUE rv, exc, args, proc;
93
94
  lcb_error_t err;
94
- struct params_st params;
95
+ struct cb_params_st params;
95
96
 
96
97
  if (bucket->handle == NULL) {
97
- rb_raise(eConnectError, "closed connection");
98
+ rb_raise(cb_eConnectError, "closed connection");
98
99
  }
99
100
  rb_scan_args(argc, argv, "0*&", &args, &proc);
100
101
  if (!bucket->async && proc != Qnil) {
101
102
  rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
102
103
  }
103
- memset(&params, 0, sizeof(struct params_st));
104
- params.type = cmd_version;
104
+ memset(&params, 0, sizeof(struct cb_params_st));
105
+ params.type = cb_cmd_version;
105
106
  params.bucket = bucket;
106
107
  cb_params_build(&params, RARRAY_LEN(args), args);
107
- ctx = xcalloc(1, sizeof(struct context_st));
108
+ ctx = calloc(1, sizeof(struct cb_context_st));
108
109
  if (ctx == NULL) {
109
- rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
110
+ rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for context");
110
111
  }
111
112
  rv = rb_hash_new();
112
113
  ctx->rv = &rv;
@@ -119,12 +120,12 @@ cb_bucket_version(int argc, VALUE *argv, VALUE self)
119
120
  exc = cb_check_error(err, "failed to schedule version request", Qnil);
120
121
  cb_params_destroy(&params);
121
122
  if (exc != Qnil) {
122
- xfree(ctx);
123
+ free(ctx);
123
124
  rb_exc_raise(exc);
124
125
  }
125
126
  bucket->nbytes += params.npayload;
126
127
  if (bucket->async) {
127
- maybe_do_loop(bucket);
128
+ cb_maybe_do_loop(bucket);
128
129
  return Qnil;
129
130
  } else {
130
131
  if (ctx->nqueries > 0) {
@@ -132,13 +133,15 @@ cb_bucket_version(int argc, VALUE *argv, VALUE self)
132
133
  lcb_wait(bucket->handle);
133
134
  }
134
135
  exc = ctx->exception;
135
- xfree(ctx);
136
+ free(ctx);
136
137
  if (exc != Qnil) {
137
138
  cb_gc_unprotect(bucket, exc);
138
139
  rb_exc_raise(exc);
139
140
  }
140
- if (bucket->exception != Qnil) {
141
- rb_exc_raise(bucket->exception);
141
+ exc = bucket->exception;
142
+ if (exc != Qnil) {
143
+ bucket->exception = Qnil;
144
+ rb_exc_raise(exc);
142
145
  }
143
146
  return rv;
144
147
  }
@@ -16,7 +16,7 @@ module ActionDispatch
16
16
  # Or remove this file and add following line to your `config/application.rb`:
17
17
  #
18
18
  # require 'action_dispatch/middleware/session/couchbase_store'
19
- # config.session_storage = :couchbase_store
19
+ # config.session_store :couchbase_store
20
20
  #
21
21
  # You can also pass additional options:
22
22
  #
@@ -25,7 +25,7 @@ module ActionDispatch
25
25
  # :expire_after => 5.minutes,
26
26
  # :couchbase => {:bucket => "sessions", :default_format => :marshal}
27
27
  # }
28
- # config.session_storage = :couchbase_store, session_options
28
+ # config.session_store :couchbase_store, session_options
29
29
  #
30
30
  # By default sessions will be serialized to JSON, to allow analyse them
31
31
  # using Map/Reduce.
@@ -184,7 +184,7 @@ module ActiveSupport
184
184
  instrument(:read_multi, names, options) do
185
185
  @data.get(names, options)
186
186
  end
187
- rescue Couchbase::Error::Base => e
187
+ rescue ::Couchbase::Error::Base => e
188
188
  logger.error("#{e.class}: #{e.message}") if logger
189
189
  raise if @raise_errors
190
190
  false
@@ -245,7 +245,7 @@ module ActiveSupport
245
245
  payload[:amount] = amount if payload
246
246
  @data.incr(name, amount, options)
247
247
  end
248
- rescue Couchbase::Error::Base => e
248
+ rescue ::Couchbase::Error::Base => e
249
249
  logger.error("#{e.class}: #{e.message}") if logger
250
250
  raise if @raise_errors
251
251
  false
@@ -277,7 +277,7 @@ module ActiveSupport
277
277
  payload[:amount] = amount if payload
278
278
  @data.decr(name, amount, options)
279
279
  end
280
- rescue Couchbase::Error::Base => e
280
+ rescue ::Couchbase::Error::Base => e
281
281
  logger.error("#{e.class}: #{e.message}") if logger
282
282
  raise if @raise_errors
283
283
  false
@@ -297,7 +297,7 @@ module ActiveSupport
297
297
  # Read an entry from the cache.
298
298
  def read_entry(key, options) # :nodoc:
299
299
  @data.get(key, options)
300
- rescue Couchbase::Error::Base => e
300
+ rescue ::Couchbase::Error::Base => e
301
301
  logger.error("#{e.class}: #{e.message}") if logger
302
302
  raise if @raise_errors
303
303
  nil
@@ -314,7 +314,7 @@ module ActiveSupport
314
314
  options[:ttl] ||= ttl
315
315
  end
316
316
  @data.send(method, key, value, options)
317
- rescue Couchbase::Error::Base => e
317
+ rescue ::Couchbase::Error::Base => e
318
318
  logger.error("#{e.class}: #{e.message}") if logger
319
319
  raise if @raise_errors
320
320
  false
@@ -323,7 +323,7 @@ module ActiveSupport
323
323
  # Delete an entry from the cache.
324
324
  def delete_entry(key, options) # :nodoc:
325
325
  @data.delete(key, options)
326
- rescue Couchbase::Error::Base => e
326
+ rescue ::Couchbase::Error::Base => e
327
327
  logger.error("#{e.class}: #{e.message}") if logger
328
328
  raise if @raise_errors
329
329
  false
data/lib/couchbase.rb CHANGED
@@ -26,6 +26,7 @@ require 'couchbase/bucket'
26
26
  require 'couchbase/view_row'
27
27
  require 'couchbase/view'
28
28
  require 'couchbase/result'
29
+ require 'couchbase/cluster'
29
30
 
30
31
  # Couchbase ruby client
31
32
  module Couchbase
@@ -106,6 +106,9 @@ module Couchbase
106
106
  req.on_body do |body|
107
107
  res = MultiJson.load(body.value)
108
108
  res["rows"].each do |obj|
109
+ if obj['doc']
110
+ obj['doc']['value'] = obj['doc'].delete('json')
111
+ end
109
112
  doc = ViewRow.wrap(self, obj)
110
113
  key = doc.id.sub(/^_design\//, '')
111
114
  next if self.environment == :production && key =~ /dev_/
@@ -117,17 +120,6 @@ module Couchbase
117
120
  async? ? nil : docmap
118
121
  end
119
122
 
120
- # Fetch all documents from the bucket.
121
- #
122
- # @since 1.2.0
123
- #
124
- # @param [Hash] params Params for Couchbase +/_all_docs+ query
125
- #
126
- # @return [Couchbase::View] View object
127
- def all_docs(params = {})
128
- View.new(self, "_all_docs", params)
129
- end
130
-
131
123
  # Update or create design doc with supplied views
132
124
  #
133
125
  # @since 1.2.0
@@ -238,6 +230,9 @@ module Couchbase
238
230
  # end
239
231
  # end
240
232
  def flush
233
+ if !async? && block_given?
234
+ raise ArgumentError, "synchronous mode doesn't support callbacks"
235
+ end
241
236
  req = make_http_request("/pools/default/buckets/#{bucket}/controller/doFlush",
242
237
  :type => :management, :method => :post, :extended => true)
243
238
  res = nil
@@ -0,0 +1,105 @@
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
+ module Couchbase
19
+
20
+ class Cluster
21
+
22
+ # Establish connection to the cluster for administration
23
+ #
24
+ # @param [Hash] options The connection parameter
25
+ # @option options [String] :username The username
26
+ # @option options [String] :password The password
27
+ # @option options [String] :pool ("default") The pool name
28
+ # @option options [String] :hostname ("localhost") The hostname
29
+ # @option options [String] :port (8091) The port
30
+ def initialize(options = {})
31
+ if options[:username].nil? || options[:password].nil?
32
+ raise ArgumentError, "username and password mandatory to connect to the cluster"
33
+ end
34
+ @connection = Bucket.new(options.merge(:type => :cluster))
35
+ end
36
+
37
+ # Create data bucket
38
+ #
39
+ # @param [String] name The name of the bucket
40
+ # @param [Hash] options The bucket parameters
41
+ # @option options [String] :bucket_type ("couchbase") The type of the
42
+ # bucket. Possible values are "memcached" and "couchbase".
43
+ # @option options [Fixnum] :ram_quota (100) The RAM quota in megabytes.
44
+ # @option options [Fixnum] :replica_number (1) The number of replicas of
45
+ # each document
46
+ # @option options [String] :auth_type ("sasl") The authentication type.
47
+ # Possible values are "sasl" and "none". Note you should specify free
48
+ # port for "none"
49
+ # @option options [Fixnum] :proxy_port The port for moxi
50
+ def create_bucket(name, options = {})
51
+ defaults = {
52
+ :type => "couchbase",
53
+ :ram_quota => 100,
54
+ :replica_number => 1,
55
+ :auth_type => "sasl",
56
+ :sasl_password => "",
57
+ :proxy_port => nil
58
+ }
59
+ options = defaults.merge(options)
60
+ params = {"name" => name}
61
+ params["bucketType"] = options[:type]
62
+ params["ramQuotaMB"] = options[:ram_quota]
63
+ params["replicaNumber"] = options[:replica_number]
64
+ params["authType"] = options[:auth_type]
65
+ params["saslPassword"] = options[:sasl_password]
66
+ params["proxyPort"] = options[:proxy_port]
67
+ payload = Utils.encode_params(params.reject!{|k, v| v.nil?})
68
+ request = @connection.make_http_request("/pools/default/buckets",
69
+ :content_type => "application/x-www-form-urlencoded",
70
+ :type => :management,
71
+ :method => :post,
72
+ :extended => true,
73
+ :body => payload)
74
+ response = nil
75
+ request.on_body do |r|
76
+ response = r
77
+ response.instance_variable_set("@operation", :create_bucket)
78
+ yield(response) if block_given?
79
+ end
80
+ request.continue
81
+ response
82
+ end
83
+
84
+ # Delete the data bucket
85
+ #
86
+ # @param [String] name The name of the bucket
87
+ # @param [Hash] options
88
+ def delete_bucket(name, options = {})
89
+ request = @connection.make_http_request("/pools/default/buckets/#{name}",
90
+ :type => :management,
91
+ :method => :delete,
92
+ :extended => true)
93
+ response = nil
94
+ request.on_body do |r|
95
+ response = r
96
+ response.instance_variable_set("@operation", :delete_bucket)
97
+ yield(response) if block_given?
98
+ end
99
+ request.continue
100
+ response
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -19,11 +19,8 @@ module Couchbase
19
19
 
20
20
  class Utils
21
21
 
22
- def self.build_query(uri, params = nil)
23
- uri = uri.dup
24
- return uri if params.nil? || params.empty?
25
- uri << "?"
26
- uri << params.map do |k, v|
22
+ def self.encode_params(params)
23
+ params.map do |k, v|
27
24
  next if !v && k.to_s == "group"
28
25
  if %w{key keys startkey endkey start_key end_key}.include?(k.to_s)
29
26
  v = MultiJson.dump(v)
@@ -36,6 +33,12 @@ module Couchbase
36
33
  end.compact.join("&")
37
34
  end
38
35
 
36
+ def self.build_query(uri, params = nil)
37
+ uri = uri.dup
38
+ return uri if params.nil? || params.empty?
39
+ uri << "?" << encode_params(params)
40
+ end
41
+
39
42
  def self.escape(s)
40
43
  s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/nu) {
41
44
  '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
@@ -17,5 +17,5 @@
17
17
 
18
18
  # Couchbase ruby client
19
19
  module Couchbase
20
- VERSION = "1.2.0.beta"
20
+ VERSION = "1.2.0"
21
21
  end
@@ -15,6 +15,8 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
+ require 'base64'
19
+
18
20
  module Couchbase
19
21
 
20
22
  module Error
@@ -27,6 +29,28 @@ module Couchbase
27
29
  super("#{prefix}#{from}: #{reason}")
28
30
  end
29
31
  end
32
+
33
+ class HTTP < Base
34
+ attr_reader :type, :reason
35
+
36
+ def parse_body!
37
+ if @body
38
+ hash = MultiJson.load(@body)
39
+ @type = hash["error"]
40
+ @reason = hash["reason"]
41
+ end
42
+ rescue MultiJson::DecodeError
43
+ @type = @reason = nil
44
+ end
45
+
46
+ def to_s
47
+ str = super
48
+ if @type || @reason
49
+ str.sub(/ \(/, ": #{[@type, @reason].compact.join(": ")} (")
50
+ end
51
+ str
52
+ end
53
+ end
30
54
  end
31
55
 
32
56
  # This class implements Couchbase View execution
@@ -50,7 +74,7 @@ module Couchbase
50
74
  def initialize(bucket, endpoint, params = {})
51
75
  @bucket = bucket
52
76
  @endpoint = endpoint
53
- @params = params
77
+ @params = {:connection_timeout => 75_000}.merge(params)
54
78
  @wrapper_class = params.delete(:wrapper_class) || ViewRow
55
79
  unless @wrapper_class.respond_to?(:wrap)
56
80
  raise ArgumentError, "wrapper class should reposond to :wrap, check the options"
@@ -142,8 +166,14 @@ module Couchbase
142
166
  # the document and store them in {ViewRow#meta} hash.
143
167
  #
144
168
  # @param [Hash] params parameters for Couchbase query.
145
- # @option params [true, false] :include_docs (false) Include the full
146
- # content of the documents in the return.
169
+ # @option params [true, false] :include_docs (false) Include the
170
+ # full content of the documents in the return. Note that the document
171
+ # is fetched from the in memory cache where it may have been changed
172
+ # or even deleted. See also +:quiet+ parameter below to control error
173
+ # reporting during fetch.
174
+ # @option params [true, false] :quiet (true) Do not raise error if
175
+ # associated document not found in the memory. If the parameter +true+
176
+ # will use +nil+ value instead.
147
177
  # @option params [true, false] :descending (false) Return the documents
148
178
  # in descending by key order
149
179
  # @option params [String, Fixnum, Hash, Array] :key Return only
@@ -175,7 +205,7 @@ module Couchbase
175
205
  # response stream.
176
206
  # :stop:: Stop immediately when an error condition occurs. No
177
207
  # further view information will be returned.
178
- # @option params [Fixnum] :connection_timeout (60000) Timeout before the
208
+ # @option params [Fixnum] :connection_timeout (75000) Timeout before the
179
209
  # view request is dropped (milliseconds)
180
210
  # @option params [true, false] :reduce (true) Use the reduction function
181
211
  # @option params [true, false] :group (false) Group the results using
@@ -236,6 +266,11 @@ module Couchbase
236
266
  body = MultiJson.dump(body) unless body.is_a?(String)
237
267
  options.update(:body => body, :method => params.delete(:method) || :post)
238
268
  end
269
+ include_docs = params.delete(:include_docs)
270
+ quiet = true
271
+ if params.has_key?(:quiet)
272
+ quiet = params.delete(:quiet)
273
+ end
239
274
  path = Utils.build_query(@endpoint, params)
240
275
  request = @bucket.make_http_request(path, options)
241
276
  res = []
@@ -260,6 +295,17 @@ module Couchbase
260
295
  raise Error::View.new(from, reason)
261
296
  end
262
297
  else
298
+ if include_docs
299
+ val, flags, cas = @bucket.get(obj['id'], :extended => true, :quiet => quiet)
300
+ obj['doc'] = {
301
+ 'value' => val,
302
+ 'meta' => {
303
+ 'id' => obj['id'],
304
+ 'flags' => flags,
305
+ 'cas' => cas
306
+ }
307
+ }
308
+ end
263
309
  if block_given?
264
310
  yield @wrapper_class.wrap(@bucket, obj)
265
311
  else
@@ -270,7 +316,7 @@ module Couchbase
270
316
  # run event loop until the terminating chunk will be found
271
317
  # last_res variable keeps latest known chunk of the result
272
318
  last_res = nil
273
- loop do
319
+ while true
274
320
  # feed response received chunks to the parser
275
321
  while r = res.shift
276
322
  if r.error