binary42-fastcache 0.1 → 0.2

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.
@@ -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