em-handlersocket 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +0 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/README.md +44 -0
- data/Rakefile +2 -0
- data/em-handlersocket.gemspec +24 -0
- data/lib/em-handlersocket/client.rb +80 -0
- data/lib/em-handlersocket/version.rb +5 -0
- data/lib/em-handlersocket.rb +16 -0
- data/spec/client_spec.rb +122 -0
- data/spec/helper.rb +4 -0
- metadata +102 -0
data/.gitignore
ADDED
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
em-handlersocket (0.1.0)
|
5
|
+
eventmachine
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
eventmachine (0.12.11)
|
12
|
+
rspec (2.4.0)
|
13
|
+
rspec-core (~> 2.4.0)
|
14
|
+
rspec-expectations (~> 2.4.0)
|
15
|
+
rspec-mocks (~> 2.4.0)
|
16
|
+
rspec-core (2.4.0)
|
17
|
+
rspec-expectations (2.4.0)
|
18
|
+
diff-lcs (~> 1.1.2)
|
19
|
+
rspec-mocks (2.4.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
em-handlersocket!
|
26
|
+
eventmachine
|
27
|
+
rspec
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# EM-HandlerSocket
|
2
|
+
|
3
|
+
EventMachine client for HandlerSocket MySQL plugin for direct read/write of InnoDB tables. This plugin bypasses any/all SQL parsing, query planner, and other MySQL locks, while giving you the advantage of InnoDB persistence, speed, and the network daemon! Best of all, HandlerSocket plugin runs alongside your regular MySQL engine, which means you still have the full advantage of a SQL console at your disposal.
|
4
|
+
|
5
|
+
[HandlerSocket: The NoSQL MySQL & Ruby](http://www.igvita.com/2011/01/14/handlersocket-the-nosql-mysql-ruby/)
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Plays nicely with the (EventMachine) reactor
|
10
|
+
- No native extensions
|
11
|
+
- Pipelined processing
|
12
|
+
|
13
|
+
## Example: Open & Read from an InnoDB index
|
14
|
+
|
15
|
+
Open the PRIMARY key index on the widgets.user InnoDB table, and query for id == 1.
|
16
|
+
|
17
|
+
EM.run {
|
18
|
+
c = EM::HandlerSocket.new
|
19
|
+
idx = {:id => 0, :db => 'widgets', :table => 'user', :index_name => 'PRIMARY', :columns => 'user_name'}
|
20
|
+
|
21
|
+
d = c.open_index(idx)
|
22
|
+
d.callback do
|
23
|
+
|
24
|
+
d = c.query(:id => 0, :op => '=', :key => '1')
|
25
|
+
d.errback { p 'uh oh!' }
|
26
|
+
d.callback do |data|
|
27
|
+
p ["received data", data]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
## Todo
|
33
|
+
|
34
|
+
- Add support for insert/update/delete
|
35
|
+
|
36
|
+
## Resources
|
37
|
+
|
38
|
+
- [HandlerSocket Protocol](https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL/blob/master/docs-en/protocol.en.txt)
|
39
|
+
- [HandlerSocket @ DeNA](http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html)
|
40
|
+
|
41
|
+
# License
|
42
|
+
|
43
|
+
(The MIT License)
|
44
|
+
Copyright © 2011 Ilya Grigorik
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "em-handlersocket/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "em-handlersocket"
|
7
|
+
s.version = EventMachine::HandlerSocket::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ilya Grigorik"]
|
10
|
+
s.email = ["ilya@igvita.com"]
|
11
|
+
s.homepage = "http://github.com/igrigorik/em-handlersocket"
|
12
|
+
s.summary = "Asynchronous (EventMachine) HandlerSocket client"
|
13
|
+
s.description = s.summary
|
14
|
+
|
15
|
+
s.rubyforge_project = "em-handlersocket"
|
16
|
+
|
17
|
+
s.add_dependency "eventmachine"
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module HandlerSocket
|
3
|
+
|
4
|
+
class Deferrable < EM::DefaultDeferrable
|
5
|
+
attr_accessor :lines, :buffer
|
6
|
+
|
7
|
+
def initialize(lines)
|
8
|
+
@lines = lines
|
9
|
+
@buffer = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def recieve(line)
|
13
|
+
status, cols, data = line.chomp.split("\t")
|
14
|
+
@buffer.push data
|
15
|
+
|
16
|
+
# non-zero response code indicates error
|
17
|
+
if status.to_i != 0
|
18
|
+
fail
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
|
22
|
+
done?
|
23
|
+
end
|
24
|
+
|
25
|
+
# non zero response code, or we've processed all lines
|
26
|
+
def done?; (@buffer.size == @lines); end
|
27
|
+
|
28
|
+
def succeed
|
29
|
+
super(@buffer)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Client < EventMachine::Connection
|
34
|
+
include EventMachine::Deferrable
|
35
|
+
include EventMachine::Protocols::LineProtocol
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@deferrables = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def connection_completed
|
42
|
+
succeed
|
43
|
+
end
|
44
|
+
|
45
|
+
def open_index(opts)
|
46
|
+
execute([['P', opts[:id], opts[:db], opts[:table], opts[:index_name], opts[:columns]]])
|
47
|
+
end
|
48
|
+
|
49
|
+
def query(*queries)
|
50
|
+
execute(queries.map{|q| [q[:id], q[:op], q[:key].size, q[:key], q[:limit], q[:offset]].compact })
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute(cmd, &blk)
|
54
|
+
callback { send(cmd) }
|
55
|
+
add_deferrable(cmd.size, &blk)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def send(data)
|
61
|
+
send_data data.map {|d| d.join("\t")}.join("\n") + "\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
def receive_line(line)
|
65
|
+
if @deferrables.first.recieve(line)
|
66
|
+
@deferrables.shift.succeed
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_deferrable(lines, &blk)
|
71
|
+
df = Deferrable.new(lines)
|
72
|
+
df.callback &blk
|
73
|
+
|
74
|
+
@deferrables.push(df)
|
75
|
+
df
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'em/protocols/line_protocol'
|
3
|
+
require 'em-handlersocket/client'
|
4
|
+
|
5
|
+
module EventMachine
|
6
|
+
module HandlerSocket
|
7
|
+
def self.new(options = {})
|
8
|
+
opt = {
|
9
|
+
:host => '127.0.0.1',
|
10
|
+
:port => '9998'
|
11
|
+
}.merge(options)
|
12
|
+
|
13
|
+
EventMachine.connect(opt[:host], opt[:port], EventMachine::HandlerSocket::Client)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
#
|
4
|
+
# create database widgets;
|
5
|
+
# CREATE TABLE user (
|
6
|
+
# user_id INT UNSIGNED PRIMARY KEY,
|
7
|
+
# user_name VARCHAR(50),
|
8
|
+
# user_email VARCHAR(255),
|
9
|
+
# created DATETIME
|
10
|
+
# ) ENGINE=InnoDB;
|
11
|
+
#
|
12
|
+
# insert into user (user_id, user_name, user_email, created) values (1, 'Ilya', 'ilya@igvita.com', '2010-01-01');
|
13
|
+
# insert into user (user_id, user_name, user_email, created) values (2, 'John', 'john@example.com', '2010-01-02');
|
14
|
+
# insert into user (user_id, user_name, user_email, created) values (3, 'Bob', 'bob@example.com', '2010-01-03');
|
15
|
+
#
|
16
|
+
|
17
|
+
describe EventMachine::HandlerSocket do
|
18
|
+
|
19
|
+
it "should connect to HandlerSocket server" do
|
20
|
+
EM.run {
|
21
|
+
c = EM::HandlerSocket.new(:host => '127.0.0.1', :port => '9998')
|
22
|
+
c.class.should == EventMachine::HandlerSocket::Client
|
23
|
+
|
24
|
+
EM.stop
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should connect to localhost, read-only port by default" do
|
29
|
+
EM.run {
|
30
|
+
c = EM::HandlerSocket.new
|
31
|
+
c.class.should == EventMachine::HandlerSocket::Client
|
32
|
+
|
33
|
+
EM.stop
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should open an index via raw interface" do
|
38
|
+
EM.run {
|
39
|
+
c = EM::HandlerSocket.new
|
40
|
+
|
41
|
+
df = c.execute([['P', '0', 'widgets', 'user', 'PRIMARY', 'user_name,user_email,created']])
|
42
|
+
df.callback { EM.stop }
|
43
|
+
df.errback { fail }
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should invoke errback on bad query" do
|
48
|
+
EM.run {
|
49
|
+
c = EM::HandlerSocket.new
|
50
|
+
|
51
|
+
df = c.execute([['P', '0', 'badDB', 'user', 'PRIMARY', 'user_name,user_email,created']])
|
52
|
+
df.callback { fail }
|
53
|
+
df.errback { EM.stop }
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should open an index" do
|
58
|
+
EM.run {
|
59
|
+
c = EM::HandlerSocket.new
|
60
|
+
idx = {:id => 0, :db => 'widgets', :table => 'user', :index_name => 'PRIMARY', :columns => 'user_name'}
|
61
|
+
|
62
|
+
d = c.open_index(idx)
|
63
|
+
d.callback do
|
64
|
+
EM.stop
|
65
|
+
end
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should fetch a single record" do
|
70
|
+
EM.run {
|
71
|
+
c = EM::HandlerSocket.new
|
72
|
+
idx = {:id => 0, :db => 'widgets', :table => 'user', :index_name => 'PRIMARY', :columns => 'user_name'}
|
73
|
+
|
74
|
+
d = c.open_index(idx)
|
75
|
+
d.callback do
|
76
|
+
|
77
|
+
d = c.query(:id => 0, :op => '=', :key => '1')
|
78
|
+
d.callback do |data|
|
79
|
+
data.should == ['Ilya']
|
80
|
+
EM.stop
|
81
|
+
end
|
82
|
+
end
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should fetch multiple records" do
|
87
|
+
EM.run {
|
88
|
+
c = EM::HandlerSocket.new
|
89
|
+
idx = {:id => 0, :db => 'widgets', :table => 'user', :index_name => 'PRIMARY', :columns => 'user_name'}
|
90
|
+
|
91
|
+
d = c.open_index(idx)
|
92
|
+
d.callback do |s|
|
93
|
+
|
94
|
+
d = c.query({:id => 0, :op => '=', :key => '1'}, {:id => 0, :op => '=', :key => '2'})
|
95
|
+
d.errback { fail }
|
96
|
+
d.callback do |data|
|
97
|
+
data.should == ['Ilya', 'John']
|
98
|
+
EM.stop
|
99
|
+
end
|
100
|
+
end
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should execute a query against a composite index" do
|
105
|
+
EM.run {
|
106
|
+
c = EM::HandlerSocket.new
|
107
|
+
idx = {:id => 0, :db => 'widgets', :table => 'user', :index_name => 'id_created', :columns => 'user_name'}
|
108
|
+
|
109
|
+
d = c.open_index(idx)
|
110
|
+
d.callback do |s|
|
111
|
+
|
112
|
+
d = c.query(:id => 0, :op => '>=', :key => ['2', '2010-01-03'])
|
113
|
+
d.errback { fail }
|
114
|
+
d.callback do |data|
|
115
|
+
data.should == ['Bob']
|
116
|
+
EM.stop
|
117
|
+
end
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/spec/helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-handlersocket
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Ilya Grigorik
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-14 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: eventmachine
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
description: Asynchronous (EventMachine) HandlerSocket client
|
47
|
+
email:
|
48
|
+
- ilya@igvita.com
|
49
|
+
executables: []
|
50
|
+
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
extra_rdoc_files: []
|
54
|
+
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- .rspec
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- em-handlersocket.gemspec
|
63
|
+
- lib/em-handlersocket.rb
|
64
|
+
- lib/em-handlersocket/client.rb
|
65
|
+
- lib/em-handlersocket/version.rb
|
66
|
+
- spec/client_spec.rb
|
67
|
+
- spec/helper.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/igrigorik/em-handlersocket
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project: em-handlersocket
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Asynchronous (EventMachine) HandlerSocket client
|
100
|
+
test_files:
|
101
|
+
- spec/client_spec.rb
|
102
|
+
- spec/helper.rb
|