binary42-fastcache 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+ require 'fastcache'
4
+ require 'memcache'
5
+ require 'benchmark'
6
+
7
+ NODE = ['localhost:11211']
8
+
9
+ n = 1000
10
+ Benchmark.bm(30) do |x|
11
+ x.report 'basic operations - memcache-client' do
12
+ conn = MemCache.new(*NODE)
13
+ n.times do
14
+ conn.get('f')
15
+ conn.set('f', '42')
16
+ conn.get('f')
17
+ conn.delete('f')
18
+ end
19
+ conn.reset
20
+ end
21
+ x.report 'basic operations - fastcache' do
22
+ conn = FastCache.connect(NODE)
23
+ n.times do
24
+ conn.get('f')
25
+ conn.set('f', '42')
26
+ conn.get('f')
27
+ conn.delete('f')
28
+ end
29
+ conn.close_all_connections
30
+ end
31
+ end
data/lib/fastcache.rb CHANGED
@@ -14,7 +14,7 @@ require 'fastcache/hash/sha1'
14
14
 
15
15
  # Key routers
16
16
  # TODO: Check for ruby-ketama
17
- require 'fastcache/router/basic'
17
+ require 'fastcache/routing/basic_router'
18
18
 
19
19
  # Networking and protocol support code
20
20
  require 'fastcache/memcache'
@@ -32,9 +32,10 @@ class << FastCache
32
32
  :type => 'raw'
33
33
  }.merge(opts)
34
34
 
35
- connections[opts[:name]].close if connections[opts[:name]]
35
+ connections[opts[:name]].close_all_connections if connections[opts[:name]]
36
36
 
