binary42-remix-stash 0.9.1 → 0.9.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.
@@ -8,6 +8,8 @@ It does require memcached 1.4+ but you should be running that anyway (if you are
8
8
 
9
9
  # TODO
10
10
 
11
+ * write extras, keys, and values directly to avoid packing overhead
12
+ * gracefully handle connection interruptions
11
13
  * optimize option merging with cache
12
14
  * make clusters selectable per stash
13
15
  * implement the rest of the memcached 1.4 binary API
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.9.2
@@ -2,9 +2,9 @@ require 'benchmark'
2
2
  require File.dirname(__FILE__) + '/../harness'
3
3
  require File.dirname(__FILE__) + '/../harness_cache'
4
4
 
5
- LARGE_NUMBER = 20_000
5
+ LARGE_NUMBER = 200_000
6
6
 
7
- Benchmark.bm do |b|
7
+ Benchmark.bmbm do |b|
8
8
  b.report('get/set stash') do
9
9
  LARGE_NUMBER.times {|n|
10
10
  stash[:abcxyz123] = n
@@ -0,0 +1,63 @@
1
+ require 'benchmark'
2
+ require File.dirname(__FILE__) + '/../harness'
3
+ require File.dirname(__FILE__) + '/../harness_cache'
4
+
5
+ LARGE_NUMBER = 20_000
6
+
7
+ large_value = 'a' * 100_000
8
+ med_value = 'b' * 2_000
9
+ small_value = 'c' * 100
10
+ tiny_value = 'd'
11
+
12
+ KEY = 'abc123xyz'
13
+
14
+ Benchmark.bmbm do |b|
15
+ b.report('100k stash') do
16
+ LARGE_NUMBER.times {
17
+ stash.write(KEY, large_value)
18
+ stash.read(KEY)
19
+ }
20
+ end
21
+ b.report('100k cache') do
22
+ LARGE_NUMBER.times {
23
+ Cache.set(KEY, large_value, 0, true)
24
+ Cache.get(KEY, true)
25
+ }
26
+ end
27
+ b.report('2k stash') do
28
+ LARGE_NUMBER.times {
29
+ stash.write(KEY, med_value)
30
+ stash.read(KEY)
31
+ }
32
+ end
33
+ b.report('2k cache') do
34
+ LARGE_NUMBER.times {
35
+ Cache.set(KEY, med_value, 0, true)
36
+ Cache.get(KEY, true)
37
+ }
38
+ end
39
+ b.report('100b stash') do
40
+ LARGE_NUMBER.times {
41
+ stash.write(KEY, small_value)
42
+ stash.read(KEY)
43
+ }
44
+ end
45
+ b.report('100b cache') do
46
+ LARGE_NUMBER.times {
47
+ Cache.set(KEY, small_value, 0, true)
48
+ Cache.get(KEY, true)
49
+ }
50
+ end
51
+ b.report('1b stash') do
52
+ LARGE_NUMBER.times {
53
+ stash.write(KEY, tiny_value)
54
+ stash.read(KEY)
55
+ }
56
+ end
57
+ b.report('1b cache') do
58
+ LARGE_NUMBER.times {
59
+ Cache.set(KEY, tiny_value, 0, true)
60
+ Cache.get(KEY, true)
61
+ }
62
+ end
63
+ end
@@ -1,7 +1,7 @@
1
1
  module Stash::Protocol
2
2
  extend self
3
3
 
4
- HEADER_FORMAT = "CCnCCnNa4a8"
4
+ HEADER_FORMAT = "CCnCCnNNQ"
5
5
 
6
6
  # Magic codes
7
7
  REQUEST = 0x80
@@ -50,47 +50,18 @@ module Stash::Protocol
50
50
  # Extras
51
51
  COUNTER_FAULT_EXPIRATION = 0xFFFFFFFF
52
52
 
53
- def add(io, key, data)
54
- # Field (offset) (value)
55
- # Magic (0) : 0x80
56
- # Opcode (1) : 0x02
57
- # Key length (2,3) : 0x0005
58
- # Extra length (4) : 0x08
59
- # Data type (5) : 0x00
60
- # Reserved (6,7) : 0x0000
61
- # Total body (8-11) : 0x00000012
62
- # Opaque (12-15): 0x00000000
63
- # CAS (16-23): 0x0000000000000000
64
- # Extras :
65
- # Flags (24-27): 0xdeadbeef
66
- # Expiry (28-31): 0x00000e10
67
- # Key (32-36): The textual string "Hello"
68
- # Value (37-41): The textual string "World"
69
- header = [REQUEST, ADD, key.size, 8, 0, 0, data.size + key.size + 8, '', '', 0, 0, key, data].pack(HEADER_FORMAT + 'NNa*a*')
53
+ ADD_PACKET = HEADER_FORMAT + 'NNa*a*'
54
+ def add(io, key, data, ttl = 0)
55
+ header = [REQUEST, ADD, key.size, 8, 0, 0, data.size + key.size + 8, 0, 0, 0, ttl, key, data].pack(ADD_PACKET)
70
56
  io.write(header)
71
57
  resp = read_resp(io)
72
58
  resp[:status] == NO_ERROR
73
59
  end
74
60
 
61
+ DECR_PACKET = HEADER_FORMAT + 'NNQNa*'
75
62
  def decr(io, key, step)
76
- # Field (offset) (value)
77
- # Magic (0) : 0x80
78
- # Opcode (1) : 0x06
79
- # Key length (2,3) : 0x0007
80
- # Extra length (4) : 0x14
81
- # Data type (5) : 0x00
82
- # Reserved (6,7) : 0x0000
83
- # Total body (8-11) : 0x0000001b
84
- # Opaque (12-15): 0x00000000
85
- # CAS (16-23): 0x0000000000000000
86
- # Extras :
87
- # delta (24-31): 0x0000000000000001
88
- # initial (32-39): 0x0000000000000000
89
- # exipration (40-43): 0x00000e10
90
- # Key : Textual string "counter"
91
- # Value : None
92
63
  low, high = split64(step)
93
- header = [REQUEST, DECREMENT, key.size, 20, 0, 0, key.size + 20, '', '', high, low, 0, COUNTER_FAULT_EXPIRATION, key].pack(HEADER_FORMAT + 'NNQNa*')
64
+ header = [REQUEST, DECREMENT, key.size, 20, 0, 0, key.size + 20, 0, 0, high, low, 0, COUNTER_FAULT_EXPIRATION, key].pack(DECR_PACKET)
94
65
  io.write(header)
95
66
  resp = read_resp(io)
96
67
  if resp[:status] == NO_ERROR
@@ -98,60 +69,25 @@ module Stash::Protocol
98
69
  end
99
70
  end
100
71
 
72
+ DELETE_PACKET = HEADER_FORMAT + 'a*'
101
73
  def delete(io, key, ttl = 0)
102
- # Field (offset) (value)
103
- # Magic (0) : 0x80
104
- # Opcode (1) : 0x04
105
- # Key length (2,3) : 0x0005
106
- # Extra length (4) : 0x00
107
- # Data type (5) : 0x00
108
- # Reserved (6,7) : 0x0000
109
- # Total body (8-11) : 0x00000005
110
- # Opaque (12-15): 0x00000000
111
- # CAS (16-23): 0x0000000000000000
112
- # Extras : None
113
- # Key : The textual string "Hello"
114
- # Value : None
115
- header = [REQUEST, DELETE, key.size, 0, 0, 0, key.size, '', '', key].pack(HEADER_FORMAT + 'a*')
74
+ header = [REQUEST, DELETE, key.size, 0, 0, 0, key.size, 0, 0, key].pack(DELETE_PACKET)
116
75
  io.write(header)
117
76
  resp = read_resp(io)
118
77
  resp[:status] == NO_ERROR
119
78
  end
120
79
 
80
+ FLUSH_PACKET = HEADER_FORMAT + 'N'
121
81
  def flush(io)
122
- # Field (offset) (value)
123
- # Magic (0) : 0x80
124
- # Opcode (1) : 0x08
125
- # Key length (2,3) : 0x0000
126
- # Extra length (4) : 0x04
127
- # Data type (5) : 0x00
128
- # Reserved (6,7) : 0x0000
129
- # Total body (8-11) : 0x00000004
130
- # Opaque (12-15): 0x00000000
131
- # CAS (16-23): 0x0000000000000000
132
- # Extras :
133
- # Expiry (24-27): 0x000e10
134
- header = [REQUEST, FLUSH, 0, 4, 0, 0, 4, '', '', 0].pack(HEADER_FORMAT + 'N')
82
+ header = [REQUEST, FLUSH, 0, 4, 0, 0, 4, 0, 0, 0].pack(FLUSH_PACKET)
135
83
  io.write(header)
136
84
  resp = read_resp(io)
137
85
  resp[:status] == NO_ERROR
138
86
  end
139
87
 
88
+ GET_PACKET = HEADER_FORMAT + 'a*'
140
89
  def get(io, key)
141
- # Field (offset) (value)
142
- # Magic (0) : 0x80
143
- # Opcode (1) : 0x00
144
- # Key length (2,3) : 0x0005
145
- # Extra length (4) : 0x00
146
- # Data type (5) : 0x00
147
- # Reserved (6,7) : 0x0000
148
- # Total body (8-11) : 0x00000005
149
- # Opaque (12-15): 0x00000000
150
- # CAS (16-23): 0x0000000000000000
151
- # Extras : None
152
- # Key (24-29): The textual string: "Hello"
153
- # Value : None
154
- header = [REQUEST, GET, key.size, 0, 0, 0, key.size, '', '', key].pack(HEADER_FORMAT + 'a*')
90
+ header = [REQUEST, GET, key.size, 0, 0, 0, key.size, 0, 0, key].pack(GET_PACKET)
155
91
  io.write(header)
156
92
  resp = read_resp(io)
157
93
  resp[:status] == NO_ERROR ? parse_get(resp[:body])[:value] : nil
@@ -161,25 +97,10 @@ module Stash::Protocol
161
97
  load_ruby_value(get(io, key))
162
98
  end
163
99
 
100
+ INCR_PACKET = HEADER_FORMAT + 'NNQNa*'
164
101
  def incr(io, key, step)
165
- # Field (offset) (value)
166
- # Magic (0) : 0x80
167
- # Opcode (1) : 0x05
168
- # Key length (2,3) : 0x0007
169
- # Extra length (4) : 0x14
170
- # Data type (5) : 0x00
171
- # Reserved (6,7) : 0x0000
172
- # Total body (8-11) : 0x0000001b
173
- # Opaque (12-15): 0x00000000
174
- # CAS (16-23): 0x0000000000000000
175
- # Extras :
176
- # delta (24-31): 0x0000000000000001
177
- # initial (32-39): 0x0000000000000000
178
- # exipration (40-43): 0x00000e10
179
- # Key : Textual string "counter"
180
- # Value : None
181
102
  low, high = split64(step)
182
- header = [REQUEST, INCREMENT, key.size, 20, 0, 0, key.size + 20, '', '', high, low, 0, COUNTER_FAULT_EXPIRATION, key].pack(HEADER_FORMAT + 'NNQNa*')
103
+ header = [REQUEST, INCREMENT, key.size, 20, 0, 0, key.size + 20, 0, 0, high, low, 0, COUNTER_FAULT_EXPIRATION, key].pack(INCR_PACKET)
183
104
  io.write(header)
184
105
  resp = read_resp(io)
185
106
  if resp[:status] == NO_ERROR
@@ -187,24 +108,10 @@ module Stash::Protocol
187
108
  end
188
109
  end
189
110
 
111
+ SET_PACKET = HEADER_FORMAT + 'NNa*a*'
190
112
  def set(io, key, data, ttl = 0)
191
- # Field (offset) (value)
192
- # Magic (0) : 0x80
193
- # Opcode (1) : 0x01
194
- # Key length (2,3) : 0x0005
195
- # Extra length (4) : 0x08
196
- # Data type (5) : 0x00
197
- # Reserved (6,7) : 0x0000
198
- # Total body (8-11) : 0x00000012
199
- # Opaque (12-15): 0x00000000
200
- # CAS (16-23): 0x0000000000000000
201
- # Extras :
202
- # Flags (24-27): 0xdeadbeef
203
- # Expiry (28-31): 0x00000e10
204
- # Key (32-36): The textual string "Hello"
205
- # Value (37-41): The textual string "World"
206
- header = [REQUEST, SET, key.size, 8, 0, 0, data.size + key.size + 8, '', '', 0, 0, key, data].pack(HEADER_FORMAT + 'NNa*a*')
207
- io.write(header)
113
+ header = [REQUEST, SET, key.size, 8, 0, 0, data.size + key.size + 8, 0, 0, 0, ttl, key, data].pack(SET_PACKET)
114
+ io << header
208
115
  resp = read_resp(io)
209
116
  resp[:status] == NO_ERROR
210
117
  end
@@ -213,10 +120,6 @@ module Stash::Protocol
213
120
  set(io, key, Marshal.dump(value), ttl)
214
121
  end
215
122
 
216
- def method_missing(message, *a)
217
- fail [:NOT_IMPLEMENTED, self, message, *a].inspect
218
- end
219
-
220
123
  private
221
124
 
222
125
  def load_ruby_value(data)
@@ -242,7 +145,7 @@ private
242
145
  resp = { :magic => magic, :opcode => opcode, :key_length => key_length,
243
146
  :extra => extra, :type => type, :status => status,
244
147
  :body_length => body_length, :opaque => opaque, :cas => cas }
245
- resp[:body] = io.read(resp[:body_length]) if resp[:body_length] > 0
148
+ resp[:body] = io.read(body_length) if body_length > 0
246
149
  resp
247
150
  end
248
151
 
data/lib/remix/stash.rb CHANGED
@@ -33,6 +33,13 @@ class Stash
33
33
  @opts = name == :root ? {:coherency => :action, :ttl => 0} : {}
34
34
  end
35
35
 
36
+ def add(*keys)
37
+ opts = default_opts(keys)
38
+ value = keys.pop
39
+ key = canonical_key(keys)
40
+ cluster.select(key) {|io| Protocol.add(io, key, value, opts[:ttl])}
41
+ end
42
+
36
43
  def clear(*keys)
37
44
  if keys.empty?
38
45
  if @name == :root
@@ -159,8 +166,9 @@ class Stash
159
166
 
160
167
  private
161
168
 
169
+ KEY_SEPARATOR = '/'
162
170
  def canonical_key(keys)
163
- "#{implicit_scope}#{keys.join('/')}#{vector}"
171
+ "#{implicit_scope}#{keys.join(KEY_SEPARATOR)}#{vector}"
164
172
  end
165
173
 
166
174
  def cluster
@@ -175,12 +183,13 @@ private
175
183
  params.last.is_a?(Hash) ? default.merge(params.pop) : default
176
184
  end
177
185
 
186
+ EMPTY_SCOPE = ''
178
187
  def implicit_scope
179
188
  if @scope
180
189
  scope = @scope.call(self)
181
- scope ? "#{scope}/" : ''
190
+ scope ? "#{scope}/" : EMPTY_SCOPE
182
191
  else
183
- ''
192
+ EMPTY_SCOPE
184
193
  end
185
194
  end
186
195
 
data/remix-stash.gemspec CHANGED
@@ -5,23 +5,24 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remix-stash}
8
- s.version = "0.9.1"
8
+ s.version = "0.9.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Mitchell"]
12
- s.date = %q{2009-09-02}
12
+ s.date = %q{2009-09-09}
13
13
  s.email = %q{binary42@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
16
- "README.mdown"
16
+ "README.markdown"
17
17
  ]
