kyototycoon 0.1.2 → 0.5.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/README.markdown CHANGED
@@ -1,3 +1,12 @@
1
+ KyotoTycoon client for Ruby.
2
+
3
+ # Feature / Fixture
4
+
5
+ * Always Keep-Alive connect (v0.5.0+)
6
+ * You can choise key/value encoding from URI or Base64
7
+ * You can use MessagePack tranparency
8
+ * Benchmark scripts appended(they are connect to localhost:19999)
9
+
1
10
  # Install
2
11
 
3
12
  $ gem install kyototycoon
@@ -6,7 +15,7 @@
6
15
 
7
16
  ## Simple case
8
17
 
9
- @kt = KyotoTycoon.new
18
+ @kt = KyotoTycoon.new('localhost', 1978)
10
19
 
11
20
  # traditional style
12
21
  @kt.set('foo', 123)
@@ -21,9 +30,18 @@
21
30
 
22
31
 
23
32
  ## Complex case
33
+ # KT#configure for instance setting store.
34
+
35
+ KyotoTycoon.configure(:generic) do |kt|
36
+ kt.db = '*' # on memory
37
+ end
24
38
 
25
- @kt = KyotoTycoon.new('remotehost', 12345) # connect any host, any port
26
- @kt.db = '*' # on memory, or DB file as KT known
39
+ # connect any host, any port
40
+ KyotoTycoon.configure(:favicon, 'remotehost', 12345) do |kt|
41
+ kt.db = 'favicons.kch' # DB file as KT known
42
+ end
43
+
44
+ @kt = KyotoTycoon.create(:generic) # got KT instance by KT#configure(:generic) rules
27
45
 
28
46
  # set/bulk_set/get/bulk_get uses msgpack. default as :default
29
47
  @kt.serializer = :msgpack
@@ -37,14 +55,14 @@
37
55
  # @kt.logger = STDOUT
38
56
  # @kt.logger = Logger.new(STDOUT)
39
57
 
40
- # HTTP agent
41
- @kt.agent = :skinny # low-level socket communicate. a bit of faster than :nethttp(default). try benchmark/agent.rb
42
-
43
58
  # standby server
44
59
  @kt.connect_timeout = 0.5 # => wait 0.5 sec for connection open
45
60
  @kt.servers << ['server2', 1978] # standby server that will use when primary server (a.k.a. KT#new(host, port)) is dead.
46
61
  @kt.servers << ['server3', 1978] # same as above
47
62
 
63
+ # key/value encoding from :U or :B(default). default as base64 because it seems better than URL encode for me.
64
+ @kt.colenc = :U
65
+
48
66
  # get/set
49
67
  @kt.set('foo', 42, 100) # => expire at 100 seconds after
50
68
  @kt['foo'] # => 42. it is interger by msgpack serializer works
@@ -61,7 +79,7 @@
61
79
  @kt.remove_bulk([:foo, :bar])
62
80
 
63
81
  # it can store when msgpack using.
