riak-client 1.0.0.beta → 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 (69) hide show
  1. data/.gitignore +7 -4
  2. data/Gemfile +12 -17
  3. data/Guardfile +1 -1
  4. data/LICENSE +16 -0
  5. data/README.markdown +178 -0
  6. data/RELEASE_NOTES.md +99 -0
  7. data/Rakefile +25 -1
  8. data/erl_src/riak_kv_test014_backend.beam +0 -0
  9. data/erl_src/riak_kv_test014_backend.erl +189 -0
  10. data/erl_src/riak_kv_test_backend.beam +0 -0
  11. data/erl_src/riak_kv_test_backend.erl +37 -19
  12. data/lib/riak/client.rb +322 -272
  13. data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
  14. data/lib/riak/client/decaying.rb +28 -0
  15. data/lib/riak/client/excon_backend.rb +27 -11
  16. data/lib/riak/client/http_backend.rb +71 -2
  17. data/lib/riak/client/http_backend/configuration.rb +17 -3
  18. data/lib/riak/client/http_backend/transport_methods.rb +3 -3
  19. data/lib/riak/client/net_http_backend.rb +18 -14
  20. data/lib/riak/client/node.rb +111 -0
  21. data/lib/riak/client/pool.rb +180 -0
  22. data/lib/riak/client/protobuffs_backend.rb +15 -5
  23. data/lib/riak/client/search.rb +9 -3
  24. data/lib/riak/link.rb +5 -7
  25. data/lib/riak/locale/en.yml +1 -0
  26. data/lib/riak/node/configuration.rb +1 -0
  27. data/lib/riak/node/defaults.rb +19 -6
  28. data/lib/riak/node/generation.rb +9 -2
  29. data/lib/riak/node/log.rb +2 -2
  30. data/lib/riak/node/version.rb +22 -16
  31. data/lib/riak/robject.rb +19 -3
  32. data/lib/riak/serializers.rb +1 -1
  33. data/lib/riak/test_server.rb +10 -2
  34. data/lib/riak/version.rb +1 -1
  35. data/riak-client.gemspec +3 -3
  36. data/spec/failover/failover.rb +59 -0
  37. data/spec/integration/riak/http_backends_spec.rb +2 -2
  38. data/spec/integration/riak/node_spec.rb +16 -24
  39. data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
  40. data/spec/integration/riak/test_server_spec.rb +4 -3
  41. data/spec/integration/riak/threading_spec.rb +193 -0
  42. data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
  43. data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
  44. data/spec/riak/bucket_spec.rb +2 -1
  45. data/spec/riak/client_spec.rb +80 -181
  46. data/spec/riak/excon_backend_spec.rb +3 -2
  47. data/spec/riak/http_backend/configuration_spec.rb +37 -5
  48. data/spec/riak/http_backend/object_methods_spec.rb +1 -1
  49. data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
  50. data/spec/riak/http_backend_spec.rb +53 -3
  51. data/spec/riak/map_reduce_spec.rb +1 -1
  52. data/spec/riak/net_http_backend_spec.rb +1 -2
  53. data/spec/riak/node_spec.rb +173 -0
  54. data/spec/riak/pool_spec.rb +306 -0
  55. data/spec/riak/robject_spec.rb +8 -4
  56. data/spec/riak/search_spec.rb +66 -15
  57. data/spec/riak/serializers_spec.rb +12 -1
  58. data/spec/spec_helper.rb +9 -1
  59. data/spec/support/http_backend_implementation_examples.rb +6 -2
  60. data/spec/support/sometimes.rb +46 -0
  61. data/spec/support/test_server.rb +50 -19
  62. data/spec/support/unified_backend_examples.rb +11 -10
  63. data/spec/support/version_filter.rb +14 -0
  64. metadata +40 -29
  65. data/lib/active_support/cache/riak_store.rb +0 -2
  66. data/lib/riak/cache_store.rb +0 -84
  67. data/lib/riak/client/pump.rb +0 -30
  68. data/lib/riak/util/fiber1.8.rb +0 -48
  69. data/spec/integration/riak/cache_store_spec.rb +0 -129