18
18
  s.files = [
19
19
  ".gitignore",
20
20
  "LICENSE",
21
- "README.mdown",
21
+ "README.markdown",
22
22
  "Rakefile",
23
23
  "VERSION",
24
- "examples/bench.rb",
24
+ "benchmarks/get_set.rb",
25
+ "benchmarks/payload.rb",
25
26
  "examples/eval.rb",
26
27
  "examples/gate.rb",
27
28
  "examples/getset.rb",
@@ -49,7 +50,6 @@ Gem::Specification.new do |s|
49
50
  "spec/extension_spec.rb",
50
51
  "spec/spec.rb",
51
52
  "spec/stash_spec.rb",
52
- "examples/bench.rb",
53
53
  "examples/eval.rb",
54
54
  "examples/gate.rb",
55
55
  "examples/getset.rb",
data/spec/stash_spec.rb CHANGED
@@ -105,6 +105,21 @@ class StashSpec < Spec
105
105
 
106
106
  end
107
107
 
108
+ context '#add' do
109
+
110
+ should 'allow keys to be set with new values' do
111
+ assert stash.add('f', '42')
112
+ assert_equal '42', stash.read('f')
113
+ end
114
+
115
+ should 'not overwrite existing values' do
116
+ stash['f'] = '43'
117
+ assert !stash.add('f', '42')
118
+ assert_equal '43', stash['f']
119
+ end
120
+
121
+ end
122
+
108
123
  context '#clear' do
109
124
 
110
125
  setup do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binary42-remix-stash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Mitchell
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-09-02 00:00:00 -07:00
12
+ date: 2009-09-09 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -21,14 +21,15 @@ extensions: []
21
21
 
22
22
  extra_rdoc_files:
23
23
  - LICENSE
24
- - README.mdown
24
+ - README.markdown
25
25
  files:
26
26
  - .gitignore
27
27
  - LICENSE
28
- - README.mdown
28
+ - README.markdown
29
29
  - Rakefile
30
30
  - VERSION
31
- - examples/bench.rb
31
+ - benchmarks/get_set.rb
32
+ - benchmarks/payload.rb
32
33
  - examples/eval.rb
33
34
  - examples/gate.rb
34
35
  - examples/getset.rb
@@ -75,7 +76,6 @@ test_files:
75
76
  - spec/extension_spec.rb
76
77
  - spec/spec.rb
77
78
  - spec/stash_spec.rb
78
- - examples/bench.rb
79
79
  - examples/eval.rb
80
80
  - examples/gate.rb
81
81
  - examples/getset.rb