37
- nodes = node_list.split(',').map {|node|
37
+ node_list = node_list.split(',') if String === node_list
38
+ nodes = node_list.map {|node|
38
39
  host, port = *node.split(':')
39
40
  port ||= 80
40
41
  [host, port.to_i]
@@ -65,4 +66,4 @@ private
65
66
  @connections ||= {}
66
67
  end
67
68
 
68
- end
69
+ end
@@ -1,16 +1,14 @@
1
1
  class FastCache::ModulusBucket
2
2
 
3
- def initialize(key, hash)
4
- @digest = hash.new(key).hexdigest.to_i(16)
5
- @key = key
3
+ def initialize(nodes)
4
+ @nodes = nodes
6
5
  end
7
6
 
8
- def select(nodes)
9
- count = nodes.size
10
- # Note: An upper bound on tries ought to be given at some point
7
+ def select(hash)
8
+ count = @nodes.size
11
9
  count.times do |i|
12
10
  begin
13
- break yield(@key, nodes[(@digest + i) % count])
11
+ break yield(@nodes[(hash + i) % count])
14
12
  rescue FastCache::Memcache::ProtocolError
15
13
  next
16
14
  end
@@ -1,24 +1,19 @@
1
1
  class FastCache::Connection
2
2
  include FastCache::Memcache::Protocol
3
3
 
4
- attr_accessor :bucket_class, :hash_class, :marshal_module
5
- attr_reader :nodes
4
+ attr_accessor :router
6
5
 
7
- def initialize(nodes, opts)
6
+ CONNECTION_OPTIONS = {
7
+ :router => FastCache::BasicRouter}
8
+
9
+ def initialize(nodes, opts = nil)
10
+ opts = CONNECTION_OPTIONS.merge(opts || {})
8
11
  @nodes = nodes.map {|(host, port)| FastCache::Memcache::Node.new(host, port)}
9
- @bucket_class = FastCache::ModulusBucket
10
- @hash_class = FastCache::CRC32
11
- @marshal_module = Marshal # Ruby's built in marshal
12
+ @router = opts[:router].new(@nodes)
12
13
  end
13
14
 
14
- def close
15
+ def close_all_connections
15
16
  @nodes.each do |node| node.close end
16
17
  end
17
18
 
18
- private
19
-
20
- def bucket(key)
21
- @bucket_class.new(key, @hash_class)
22
- end
23
-
24
- end
19
+ end
@@ -1,8 +1,7 @@
1
1
  module FastCache; end
2
- module FastCache::Memcache
3
- class ProtocolError < RuntimeError; end
4
- end
2
+ module FastCache::Memcache; end
5
3
 
6
4
  # Memcache components
7
5
  require 'fastcache/memcache/node'
8
6
  require 'fastcache/memcache/protocol'
7
+ require 'fastcache/memcache/errors'
@@ -0,0 +1,3 @@
1
+ module FastCache::Memcache
2
+ class ProtocolError < RuntimeError; end
3
+ end
@@ -30,7 +30,7 @@ class FastCache::Memcache::Node
30
30
  end
31
31
 
32
32
  def close
33
- socket.close
33
+ socket.close if @socket
34
34
  @socket = nil
35
35
  end
36
36
 
@@ -41,7 +41,7 @@ class FastCache::Memcache::Node
41
41
  private
42
42
 
43
43
  def socket
44
- return @socket if @socket and not @socket.closed?
44
+ return @socket if @socket && !@socket.closed?
45
45
 
46
46
  @socket = nil
47
47
 
@@ -1,11 +1,8 @@
1
- # Informal protocol for mixin:
2
- # bucket will ask for a key and return a bucket
3
- # nodes will return connection nodes
4
1
  module FastCache::Memcache::Protocol
5
2
 
6
3
  def get(key)
7
- bucket(key).select(nodes) {|node_key, node|
8
- node.write "get #{node_key}\r\n"
4
+ router.route(key) {|node|
5
+ node.write "get #{key}\r\n"
9
6
  resp = node.gets
10
7
  break FastCache::Maybe.nothing if resp == "END\r\n"
11
8
  resp =~ /(\d+)\r/
@@ -21,8 +18,8 @@ module FastCache::Memcache::Protocol
21
18
 
22
19
  def set(key, value, expiry = 0)
23
20
  value = value.to_s
24
- bucket(key).select(nodes) {|node_key, node|
25
- node.write "set #{node_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
21
+ router.route(key) {|node|
22
+ node.write "set #{key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
26
23
  result = node.gets
27
24
  if result == "STORED\r\n"
28
25
  FastCache::Maybe.just(value)
@@ -36,8 +33,8 @@ module FastCache::Memcache::Protocol
36
33
 
37
34
  def add(key, value, expiry = 0)
38
35
  data = value.to_s
39
- bucket(key).select(nodes) {|node_key, node|
40
- node.write "add #{node_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
36
+ router.route(key) {|node|
37
+ node.write "add #{key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
41
38
  resp = node.gets
42
39
  if resp == "STORED\r\n"
43
40
  FastCache::Maybe.just(data)
@@ -51,8 +48,8 @@ module FastCache::Memcache::Protocol
51
48
 
52
49
  def replace(key, value, expiry = 0)
53
50
  data = value.to_s
54
- bucket(key).select(nodes) {|node_key, node|
55
- node.write "replace #{node_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
51
+ router.route(key) {|node|
52
+ node.write "replace #{key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
56
53
  resp = node.gets
57
54
  if resp == "STORED\r\n"
58
55
  FastCache::Maybe.just(data)
@@ -65,12 +62,12 @@ module FastCache::Memcache::Protocol
65
62
  end
66
63
 
67
64
  def delete(key, expiry = 0)
68
- bucket(key).select(nodes) {|node_key, node|
69
- node.write "delete #{node_key} #{expiry}\r\n"
65
+ router.route(key) {|node|
66
+ node.write "delete #{key} #{expiry}\r\n"
70
67
  resp = node.gets
71
68
  resp == "DELETED\r\n"
72
69
  }
73
- rescue FastCache::Memcache::Protocol
70
+ rescue FastCache::Memcache::ProtocolError
74
71
  FastCache::Maybe.nothing
75
72
  end
76
73
 
@@ -78,20 +75,42 @@ module FastCache::Memcache::Protocol
78
75
  fail 'Not implemented'
79
76
  end
80
77
 
81
- def incr(*)
82
- fail 'Not implemented'
78
+ def incr(key, count)
79
+ router.route(key) {|node|
80
+ node.write "incr #{key} #{count}\r\n"
81
+ resp = node.gets
82
+ resp == "NOT_FOUND\r\n" ?
83
+ nil :
84
+ resp.to_i
85
+ }
86
+ rescue FastCache::Memcache::ProtocolError
87
+ FastCache::Maybe.nothing
83
88
  end
84
89
 
85
- def decr(*)
86
- fail 'Not implemented'
90
+ def decr(key, count)
91
+ router.route(key) {|node|
92
+ node.write "decr #{key} #{count}\r\n"
93
+ resp = node.gets
94
+ resp == "NOT_FOUND\r\n" ?
95
+ nil :
96
+ resp.to_i
97
+ }
98
+ rescue FastCache::Memcache::ProtocolError
99
+ FastCache::Maybe.nothing
87
100
  end
88
101
 
89
- def flush_all_caches
90
- fail 'Not implemented'
102
+ def flush_all_caches(delay = nil)
103
+ router.each {|node|
104
+ node.write "flush_all #{delay} noreply\r\n"
105
+ }
106
+ self
91
107
  end
92
108
 
93
- def flush_cache(key)
94
- fail 'Not implemented'
109
+ def flush_cache(key, delay = nil)
110
+ router.route(key) {|node|
111
+ node.write "flush_all #{delay} noreply\r\n"
112
+ }
113
+ self
95
114
  end
96
115
 
97
116
  def stats(*)
@@ -99,7 +118,7 @@ module FastCache::Memcache::Protocol
99
118
  end
100
119
 
101
120
  def reset_connections
102
- fail 'Not implemented yet'
121
+ router.each {|node| node.close}
103
122
  end
104
123
 
105
124
  end
@@ -0,0 +1,20 @@
1
+ class FastCache::BasicRouter
2
+
3
+ attr_reader :bucket_delegate, :hash_delegate
4
+
5
+ def initialize(nodes, bucket_class = FastCache::ModulusBucket, hash_class = FastCache::CRC32)
6
+ @nodes = nodes
7
+ @bucket_delegate = bucket_class.new(nodes)
8
+ @hash_delegate = hash_class
9
+ end
10
+
11
+ def each(&b)
12
+ @nodes.each(&b)
13
+ end
14
+
15
+ def route(key, &b)
16
+ digest = @hash_delegate.new(key.to_s).digest
17
+ @bucket_delegate.select(digest.unpack('N').first, &b)
18
+ end
19
+
20
+ end
@@ -56,4 +56,4 @@ class FastCache::Maybe
56
56
  end
57
57
  alias clear! void
58
58
 
59
- end
59
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ include FastCache
3
+
4
+ describe BasicRouter, '.new' do
5
+
6
+ it 'should take a nodes list, bucket delegate, and hashing delegate' do
7
+ router = BasicRouter.new([], ModulusBucket, CRC32)
8
+ router.bucket_delegate.should be_kind_of(ModulusBucket)
9
+ router.hash_delegate.should == CRC32
10
+ end
11
+
12
+ it 'should default to modulus and crc32 based delegates' do
13
+ router = BasicRouter.new([])
14
+ router.bucket_delegate.should be_kind_of(ModulusBucket)
15
+ router.hash_delegate.should == CRC32
16
+ end
17
+
18
+ end
19
+
20
+ describe BasicRouter, '#route' do
21
+
22
+ before :each do
23
+ @nodes = [:one, :two, :three, :four]
24
+ @router = BasicRouter.new(@nodes)
25
+ end
26
+
27
+ it 'should route to the same buck if the string representation is the same' do
28
+ @router.route(:key) {|n| @n1 = n}
29
+ @router.route('key') {|n| @n2 = n}
30
+ @n1.should == @n2
31
+ end
32
+
33
+ it 'should return a node' do
34
+ @nodes.should include(@router.route('x') {|n| n})
35
+ end
36
+
37
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binary42-fastcache
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Mitchell
@@ -26,18 +26,19 @@ files:
26
26
  - lib/fastcache/hash/md5.rb
27
27
  - lib/fastcache/hash/crc32.rb
28
28
  - lib/fastcache/util/maybe.rb
29
+ - lib/fastcache/routing/basic_router.rb
29
30
  - lib/fastcache/marshal/yaml.rb
30
31
  - lib/fastcache/marshal/duck.rb
31
32
  - lib/fastcache/marshal/ruby.rb
32
33
  - lib/fastcache/bucket/modulus.rb
33
34
  - lib/fastcache/bucket/consistent.rb
34
- - lib/fastcache/router/basic.rb
35
35
  - lib/fastcache/interface/dictionary.rb
36
36
  - lib/fastcache/interface/evaluator.rb
37
37
  - lib/fastcache/interface/connection.rb
38
38
  - lib/fastcache/memcache.rb
39
39
  - lib/fastcache/memcache/node.rb
40
40
  - lib/fastcache/memcache/protocol.rb
41
+ - lib/fastcache/memcache/errors.rb
41
42
  - lib/fastcache.rb
42
43
  - LICENSE
43
44
  - Rakefile
@@ -73,9 +74,10 @@ specification_version: 2
73
74
  summary: FastCache - a client side implementation of the memcached protocol
74
75
  test_files:
75
76
  - spec/util/maybe_spec.rb
77
+ - spec/routing/basic_router_spec.rb
76
78
  - spec/spec_helper.rb
77
79
  - spec/memcache/node_spec.rb
78
80
  - spec/memcache/protocol_spec.rb
79
- - benchmark/connection.rb
80
81
  - benchmark/benchmark_helper.rb
82
+ - benchmark/basic.rb
81
83
  - Rakefile
@@ -1,15 +0,0 @@
1
- n = 1000
2
- Benchmark.bm(30) do |x|
3
- x.report 'manual connection open/close' do
4
- n.times do
5
- conn = FastCache.connect NODE
6
- conn.close
7
- end
8
- end
9
-
10
- x.report 'block connection open/close' do
11
- n.times do
12
- FastCache.connect(NODE) {}
13
- end
14
- end
15
- end
File without changes