@@ -0,0 +1,180 @@
1
+ require 'thread'
2
+
3
+ module Riak
4
+ class Client
5
+ # A re-entrant thread-safe resource pool. Generates new resources on
6
+ # demand.
7
+ # @private
8
+ class Pool
9
+ # Raised when a taken element should be deleted from the pool.
10
+ class BadResource < RuntimeError
11
+ end
12
+
13
+ # An element of the pool. Comprises an object with an owning
14
+ # thread.
15
+ # @private
16
+ class Element
17
+ attr_accessor :object
18
+ attr_accessor :owner
19
+ def initialize(object)
20
+ @object = object
21
+ @owner = owner
22
+ end
23
+
24
+ # Claims this element of the pool for the current Thread.
25
+ def lock
26
+ self.owner = Thread.current
27
+ end
28
+
29
+ # Is this element locked/claimed?
30
+ def locked?
31
+ not owner.nil?
32
+ end
33
+
34
+ # Releases this element of the pool from the current Thread.
35
+ def unlock
36
+ self.owner = nil
37
+ end
38
+
39
+ # Is this element available for use?
40
+ def unlocked?
41
+ owner.nil?
42
+ end
43
+ end
44
+
45
+ attr_accessor :pool
46
+ attr_accessor :open
47
+ attr_accessor :close
48
+
49
+ # Open is a callable which returns a new object for the pool. Close is
50
+ # called with an object before it is freed.
51
+ def initialize(open, close)
52
+ @open = open
53
+ @close = close
54
+ @lock = Mutex.new
55
+ @iterator = Mutex.new
56
+ @element_released = ConditionVariable.new
57
+ @pool = Set.new
58
+ end
59
+
60
+ # On each element of the pool, calls close(element) and removes it.
61
+ # @private
62
+ def clear
63
+ each_element do |e|
64
+ delete_element e
65
+ end
66
+ end
67
+ alias :close :clear
68
+
69
+ # Deletes an element of the pool. Calls close on its object.
70
+ # Not intendend for external use.
71
+ def delete_element(e)
72
+ @close.call(e.object)
73
+ @lock.synchronize do
74
+ @pool.delete e
75
+ end
76
+ end
77
+
78
+ # Locks each element in turn and closes/deletes elements for which the
79
+ # object passes the block.
80
+ def delete_if
81
+ raise ArgumentError, "block required" unless block_given?
82
+
83
+ each_element do |e|
84
+ if yield e.object
85
+ delete_element e
86
+ end
87
+ end
88
+ end
89
+
90
+ # Acquire an element of the pool. Yields the object. If all
91
+ # elements are claimed, it will create another one.
92
+ # @yield [obj] a block that will perform some action with the
93
+ # element of the pool
94
+ # @yieldparam [Object] resource a resource managed by the pool.
95
+ # Locked for the duration of the block
96
+ # @param [callable] :filter a callable which receives objects and has
97
+ # the opportunity to reject each in turn.
98
+ # @param [Object] :default if no resources are available, use this object
99
+ # instead of calling #open.
100
+ # @private
101
+ def take(opts = {})
102
+ unless block_given?
103
+ raise ArgumentError, "block required"
104
+ end
105
+
106
+ r = nil
107
+ begin
108
+ e = nil
109
+ @lock.synchronize do
110
+ # Find an existing element.
111
+ if f = opts[:filter]
112
+ e = pool.find { |e| e.unlocked? and f.call(e.object) }
113
+ else
114
+ e = pool.find { |e| e.unlocked? }
115
+ end
116
+
117
+ unless e
118
+ # No objects were acceptable
119
+ resource = opts[:default] || @open.call
120
+ e = Element.new(resource)
121
+ pool << e
122
+ end
123
+ e.lock
124
+ end
125
+
126
+ r = yield e.object
127
+ rescue BadResource
128
+ delete_element e
129
+ raise
130
+ ensure
131
+ # Unlock
132
+ if e
133
+ e.unlock
134
+ @element_released.signal
135
+ end
136
+ end
137
+ r
138
+ end
139
+ alias >> take
140
+
141
+ # Iterate over a snapshot of the pool. Yielded objects are locked for the
142
+ # duration of the block. This may block the current thread until elements
143
+ # are released by other threads.
144
+ # @private
145
+ def each_element
146
+ targets = @pool.to_a
147
+ unlocked = []
148
+
149
+ @iterator.synchronize do
150
+ until targets.empty?
151
+ @lock.synchronize do
152
+ unlocked, targets = targets.partition {|e| e.unlocked? }
153
+ unlocked.each {|e| e.lock }
154
+ end
155
+
156
+ unlocked.each do |e|
157
+ begin
158
+ yield e
159
+ ensure
160
+ e.unlock
161
+ end
162
+ end
163
+ @element_released.wait(@iterator) unless targets.empty?
164
+ end
165
+ end
166
+ end
167
+
168
+ # As each_element, but yields objects, not wrapper elements.
169
+ def each
170
+ each_element do |e|
171
+ yield e.object
172
+ end
173
+ end
174
+
175
+ def size
176
+ @lock.synchronize { @pool.size }
177
+ end
178
+ end
179
+ end
180
+ end
@@ -8,6 +8,7 @@ module Riak
8
8
  class Client