64
- @kt['baz'] = {'a' => 'a', 'b' => 'b}
82
+ @kt['baz'] = {'a' => 'a', 'b' => 'b'}
65
83
  @kt['baz'] # => {'a' => 'a', 'b' => 'b}
66
84
 
67
85
  # increment
@@ -70,6 +88,10 @@
70
88
  @kt.increment('bar', 10) # => 12
71
89
  @kt.increment('bar', -5) # => 7
72
90
 
91
+ # shorthand
92
+ @kt.incr('foo') # => 1
93
+ @kt.decr('foo') # => 0
94
+
73
95
  # delete keys
74
96
  @kt.delete(:foo, :bar, :baz)
75
97
 
data/benchmark/bulk.rb CHANGED
@@ -1,11 +1,7 @@
1
1
  # -- coding: utf-8
2
2
 
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
- require "rubygems"
5
- require "benchmark"
6
- require 'kyototycoon.rb'
3
+ require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
7
4
 
8
- kt = KyotoTycoon.new
9
5
  bulk={}
10
6
  50000.times.map{|n|
11
7
  bulk[n.to_s] = "#{n}-#{rand}"
@@ -15,23 +11,5 @@ job = lambda {|kt|
15
11
  kt.get_bulk(bulk.keys)
16
12
  kt.clear
17
13
  }
18
- Benchmark.bm do |x|
19
- x.report('default') {
20
- kt.serializer=:default
21
- job.call(kt)
22
- }
23
- x.report('msgpack') {
24
- kt.serializer=:msgpack
25
- job.call(kt)
26
- }
27
- x.report('default(skinny)') {
28
- kt.agent = :skinny
29
- kt.serializer=:default
30
- job.call(kt)
31
- }
32
- x.report('msgpack(skinny)') {
33
- kt.agent = :skinny
34
- kt.serializer=:msgpack
35
- job.call(kt)
36
- }
37
- end
14
+
15
+ benchmark(job)
@@ -1,11 +1,7 @@
1
1
  # -- coding: utf-8
2
2
 
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
- require "rubygems"
5
- require "benchmark"
6
- require 'kyototycoon.rb'
3
+ require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
7
4
 
8
- kt = KyotoTycoon.new
9
5
  bulk={}
10
6
  str = "string ああ" * 10000
11
7
  100.times.map{|n|
@@ -16,13 +12,4 @@ job = lambda {|kt|
16
12
  kt.get_bulk(bulk.keys)
17
13
  kt.clear
18
14
  }
19
- Benchmark.bm do |x|
20
- x.report('default') {
21
- kt.serializer=:default
22
- job.call(kt)
23
- }
24
- x.report('msgpack') {
25
- kt.serializer=:msgpack
26
- job.call(kt)
27
- }
28
- end
15
+ benchmark(job)
data/benchmark/getset.rb CHANGED
@@ -1,11 +1,7 @@
1
1
  # -- coding: utf-8
2
2
 
3
- require "rubygems"
4
- require "benchmark"
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- require 'kyoto_tycoon.rb'
3
+ require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
7
4
 
8
- kt = KyotoTycoon.new
9
5
  job = lambda {|kt|
10
6
  1000.times{|n|
11
7
  kt.set(n.to_s, n)
@@ -13,25 +9,4 @@ job = lambda {|kt|
13
9
  }
14
10
  kt.clear
15
11
  }
16
- Benchmark.bm do |x|
17
- x.report('default') {
18
- kt.agent = :nethttp
19
- kt.serializer=:default
20
- job.call(kt)
21
- }
22
- x.report('msgpack') {
23
- kt.agent = :nethttp
24
- kt.serializer=:msgpack
25
- job.call(kt)
26
- }
27
- x.report('default(skinny)') {
28
- kt.agent = :skinny
29
- kt.serializer=:default
30
- job.call(kt)
31
- }
32
- x.report('msgpack(skinny)') {
33
- kt.agent = :skinny
34
- kt.serializer=:msgpack
35
- job.call(kt)
36
- }
37
- end
12
+ benchmark(job)
@@ -1,10 +1,6 @@
1
1
  # -- coding: utf-8
2
2
 
3
- require "rubygems"
4
- require "benchmark"
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- require "msgpack"
7
- require 'kyototycoon.rb'
3
+ require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
8
4
 
9
5
  job = lambda {|kt|
10
6
  cnt = 0
@@ -22,12 +18,4 @@ job = lambda {|kt|
22
18
  cnt
23
19
  }
24
20
 
25
- kt = KyotoTycoon.new
26
-
27
- %w"nethttp skinny".each{|agent|
28
- %w!default msgpack!.each{|serializer|
29
- kt.agent = agent.to_sym
30
- kt.serializer = serializer.to_sym
31
- puts "#{agent}/#{serializer}: #{job.call(kt)}"
32
- }
33
- }
21
+ benchmark(job)
@@ -0,0 +1,22 @@
1
+ # -- coding: utf-8
2
+
3
+ require "benchmark"
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'kyototycoon.rb'
7
+
8
+ def benchmark(job)
9
+ kt = KyotoTycoon.new('0.0.0.0', 19999)
10
+ Benchmark.bm do |x|
11
+ %w!B U!.each{|colenc|
12
+ %w!default msgpack!.each{|serializer|
13
+ x.report("#{serializer}, colenc=#{colenc}") {
14
+ kt.serializer=serializer.to_sym
15
+ kt.colenc = colenc.to_sym
16
+ job.call(kt)
17
+ }
18
+ }
19
+ }
20
+ end
21
+ end
22
+
data/kyototycoon.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{kyototycoon}
8
- s.version = "0.1.2"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["uu59"]
12
- s.date = %q{2010-12-09}
12
+ s.date = %q{2010-12-22}
13
13
  s.description = %q{KyotoTycoon client for Ruby}
14
14
  s.email = %q{a@tt25.org}
15
15
  s.extra_rdoc_files = [
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  "README.markdown",
23
23
  "Rakefile",
24
24
  "VERSION",
25
- "benchmark/agent.rb",
25
+ "benchmark/helper.rb",
26
26
  "benchmark/bulk.rb",
27
27
  "benchmark/bulk_bigdata.rb",
28
28
  "benchmark/getset.rb",
@@ -33,7 +33,6 @@ Gem::Specification.new do |s|
33
33
  "lib/kyototycoon/serializer/default.rb",
34
34
  "lib/kyototycoon/serializer/msgpack.rb",
35
35
  "lib/kyototycoon/tsvrpc.rb",
36
- "lib/kyototycoon/tsvrpc/nethttp.rb",
37
36
  "lib/kyototycoon/tsvrpc/skinny.rb",
38
37
  "spec/helper.rb"
39
38
  ]
data/lib/kyototycoon.rb CHANGED
@@ -10,9 +10,10 @@ require "kyototycoon/serializer/default.rb"
10
10
  require "kyototycoon/serializer/msgpack.rb"
11
11
  require "kyototycoon/tsvrpc.rb"
12
12
  require "kyototycoon/tsvrpc/skinny.rb"
13
- require "kyototycoon/tsvrpc/nethttp.rb"
14
13
 
15
14
  class KyotoTycoon
15
+ VERSION = '0.5.0'
16
+
16
17
  attr_accessor :colenc, :connect_timeout, :servers
17
18
  attr_reader :serializer, :logger, :db
18
19
 
@@ -42,8 +43,7 @@ class KyotoTycoon
42
43
  @servers = [[host, port]]
43
44
  @serializer = KyotoTycoon::Serializer::Default
44
45
  @logger = Logger.new(nil)
45
- @agent = :nethttp
46
- @colenc = :U
46
+ @colenc = :B
47
47
  @connect_timeout = 0.5
48
48
  end
49
49
 
@@ -63,10 +63,6 @@ class KyotoTycoon
63
63
  @logger = logger
64
64
  end
65
65
 
66
- def agent=(agent)
67
- @agent = agent
68
- end
69
-
70
66
  def get(key)
71
67
  res = request('/rpc/get', {:key => key})
72
68
  @serializer.decode(Tsvrpc.parse(res[:body])['value'])
@@ -218,35 +214,39 @@ class KyotoTycoon
218
214
  params ||= {}
219
215
  params[:DB] = @db
220
216
  end
221
- if @servers.length > 1
222
- @servers.each{|s|
223
- host,port = *s
224
- if ping(host, port)
225
- @servers = [[host, port]]
226
- break
227
- end
228
- }
229
- end
230
- if @servers.length == 0
231
- msg = "alived server not exists"
232
- @logger.crit(msg)
233
- raise msg
234
- end
235
- tsvrpc ||= begin
236
- host, port = *@servers.first
237
- Tsvrpc.new(host, port)
217
+
218
+ status,body = client.request(path, params, @colenc)
219
+ if ![200, 450].include?(status.to_i)
220
+ raise body
238
221
  end
239
- res = tsvrpc.request(path, params, @agent, @colenc)
222
+ res = {:status => status, :body => body}
240
223
  @logger.info("#{path}: #{res[:status]} with query parameters #{params.inspect}")
241
224
  res
242
225
  end
243
226
 
227
+ def client
228
+ host, port = *choice_server
229
+ @client ||= begin
230
+ Tsvrpc::Skinny.new(host, port)
231
+ end
232
+ end
233
+
234
+ def start
235
+ client.start
236
+ end
237
+
238
+ def finish
239
+ client.finish
240
+ end
241
+
242
+ private
243
+
244
244
  def ping(host, port)
245
245
  begin
246
+ rpc = Tsvrpc::Skinny.new(host, port)
246
247
  timeout(@connect_timeout){
247
248
  @logger.debug("connect check #{host}:#{port}")
248
- rpc = Tsvrpc.new(host, port)
249
- res = rpc.request('/rpc/echo', {'0' => '0'}, :skinny, :U)
249
+ res = rpc.request('/rpc/echo', {'0' => '0'}, :U)
250
250
  @logger.debug(res)
251
251
  }
252
252
  true
@@ -254,9 +254,38 @@ class KyotoTycoon
254
254
  # Ruby 1.8.7 compatible
255
255
  @logger.warn("connect failed at #{host}:#{port}")
256
256
  false
257
+ rescue SystemCallError
258
+ @logger.warn("connect failed at #{host}:#{port}")
259
+ false
257
260
  rescue => ex
258
261
  @logger.warn("connect failed at #{host}:#{port}")
259
262
  false
263
+ ensure
264
+ rpc.finish
260
265
  end
261
266
  end
267
+
268
+ def choice_server
269
+ current = @servers.first
270
+ if @servers.length > 1
271
+ @servers.each{|s|
272
+ host,port = *s
273
+ if ping(host, port)
274
+ @servers = [[host, port]]
275
+ break
276
+ end
277
+ }
278
+ end
279
+ if @servers.length == 0
280
+ msg = "alived server not exists"
281
+ @logger.crit(msg)
282
+ raise msg
283
+ end
284
+ result = @servers.first
285
+ if current != result
286
+ @client = nil
287
+ end
288
+ result
289
+ end
290
+
262
291
  end
@@ -2,29 +2,7 @@
2
2
 
3
3
 
4
4
  class KyotoTycoon
5
- class Tsvrpc
6
- def initialize(host, port)
7
- @host = host
8
- @port = port
9
- end
10
-
11
- def http(agent)
12
- case agent
13
- when :skinny
14
- Skinny.new(@host, @port)
15
- else
16
- Nethttp.new(@host, @port)
17
- end
18
- end
19
-
20
- def request(path, params, agent, colenc)
21
- status,body = *http(agent).request(path, params, colenc)
22
- if ![200, 450].include?(status)
23
- raise body
24
- end
25
- {:status => status, :body => body}
26
- end
27
-
5
+ module Tsvrpc
28
6
  def self.parse(body)
29
7
  body.split("\n").inject({}){|r, line|
30
8
  k,v = *line.split("\t", 2).map{|v| CGI.unescape(v)}
@@ -1,28 +1,29 @@
1
1
  # -- coding: utf-8
2
2
 
3
3
  class KyotoTycoon
4
- class Tsvrpc
4
+ module Tsvrpc
5
5
  class Skinny
6
6
  def initialize(host, port)
7
7
  @host = host
8
8
  @port = port
9
9
  @tpl = ""
10
- @tpl << "POST %s HTTP/1.0\r\n"
10
+ @tpl << "POST %s HTTP/1.1\r\n"
11
11
  @tpl << "Content-Length: %d\r\n"
12
12
  @tpl << "Content-Type: text/tab-separated-values; colenc=%s\r\n"
13
13
  @tpl << "\r\n%s"
14
14
  end
15
15
 
16
16
  def request(path, params, colenc)
17
- sock ||= ::TCPSocket.new(@host, @port)
17
+ start
18
18
  query = KyotoTycoon::Tsvrpc.build_query(params, colenc)
19
19
  request = @tpl % [path, query.bytesize, colenc, query]
20
- sock.write(request)
21
- status = sock.gets[9, 3]
20
+ @sock.write(request)
21
+ first_line = @sock.gets
22
+ status = first_line[9, 3]
22
23
  bodylen = 0
23
24
  body = ""
24
25
  loop do
25
- line = sock.gets
26
+ line = @sock.gets
26
27
  if line['Content-Length']
27
28
  bodylen = line.match(/[0-9]+/)[0].to_i
28
29
  next
@@ -31,10 +32,17 @@ class KyotoTycoon
31
32
  break
32
33
  end
33
34
  end
34
- body = sock.read(bodylen)
35
- sock.close
35
+ body = @sock.read(bodylen)
36
36
  [status.to_i, body]
37
37
  end
38
+
39
+ def start
40
+ @sock ||= ::TCPSocket.new(@host, @port)
41
+ end
42
+
43
+ def finish
44
+ @sock.close if @sock
45
+ end
38
46
  end
39
47
  end
40
48
  end
data/spec/helper.rb CHANGED
@@ -23,6 +23,9 @@ describe do
23
23
  end
24
24
 
25
25
  before(:each) do
26
+ end
27
+
28
+ after(:each) do
26
29
  @kt.clear
27
30
  end
28
31
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 2
9
- version: 0.1.2
7
+ - 5
8
+ - 0
9
+ version: 0.5.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - uu59
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-12-09 00:00:00 +09:00
17
+ date: 2010-12-22 00:00:00 +09:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -103,7 +103,7 @@ files:
103
103
  - README.markdown
104
104
  - Rakefile
105
105
  - VERSION
106
- - benchmark/agent.rb
106
+ - benchmark/helper.rb
107
107
  - benchmark/bulk.rb
108
108
  - benchmark/bulk_bigdata.rb
109
109
  - benchmark/getset.rb
@@ -114,7 +114,6 @@ files:
114
114
  - lib/kyototycoon/serializer/default.rb
115
115
  - lib/kyototycoon/serializer/msgpack.rb
116
116
  - lib/kyototycoon/tsvrpc.rb
117
- - lib/kyototycoon/tsvrpc/nethttp.rb
118
117
  - lib/kyototycoon/tsvrpc/skinny.rb
119
118
  - spec/helper.rb
120
119
  has_rdoc: true
data/benchmark/agent.rb DELETED
@@ -1,27 +0,0 @@
1
- # -- coding: utf-8
2
-
3
- require "rubygems"
4
- require "benchmark"
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
- require 'kyototycoon.rb'
7
-
8
- kt = KyotoTycoon.new
9
- job = lambda {|kt|
10
- 10000.times{|n|
11
- kt.set(n.to_s, n)
12
- kt.get(n)
13
- }
14
- kt.clear
15
- }
16
- Benchmark.bm do |x|
17
- x.report('skinny') {
18
- kt.agent = :skinny
19
- kt.serializer=:default
20
- job.call(kt)
21
- }
22
- x.report('nethttp') {
23
- kt.agent = :nethttp
24
- kt.serializer=:default
25
- job.call(kt)
26
- }
27
- end
@@ -1,28 +0,0 @@
1
- # -- coding: utf-8
2
-
3
- require "rubygems"
4
-
5
- class KyotoTycoon
6
- class Tsvrpc
7
- class Nethttp
8
- def initialize(host, port)
9
- @host = host
10
- @port = port
11
- @http ||= ::Net::HTTP.new(@host, @port)
12
- end
13
-
14
- def request(path, params, colenc)
15
- query = KyotoTycoon::Tsvrpc.build_query(params, colenc)
16
- req = Net::HTTP::Post.new(path)
17
- if query.length > 0
18
- req.body = query
19
- req['Content-Length'] = query.size
20
- end
21
- req['Content-Type'] = "text/tab-separated-values; colenc=#{colenc}"
22
- req['Connection'] = "close"
23
- res = @http.request(req)
24
- [res.code.to_i, res.body]
25
- end
26
- end
27
- end
28
- end