riak-client 1.0.0.beta → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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