9
9
  class ProtobuffsBackend
10
10
  include Util::Translation
11
+ include Util::Escape
11
12
 
12
13
  # Message Codes Enum
13
14
  MESSAGE_CODES = %W[
@@ -46,8 +47,10 @@ module Riak
46
47
  end
47
48
 
48
49
  attr_accessor :client
49
- def initialize(client)
50
+ attr_accessor :node
51
+ def initialize(client, node)
50
52
  @client = client
53
+ @node = node
51
54
  end
52
55
 
53
56
  simple :ping, :PingReq
@@ -67,6 +70,11 @@ module Riak
67
70
  reduce(%w[riak_kv_mapreduce reduce_identity], :arg => {:reduce_phase_only_1 => true}, :keep => true)).map {|p| p.last }
68
71
  end
69
72
 
73
+ # Gracefully shuts down this connection.
74
+ def teardown
75
+ reset_socket
76
+ end
77
+
70
78
  private
71
79
  # Implemented by subclasses
72
80
  def decode_response
@@ -74,18 +82,20 @@ module Riak
74
82
  end
75
83
 
76
84
  def socket
77
- Thread.current[:riakpbc_socket] ||= new_socket
85
+ @socket ||= new_socket
78
86
  end
79
87
 
80
88
  def new_socket
81
- socket = TCPSocket.new(@client.host, @client.pb_port)
89
+ socket = TCPSocket.new(@node.host, @node.pb_port)
82
90
  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
91
+ #TODO: Should we set the client ID here?
92
+ # set_client_id @client.client_id
83
93
  socket
84
94
  end
85
95
 
86
96
  def reset_socket
87
- socket.close if !socket.closed?
88
- Thread.current[:riakpbc_socket] = nil
97
+ @socket.close if @socket && !@socket.closed?
98
+ @socket = nil
89
99
  end
90
100
 
91
101
  UINTMAX = 0xffffffff
@@ -20,7 +20,9 @@ module Riak
20
20
  def search(*args)
21
21
  options = args.extract_options!
22
22
  index, query = args[-2], args[-1] # Allows nil index, while keeping it as firstargument
23
- http.search(index, query, options)
23
+ http do |h|
24
+ h.search(index, query, options)
25
+ end
24
26
  end
25
27
  alias :select :search
26
28
 
@@ -46,7 +48,9 @@ module Riak
46
48
  end
47
49
  end
48
50
  end
49
- http.update_search_index(index, xml.target!)
51
+ http do |h|
52
+ h.update_search_index(index, xml.target!)
53
+ end
50
54
  true
51
55
  end
52
56
  alias :add_doc :index
@@ -76,7 +80,9 @@ module Riak
76
80
  end
77
81
  end
78
82
  end
79
- http.update_search_index(index, xml.target!)
83
+ http do |h|
84
+ h.update_search_index(index, xml.target!)
85
+ end
80
86
  true
81
87
  end
82
88
  alias :delete_doc :remove
data/lib/riak/link.rb CHANGED
@@ -53,14 +53,12 @@ module Riak
53
53
 
54
54
  # @return [String] the URL (relative or absolute) of the related resource
