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.
- data/.gitignore +7 -4
- data/Gemfile +12 -17
- data/Guardfile +1 -1
- data/LICENSE +16 -0
- data/README.markdown +178 -0
- data/RELEASE_NOTES.md +99 -0
- data/Rakefile +25 -1
- data/erl_src/riak_kv_test014_backend.beam +0 -0
- data/erl_src/riak_kv_test014_backend.erl +189 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +37 -19
- data/lib/riak/client.rb +322 -272
- data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
- data/lib/riak/client/decaying.rb +28 -0
- data/lib/riak/client/excon_backend.rb +27 -11
- data/lib/riak/client/http_backend.rb +71 -2
- data/lib/riak/client/http_backend/configuration.rb +17 -3
- data/lib/riak/client/http_backend/transport_methods.rb +3 -3
- data/lib/riak/client/net_http_backend.rb +18 -14
- data/lib/riak/client/node.rb +111 -0
- data/lib/riak/client/pool.rb +180 -0
- data/lib/riak/client/protobuffs_backend.rb +15 -5
- data/lib/riak/client/search.rb +9 -3
- data/lib/riak/link.rb +5 -7
- data/lib/riak/locale/en.yml +1 -0
- data/lib/riak/node/configuration.rb +1 -0
- data/lib/riak/node/defaults.rb +19 -6
- data/lib/riak/node/generation.rb +9 -2
- data/lib/riak/node/log.rb +2 -2
- data/lib/riak/node/version.rb +22 -16
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/serializers.rb +1 -1
- data/lib/riak/test_server.rb +10 -2
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +3 -3
- data/spec/failover/failover.rb +59 -0
- data/spec/integration/riak/http_backends_spec.rb +2 -2
- data/spec/integration/riak/node_spec.rb +16 -24
- data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
- data/spec/integration/riak/test_server_spec.rb +4 -3
- data/spec/integration/riak/threading_spec.rb +193 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
- data/spec/riak/bucket_spec.rb +2 -1
- data/spec/riak/client_spec.rb +80 -181
- data/spec/riak/excon_backend_spec.rb +3 -2
- data/spec/riak/http_backend/configuration_spec.rb +37 -5
- data/spec/riak/http_backend/object_methods_spec.rb +1 -1
- data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
- data/spec/riak/http_backend_spec.rb +53 -3
- data/spec/riak/map_reduce_spec.rb +1 -1
- data/spec/riak/net_http_backend_spec.rb +1 -2
- data/spec/riak/node_spec.rb +173 -0
- data/spec/riak/pool_spec.rb +306 -0
- data/spec/riak/robject_spec.rb +8 -4
- data/spec/riak/search_spec.rb +66 -15
- data/spec/riak/serializers_spec.rb +12 -1
- data/spec/spec_helper.rb +9 -1
- data/spec/support/http_backend_implementation_examples.rb +6 -2
- data/spec/support/sometimes.rb +46 -0
- data/spec/support/test_server.rb +50 -19
- data/spec/support/unified_backend_examples.rb +11 -10
- data/spec/support/version_filter.rb +14 -0
- metadata +40 -29
- data/lib/active_support/cache/riak_store.rb +0 -2
- data/lib/riak/cache_store.rb +0 -84
- data/lib/riak/client/pump.rb +0 -30
- data/lib/riak/util/fiber1.8.rb +0 -48
- 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
|
-
|
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
|
-
|
85
|
+
@socket ||= new_socket
|
78
86
|
end
|
79
87
|
|
80
88
|
def new_socket
|
81
|
-
socket = TCPSocket.new(@
|
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
|
88
|
-
|
97
|
+
@socket.close if @socket && !@socket.closed?
|
98
|
+
@socket = nil
|
89
99
|
end
|
90
100
|
|
91
101
|
UINTMAX = 0xffffffff
|
data/lib/riak/client/search.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
61
|
+
"/riak/#{escape(bucket)}" + (key.blank? ? "" : "/#{escape(key)}")
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
data/lib/riak/locale/en.yml
CHANGED
@@ -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
|
data/lib/riak/node/defaults.rb
CHANGED
@@ -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
|
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 =>
|
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 =>
|
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 =>
|
46
|
-
:heap_word_limit =>
|
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
|
66
|
+
# Based on Riak 1.1.
|
54
67
|
VM_DEFAULTS = {
|
55
68
|
"+K" => true,
|
56
69
|
"+A" => 64,
|
data/lib/riak/node/generation.rb
CHANGED
@@ -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
|
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
|
data/lib/riak/node/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|