monga 0.0.2 → 0.0.3
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 +1 -0
- data/.travis.yml +1 -0
- data/README.md +59 -3
- data/lib/monga/client.rb +51 -6
- data/lib/monga/clients/master_slave_client.rb +0 -5
- data/lib/monga/clients/replica_set_client.rb +32 -71
- data/lib/monga/clients/single_instance_client.rb +53 -0
- data/lib/monga/collection.rb +102 -41
- data/lib/monga/connection.rb +38 -13
- data/lib/monga/connection_pool.rb +6 -17
- data/lib/monga/connections/buffer.rb +33 -0
- data/lib/monga/connections/em_connection.rb +25 -56
- data/lib/monga/connections/em_proxy_connection.rb +80 -0
- data/lib/monga/connections/fibered_connection.rb +26 -0
- data/lib/monga/connections/fibered_proxy_connection.rb +23 -0
- data/lib/monga/connections/proxy_connection.rb +4 -0
- data/lib/monga/connections/tcp_connection.rb +57 -0
- data/lib/monga/cursor.rb +197 -95
- data/lib/monga/database.rb +175 -60
- data/lib/monga/{requests → protocol}/delete.rb +1 -2
- data/lib/monga/{requests → protocol}/get_more.rb +1 -1
- data/lib/monga/{requests → protocol}/insert.rb +1 -2
- data/lib/monga/{requests → protocol}/kill_cursors.rb +1 -1
- data/lib/monga/{requests → protocol}/query.rb +3 -3
- data/lib/monga/{requests → protocol}/update.rb +1 -1
- data/lib/monga/request.rb +27 -23
- data/lib/monga/utils/constants.rb +5 -0
- data/lib/monga/utils/exceptions.rb +11 -0
- data/lib/monga.rb +19 -11
- data/monga.gemspec +2 -2
- data/spec/helpers/mongodb.rb +115 -38
- data/spec/monga/block/collection_spec.rb +172 -0
- data/spec/monga/block/cursor_spec.rb +160 -0
- data/spec/monga/block/database_spec.rb +80 -0
- data/spec/monga/block/single_instance_client_spec.rb +31 -0
- data/spec/monga/em/collection_spec.rb +308 -0
- data/spec/monga/em/cursor_spec.rb +256 -0
- data/spec/monga/em/database_spec.rb +140 -0
- data/spec/monga/em/replica_set_client_spec.rb +86 -0
- data/spec/monga/em/single_instance_client_spec.rb +28 -0
- data/spec/monga/sync/collection_spec.rb +247 -0
- data/spec/monga/sync/cursor_spec.rb +211 -0
- data/spec/monga/sync/database_spec.rb +110 -0
- data/spec/monga/sync/replica_set_client_spec.rb +54 -0
- data/spec/monga/sync/single_instance_client_spec.rb +25 -0
- data/spec/spec_helper.rb +2 -20
- metadata +50 -38
- data/lib/monga/clients/client.rb +0 -24
- data/lib/monga/connections/primary.rb +0 -46
- data/lib/monga/connections/secondary.rb +0 -13
- data/lib/monga/exceptions.rb +0 -9
- data/lib/monga/miner.rb +0 -72
- data/lib/monga/response.rb +0 -11
- data/spec/helpers/truncate.rb +0 -15
- data/spec/monga/collection_spec.rb +0 -448
- data/spec/monga/connection_pool_spec.rb +0 -50
- data/spec/monga/connection_spec.rb +0 -64
- data/spec/monga/cursor_spec.rb +0 -186
- data/spec/monga/database_spec.rb +0 -67
- data/spec/monga/replica_set_client_spec.rb +0 -46
- data/spec/monga/requests/delete_spec.rb +0 -0
- data/spec/monga/requests/insert_spec.rb +0 -0
- data/spec/monga/requests/query_spec.rb +0 -28
@@ -0,0 +1,33 @@
|
|
1
|
+
module Monga::Connections
|
2
|
+
class Buffer
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
attr_reader :buffer
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@buffer = ""
|
9
|
+
@positon = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def append(data)
|
13
|
+
@buffer += data
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
while true
|
18
|
+
size = @buffer.size
|
19
|
+
if size > Monga::HEADER_SIZE
|
20
|
+
msg_length = @buffer[0, 4].unpack("L").first
|
21
|
+
if msg_length && size >= msg_length
|
22
|
+
data = @buffer.slice!(0, msg_length)
|
23
|
+
yield data.unpack("LLLLLQLLa*")
|
24
|
+
else
|
25
|
+
break
|
26
|
+
end
|
27
|
+
else
|
28
|
+
break
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -2,61 +2,29 @@ module Monga::Connections
|
|
2
2
|
class EMConnection < EM::Connection
|
3
3
|
include EM::Deferrable
|
4
4
|
|
5
|
-
class Buffer
|
6
|
-
include Enumerable
|
7
|
-
|
8
|
-
attr_reader :buffer
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@buffer = ""
|
12
|
-
@positon = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def append(data)
|
16
|
-
@buffer += data
|
17
|
-
end
|
18
|
-
|
19
|
-
def each
|
20
|
-
while true
|
21
|
-
size = @buffer.size
|
22
|
-
if size > Monga::HEADER_SIZE
|
23
|
-
msg_length = @buffer[0, 4].unpack("L").first
|
24
|
-
if msg_length && size >= msg_length
|
25
|
-
data = @buffer.slice!(0, msg_length)
|
26
|
-
yield data.unpack("LLLLLQLLa*")
|
27
|
-
else
|
28
|
-
break
|
29
|
-
end
|
30
|
-
else
|
31
|
-
break
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
5
|
attr_reader :responses, :host, :port
|
38
6
|
|
39
|
-
def initialize(
|
40
|
-
@host =
|
41
|
-
@port =
|
7
|
+
def initialize(host, port, timeout)
|
8
|
+
@host = host
|
9
|
+
@port = port
|
10
|
+
@timeout = timeout
|
42
11
|
@reactor_running = true
|
43
12
|
@responses = {}
|
44
13
|
end
|
45
14
|
|
46
|
-
def self.connect(
|
47
|
-
host
|
48
|
-
port = opts[:port] ||= Monga::DEFAULT_PORT
|
49
|
-
|
50
|
-
EM.connect(host, port, self, opts)
|
15
|
+
def self.connect(host, port, timeout)
|
16
|
+
EM.connect(host, port, self, host, port, timeout)
|
51
17
|
end
|
52
18
|
|
53
19
|
def send_command(msg, request_id=nil, &cb)
|
20
|
+
# Reconnect is a hack for testing.
|
21
|
+
# We are stopping EvenMachine for each test.
|
22
|
+
# This hack reconnects to Mongo on first query
|
54
23
|
reconnect unless @connected
|
55
24
|
|
56
25
|
callback do
|
57
26
|
send_data msg
|
58
27
|
end
|
59
|
-
|
60
28
|
@responses[request_id] = cb if cb
|
61
29
|
end
|
62
30
|
|
@@ -99,22 +67,21 @@ module Monga::Connections
|
|
99
67
|
end
|
100
68
|
end
|
101
69
|
|
102
|
-
def force_reconnect(host, port)
|
103
|
-
@connected = false
|
104
|
-
@host = host
|
105
|
-
@port = port
|
106
|
-
end
|
107
|
-
|
108
70
|
def connected?
|
109
|
-
|
71
|
+
reconnect unless @reactor_running
|
110
72
|
@connected || false
|
111
73
|
end
|
112
74
|
|
113
75
|
def unbind
|
76
|
+
@connected = false
|
114
77
|
Monga.logger.debug("Lost connection #{@host}:#{@port}")
|
115
78
|
|
116
|
-
@responses.each
|
117
|
-
|
79
|
+
@responses.keys.each do |k|
|
80
|
+
cb = @responses.delete k
|
81
|
+
err = Monga::Exceptions::Disconnected.new("Disconnected from #{@host}:#{@port}")
|
82
|
+
cb.call(err)
|
83
|
+
end
|
84
|
+
|
118
85
|
@primary = false
|
119
86
|
@pending_for_reconnect = false
|
120
87
|
set_deferred_status(nil)
|
@@ -129,21 +96,23 @@ module Monga::Connections
|
|
129
96
|
@reactor_running = false
|
130
97
|
end
|
131
98
|
|
132
|
-
def
|
99
|
+
def primary?
|
133
100
|
@primary || false
|
134
101
|
end
|
135
102
|
|
136
|
-
def is_master?
|
137
|
-
|
138
|
-
req = Monga::
|
103
|
+
def is_master?
|
104
|
+
reconnect unless @connected
|
105
|
+
req = Monga::Protocol::Query.new(self, "admin", "$cmd", query: {"isMaster" => 1}, limit: 1)
|
139
106
|
command = req.command
|
140
107
|
request_id = req.request_id
|
141
108
|
@responses[request_id] = proc do |data|
|
142
|
-
resp = req.parse_response(data)
|
143
|
-
if Exception ===
|
109
|
+
err, resp = req.parse_response(data)
|
110
|
+
if Exception === err
|
144
111
|
@primary = false
|
112
|
+
yield nil
|
145
113
|
else
|
146
114
|
@primary = resp.last.first["ismaster"]
|
115
|
+
yield @primary ? :primary : :secondary
|
147
116
|
end
|
148
117
|
end
|
149
118
|
send_data command
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Monga::Connections
|
2
|
+
# ProxyConnection accepts requests when ReplicaSetClient didn't know where to send requests.
|
3
|
+
# I.E. when client is just initialized here is no any established connections,
|
4
|
+
# so client waits for the connection ready to accept requests.
|
5
|
+
# Also, when primary is down it will collect request while nodes are voting.
|
6
|
+
# Importaint to say, that requests will be stored in this object only for `timeout` period.
|
7
|
+
class EMProxyConnection
|
8
|
+
# Pause while searching server in seconds
|
9
|
+
WAIT = 0.1
|
10
|
+
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
@timeout = @client.timeout
|
14
|
+
@requests = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# If timeout is defined then collect request and start timout.
|
18
|
+
# If timout is not defined or zero then return exception.
|
19
|
+
def send_command(msg, request_id = nil, &cb)
|
20
|
+
if @timeout && @timeout > 0
|
21
|
+
@requests[request_id] = [msg, cb] if cb
|
22
|
+
set_timeout
|
23
|
+
find_server!
|
24
|
+
else
|
25
|
+
error = Monga::Exceptions::Disconnected.new "Can't find appropriate server (all disconnected)"
|
26
|
+
cb.call(error) if cb
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# If timeout happend send exception to all collected requests.
|
31
|
+
def set_timeout
|
32
|
+
unless @pending_timeout
|
33
|
+
@pending_timeout = true
|
34
|
+
EM.add_timer(@timeout) do
|
35
|
+
@pending_timeout = false
|
36
|
+
@requests.keys.each do |request_id|
|
37
|
+
msg, cb = @requests.delete request_id
|
38
|
+
error = Monga::Exceptions::Disconnected.new "Can't find appropriate server (all disconnected)"
|
39
|
+
cb.call(error)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Find server unless server is found
|
46
|
+
def find_server!
|
47
|
+
if !@pending_server && @pending_timeout
|
48
|
+
@pending_server = true
|
49
|
+
_count = 0
|
50
|
+
@client.clients.each do |client|
|
51
|
+
client.force_status! do |status|
|
52
|
+
if status == :primary && [:primary, :primary_preferred, :secondary_preferred].include?(@client.read_pref)
|
53
|
+
@pending_server = false
|
54
|
+
server_found!
|
55
|
+
elsif status == :secondary && [:secondary, :primary_preferred, :secondary_preferred].include?(@client.read_pref)
|
56
|
+
@pending_server = false
|
57
|
+
server_found!
|
58
|
+
else
|
59
|
+
EM.add_timer(WAIT) do
|
60
|
+
EM.next_tick do
|
61
|
+
@pending_server = false if (_count +=1) == @client.clients.size
|
62
|
+
find_server!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# YEEEHA! Send all collected requests back to client
|
71
|
+
def server_found!
|
72
|
+
@pending_timeout = false
|
73
|
+
@requests.keys.each do |request_id|
|
74
|
+
msg, blk = @requests.delete request_id
|
75
|
+
@client.aquire_connection.send_command(msg, request_id, &blk)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'fiber'
|
2
|
+
|
3
|
+
class Fiber
|
4
|
+
alias :call :resume
|
5
|
+
end
|
6
|
+
|
7
|
+
module Monga::Connections
|
8
|
+
class FiberedConnection < EMConnection
|
9
|
+
def send_command(msg, request_id=nil, &cb)
|
10
|
+
fib = Fiber.current
|
11
|
+
reconnect unless @connected
|
12
|
+
|
13
|
+
callback do
|
14
|
+
send_data msg
|
15
|
+
end
|
16
|
+
|
17
|
+
if cb
|
18
|
+
reconnect unless @connected
|
19
|
+
@responses[request_id] = fib
|
20
|
+
res = Fiber.yield
|
21
|
+
raise res if Exception === res
|
22
|
+
cb.call(res)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Monga::Connections
|
2
|
+
class FiberedProxyConnection < EMProxyConnection
|
3
|
+
def send_command(msg, request_id = nil, &cb)
|
4
|
+
if @timeout && @timeout > 0
|
5
|
+
@requests[request_id] = [msg, @fib]
|
6
|
+
@fib = Fiber.current
|
7
|
+
set_timeout
|
8
|
+
find_server!
|
9
|
+
res = Fiber.yield
|
10
|
+
@requests.delete(request_id)
|
11
|
+
raise res if Exception === res
|
12
|
+
@client.aquire_connection.send_command(msg, request_id, &cb)
|
13
|
+
else
|
14
|
+
error = Monga::Exceptions::Disconnected.new "Can't find appropriate server (all disconnected)"
|
15
|
+
cb.call(error) if cb
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def server_found!
|
20
|
+
@fib.resume
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
# Currently blocking mode is very poor.
|
4
|
+
# It is working as is.
|
5
|
+
# Going to support reconnecting and timouts later.
|
6
|
+
# Use it for tests and prototyping. Not the best choice for production.
|
7
|
+
|
8
|
+
module Monga::Connections
|
9
|
+
class TCPConnection
|
10
|
+
def self.connect(host, port, timeout)
|
11
|
+
new(host, port, timeout)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(host, port, timeout)
|
15
|
+
@host, @port, @timout = host, port, timeout
|
16
|
+
@connected = true
|
17
|
+
@buffer = Buffer.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def connected?
|
21
|
+
@connected
|
22
|
+
end
|
23
|
+
|
24
|
+
def socket
|
25
|
+
@socket ||= TCPSocket.new(@host, @port)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fake answer, as far as we are blocking
|
29
|
+
def responses
|
30
|
+
0
|
31
|
+
end
|
32
|
+
|
33
|
+
def send_command(msg, request_id=nil, &cb)
|
34
|
+
socket.send msg.to_s, 0
|
35
|
+
if cb
|
36
|
+
length = socket.read(4)
|
37
|
+
raise Errno::ECONNREFUSED, "Socket returns nothing like it would be closed." unless length
|
38
|
+
@buffer.append(length)
|
39
|
+
l = length.unpack("L").first
|
40
|
+
rest = socket.read(l-4)
|
41
|
+
@buffer.append(rest)
|
42
|
+
@buffer.each do |message|
|
43
|
+
rid = message[2]
|
44
|
+
fail "Returned Request Id is not equal to sended one" if rid != request_id
|
45
|
+
cb.call(message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue Errno::ECONNREFUSED, Errno::EPIPE => e
|
49
|
+
@connected = false
|
50
|
+
@socket = nil
|
51
|
+
if cb
|
52
|
+
err = Monga::Exceptions::Disconnected.new("Disconnected from #{@host}:#{@port}, #{e.message}")
|
53
|
+
cb.call(err)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|