55
55
  def url(new_scheme=false)
56
- if @bucket
57
- if new_scheme
58
- "/buckets/#{escape(bucket)}" + (key.blank? ? "" : "/keys/#{escape(key)}")
59
- else
60
- "/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
61
- end
56
+ return @url unless @bucket
57
+
58
+ if new_scheme
59
+ "/buckets/#{escape(bucket)}" + (key.blank? ? "" : "/keys/#{escape(key)}")
62
60
  else
63
- @url
61
+ "/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
64
62
  end
65
63
  end
66
64
 
@@ -30,6 +30,7 @@ en:
30
30
  loading_bucket: "while loading bucket '%{name}'"
31
31
  list_buckets: "Riak::Client#buckets is an expensive operation that should not be used in production.\n %{backtrace}"
32
32
  list_keys: "Riak::Bucket#keys is an expensive operation that should not be used in production.\n %{backtrace}"
33
+ luwak_unsupported: "Riak server does not support Luwak. Enable it in app.config before using."
33
34
  missing_block: "A block must be given."
34
35
  missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
35
36
  module_function_pair_required: "function must have two elements when an array"
@@ -156,6 +156,7 @@ module Riak
156
156
  env[:eleveldb][:data_root] ||= (data + 'leveldb').expand_path.to_s
157
157
  env[:merge_index][:data_root] ||= (data + 'merge_index').expand_path.to_s
158
158
  env[:riak_core][:ring_state_dir] ||= ring.expand_path.to_s
159
+ env[:riak_core][:slide_private_dir] ||= (data + 'slide-data').expand_path.to_s
159
160
  NODE_DIRECTORIES.each do |dir|
160
161
  next if [:ring, :pipe].include?(dir)
161
162
  env[:riak_core][:"platform_#{dir}_dir"] ||= send(dir).to_s
@@ -2,7 +2,7 @@ require 'riak/core_ext/deep_dup'
2
2
 
3
3
  module Riak
4
4
  class Node
5
- # Settings based on Riak master/1.0.
5
+ # Settings based on Riak 1.1.
6
6
  ENV_DEFAULTS = {
7
7
  :riak_core => {
8
8
  :ring_creation_size => 64
@@ -16,11 +16,13 @@ module Riak
16
16
  :js_max_vm_mem => 8,
17
17
  :js_thread_stack => 16,
18
18
  :riak_kv_stat => true,
19
- :legacy_stats => false,
19
+ :legacy_stats => true,
20
20
  :vnode_vclocks => true,
21
21
  :http_url_encoding => :on,
22
22
  :legacy_keylisting => false,
23
23
  :mapred_system => :pipe,
24
+ :mapred_2i_pipe => true,
25
+ :listkeys_backpressure => true,
24
26
  :add_paths => []
25
27
  },
26
28
  :riak_search => {
@@ -36,21 +38,32 @@ module Riak
36
38
  :eleveldb => {},
37
39
  :bitcask => {},
38
40
  :lager => {
39
- :crash_log_size => 65536,
41
+ :crash_log_size => 10485760,
42
+ :crash_log_msg_size => 65536,
43
+ :crash_log_date => "$D0",
44
+ :crash_log_count => 5,
40
45
  :error_logger_redirect => true
41
46
  },
42
47
  :riak_sysmon => {
43
48
  :process_limit => 30,
44
49
  :port_limit => 30,
45
- :gc_ms_limit => 50,
46
- :heap_word_limit => 10485760
50
+ :gc_ms_limit => 100,
51
+ :heap_word_limit => 40111000,
52
+ :busy_port => true,
53
+ :busy_dist_port => true
47
54
  },
48
55
  :sasl => {
49
56
  :sasl_error_logger => false
57
+ },
58
+ :riak_control => {
59
+ :enabled => false,
60
+ :auth => :userlist,
61
+ :userlist => {"user" => "pass"},
62
+ :admin => true
50
63
  }
51
64
  }.freeze
52
65
 
53
- # Based on Riak master/1.0.
66
+ # Based on Riak 1.1.
54
67
  VM_DEFAULTS = {
55
68
  "+K" => true,
56
69
  "+A" => 64,
@@ -29,7 +29,13 @@ module Riak
29
29
  def drop
30
30
  was_started = started?
31
31
  stop if was_started
32
- data.children.each {|dir| dir.children.each {|c| c.rmtree } }
32
+ data.children.each do |item|
33
+ if item.directory?
34
+ item.children.each {|c| c.rmtree }
35
+ else
36
+ item.delete
37
+ end
38
+ end
33
39
  start if was_started
34
40
  end
35
41
 
@@ -76,7 +82,8 @@ module Riak
76
82
  line.sub!(/(RUNNER_ETC_DIR=)(.*)/, '\1' + etc.to_s)
77
83
  line.sub!(/(RUNNER_USER=)(.*)/, '\1')
78
84
  line.sub!(/(RUNNER_LOG_DIR=)(.*)/, '\1' + log.to_s)
79
- line.sub!(/(PIPE_DIR=)(.*)/, '\1' + pipe.to_s)
85
+ line.sub!(/(PIPE_DIR=)(.*)/, '\1' + pipe.to_s + "/") # PIPE_DIR must have a trailing slash
86
+ line.sub!(/(PLATFORM_DATA_DIR=)(.*)/, '\1' + data.to_s)
80
87
  line.sub!('grep "$RUNNER_BASE_DIR/.*/[b]eam"', 'grep "$RUNNER_ETC_DIR/app.config"')
81
88
  if line.strip == "RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*}"
82
89
  line = "RUNNER_BASE_DIR=#{source.parent.to_s}\n"
data/lib/riak/node/log.rb CHANGED
@@ -23,8 +23,8 @@ module Riak
23
23
  def expand_log_level(level)
24
24
  case level
25
25
  when Range
26
- first = LAGER_LEVELS.index(level.begin) || 0
27
- last = LAGER_LEVELS.index(level.end) || -1
26
+ first = LAGER_LEVELS.index(level.begin.to_sym) || 0
27
+ last = LAGER_LEVELS.index(level.end.to_sym) || -1
28
28
  LAGER_LEVELS[first..last]
29
29
  when Symbol
30
30
  level
@@ -2,10 +2,18 @@ require 'pathname'
2
2
 
3
3
  module Riak
4
4
  class Node
5
+ # @return [String] the version of the Riak node
5
6
  def version
6
7
  @version ||= configure_version
7
8
  end
8
9
 
10
+ # @return [Pathname] the location of Riak installation, aka RUNNER_BASE_DIR
11
+ def base_dir
12
+ @base_dir ||= configure_base_dir
13
+ end
14
+
15
+ protected
16
+ # Detects the Riak version from the generated release
9
17
  def configure_version
10
18
  if base_dir
11
19
  versions = (base_dir + 'releases' + 'start_erl.data').read
@@ -13,24 +21,22 @@ module Riak
13
21
  end
14
22
  end
15
23
 
16
- def base_dir
17
- @base_dir ||= configure_base_dir
18
- end
19
-
24
+ # Determines the base_dir from source control script
20
25
  def configure_base_dir
21
- pattern = /^RUNNER_BASE_DIR=(.*)/
22
- lines = control_script.readlines.grep(pattern)
23
- if lines.empty?
24
- nil
26
+ # Use the script from the source directory so we don't require
27
+ # it to be generated first.
28
+ (source + control_script_name).each_line.find {|l| l =~ /^RUNNER_BASE_DIR=(.*)/ }
29
+
30
+ # There should only be one matching line, so the contents of $1
31
+ # will be the matched path. If there's nothing matched, we
32
+ # return nil.
33
+ case $1
34
+ when '${RUNNER_SCRIPT_DIR%/*}'
35
+ source.parent
36
+ when String
37
+ Pathname.new($1).expand_path
25
38
  else
26
- line = lines.first
27
- case line
28
- when /^RUNNER_BASE_DIR=$\{RUNNER_SCRIPT_DIR%\/*\}/
29
- source.parent
30
- else
31
- path = pattern.match(line)[1]
32
- Pathname.new(path).expand_path if File.directory?(path)
33
- end
39
+ nil
34
40
  end
35
41
  end
36
42
  end