ruby-tokyotyrant 0.3.1 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +30 -3
- data/Rakefile +6 -2
- data/benchmarks/balancer.rb +101 -0
- data/benchmarks/db.rb +22 -2
- data/ext/tokyo_tyrant.c +4 -0
- data/ext/tokyo_tyrant.h +3 -0
- data/ext/tokyo_tyrant_module.c +2 -2
- data/ext/tokyo_utils.c +362 -0
- data/ext/tokyo_utils.h +8 -0
- data/lib/tokyo_tyrant/balancer.rb +189 -0
- data/spec/spec_base.rb +1 -0
- data/spec/tokyo_tyrant_balancer_db_spec.rb +160 -0
- data/spec/tokyo_tyrant_balancer_table_spec.rb +177 -0
- metadata +19 -4
data/README.rdoc
CHANGED
@@ -4,10 +4,11 @@ This is a c extension for Ruby to access TokyoTyrant databases. It currently su
|
|
4
4
|
|
5
5
|
== Install
|
6
6
|
|
7
|
-
# install tokyocabinet (1.4.
|
7
|
+
# install tokyocabinet (1.4.34) and tokyotyrant (requires 1.1.35)
|
8
8
|
# after installing tc and tt on linux I had to /sbin/ldconfig (as root)
|
9
|
-
gem
|
10
|
-
sudo gem
|
9
|
+
sudo gem install gemcutter
|
10
|
+
sudo gem tumble
|
11
|
+
sudo gem install ruby-tokyotyrant
|
11
12
|
|
12
13
|
== Performance
|
13
14
|
|
@@ -196,6 +197,32 @@ This is not in production but the initial benchmarks are very interesting. Resul
|
|
196
197
|
t.run(:echo, 'hello', 'world')
|
197
198
|
# => "hello\tworld"
|
198
199
|
|
200
|
+
=== Balanced Nodes with Consistent Hashing
|
201
|
+
|
202
|
+
# usage is similar to single node
|
203
|
+
require 'tokyo_tyrant/balancer'
|
204
|
+
|
205
|
+
servers = ['127.0.0.1:1978',
|
206
|
+
'127.0.0.1:1979',
|
207
|
+
'127.0.0.1:1980',
|
208
|
+
'127.0.0.1:1981']
|
209
|
+
|
210
|
+
tb = TokyoTyrant::Balancer::Table.new(servers)
|
211
|
+
|
212
|
+
# store server is determined by key which is consistent
|
213
|
+
tb[:foo] = { 'foo' => 'bar' }
|
214
|
+
tb[:bar] = { 'bar' => 'baz' }
|
215
|
+
|
216
|
+
# retrieval server is determined by key which is consistent
|
217
|
+
tb[:foo]
|
218
|
+
# => { 'foo' => 'bar' }
|
219
|
+
|
220
|
+
# aggregate from all nodes
|
221
|
+
tb.size
|
222
|
+
|
223
|
+
# parallel_search based querying across all nodes
|
224
|
+
tb.find{ |q| q.condition(:foo, :streq, 'bar') }
|
225
|
+
|
199
226
|
== Contributors
|
200
227
|
|
201
228
|
* Flinn Mueller (actsasflinn) author/maintainer
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ CLEAN.include('pkg', 'tmp')
|
|
20
20
|
|
21
21
|
gemspec = Gem::Specification.new do |s|
|
22
22
|
s.name = 'ruby-tokyotyrant'
|
23
|
-
s.version = '0.
|
23
|
+
s.version = '0.4'
|
24
24
|
s.authors = [ 'Flinn' ]
|
25
25
|
s.email = 'flinn@actsasflinn.com'
|
26
26
|
s.homepage = 'http://github.com/actsasflinn/ruby-tokyotyrant/'
|
@@ -35,9 +35,12 @@ gemspec = Gem::Specification.new do |s|
|
|
35
35
|
'Rakefile',
|
36
36
|
'README.rdoc'] +
|
37
37
|
Dir['ext/**/*.[rb|c|h]'] +
|
38
|
+
Dir['lib/**/*.rb'] +
|
38
39
|
Dir['spec/**/*'] +
|
39
40
|
Dir['benchmarks/**/*']
|
40
41
|
s.extensions << "ext/extconf.rb"
|
42
|
+
|
43
|
+
s.add_runtime_dependency('fast_hash_ring', '>= 0.1.1')
|
41
44
|
end
|
42
45
|
|
43
46
|
task :gemspec do
|
@@ -50,13 +53,14 @@ Rake::GemPackageTask.new(gemspec) do |pkg|
|
|
50
53
|
pkg.need_tar = true
|
51
54
|
end
|
52
55
|
|
53
|
-
Rake::PackageTask.new('ruby-tokyotyrant', '0.
|
56
|
+
Rake::PackageTask.new('ruby-tokyotyrant', '0.4') do |pkg|
|
54
57
|
pkg.need_zip = true
|
55
58
|
pkg.package_files = FileList[
|
56
59
|
'COPYING',
|
57
60
|
'Rakefile',
|
58
61
|
'README.rdoc',
|
59
62
|
'ext/**/*',
|
63
|
+
'lib/**/*.[rb]',
|
60
64
|
'spec/**/*',
|
61
65
|
'benchmarks/**/*'
|
62
66
|
].to_a
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'faker'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
#
|
7
|
+
# the data
|
8
|
+
#
|
9
|
+
|
10
|
+
colnames = %w{ name sex birthday divisions }
|
11
|
+
|
12
|
+
$year = (1909 .. 2009).to_a
|
13
|
+
$month = (1..12).to_a
|
14
|
+
$day = (1..28).to_a # not bothering with month diffs
|
15
|
+
|
16
|
+
def rbdate
|
17
|
+
DateTime.new($year[rand($year.size)], $month[rand($month.size)], $day[rand($day.size)])
|
18
|
+
end
|
19
|
+
|
20
|
+
def rdiv
|
21
|
+
case rand(3)
|
22
|
+
when 0
|
23
|
+
'dev'
|
24
|
+
when 1
|
25
|
+
'brd'
|
26
|
+
else
|
27
|
+
'brd,dev'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def rgen
|
32
|
+
(rand(2) == 1 ? 'male' : 'female')
|
33
|
+
end
|
34
|
+
|
35
|
+
data = [
|
36
|
+
[ 'Alphonse Armalite', 'male', DateTime.new(1972, 10, 14), 'brd,dev' ],
|
37
|
+
[ 'Brutus Beromunster', 'male', DateTime.new(1964, 07, 14), 'dev' ],
|
38
|
+
[ 'Crystel Chucknorris', 'female', DateTime.new(1980, 07, 12), 'brd' ],
|
39
|
+
[ 'Desree Dylan', 'female', DateTime.new(1954, 07, 13), 'brd,dev' ]
|
40
|
+
]
|
41
|
+
|
42
|
+
10_000.times do |i|
|
43
|
+
data << [ Faker::Name.name, rgen, rbdate, rdiv]
|
44
|
+
end
|
45
|
+
|
46
|
+
$find_name_list = []
|
47
|
+
100.times { $find_name_list << data[rand(data.size)][0] }
|
48
|
+
|
49
|
+
data.collect! { |e|
|
50
|
+
(0..colnames.length - 1).inject({}) { |h, i| h[colnames[i]] = e[i]; h }
|
51
|
+
}
|
52
|
+
|
53
|
+
data_h = {}
|
54
|
+
i = 0
|
55
|
+
data1 = data.collect { |e|
|
56
|
+
i = i + 1
|
57
|
+
h = e.dup
|
58
|
+
h['birthday'] = h['birthday'].to_s
|
59
|
+
data_h[i.to_s] = h
|
60
|
+
h
|
61
|
+
}
|
62
|
+
|
63
|
+
require 'tokyo_tyrant'
|
64
|
+
|
65
|
+
t = TokyoTyrant::Table.new('127.0.0.1', 45001)
|
66
|
+
t.clear
|
67
|
+
|
68
|
+
2.times { puts }
|
69
|
+
puts 'TokyoTyrant Single'
|
70
|
+
|
71
|
+
Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
72
|
+
b.report('inserting data') do
|
73
|
+
data1.each_with_index { |e, i| t[i.to_s] = e }
|
74
|
+
end
|
75
|
+
|
76
|
+
b.report('reading data') do
|
77
|
+
data1.each_with_index { |e, i| nothing = t[i.to_s] }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require 'lib/tokyo_tyrant/balancer'
|
82
|
+
|
83
|
+
servers = ['127.0.0.1:45001',
|
84
|
+
'127.0.0.1:45002',
|
85
|
+
'127.0.0.1:45003']
|
86
|
+
|
87
|
+
tb = TokyoTyrant::Balancer::Table.new(servers)
|
88
|
+
tb.clear
|
89
|
+
|
90
|
+
2.times { puts }
|
91
|
+
puts 'TokyoTyrant Balancer'
|
92
|
+
|
93
|
+
Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
94
|
+
b.report('inserting data') do
|
95
|
+
data1.each_with_index { |e, i| tb[i.to_s] = e }
|
96
|
+
end
|
97
|
+
|
98
|
+
b.report('reading data') do
|
99
|
+
data1.each_with_index { |e, i| nothing = tb[i.to_s] }
|
100
|
+
end
|
101
|
+
end
|
data/benchmarks/db.rb
CHANGED
@@ -4,10 +4,11 @@ require 'faker'
|
|
4
4
|
|
5
5
|
data = []
|
6
6
|
|
7
|
-
|
7
|
+
100_000.times do |i|
|
8
8
|
data << Faker::Name.name
|
9
9
|
end
|
10
10
|
|
11
|
+
=begin
|
11
12
|
require 'rufus/tokyo/tyrant'
|
12
13
|
|
13
14
|
r = Rufus::Tokyo::Tyrant.new('127.0.0.1', 45000)
|
@@ -44,9 +45,10 @@ Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
|
44
45
|
data.each_with_index { |e, i| nothing = rdb.get(i.to_s) }
|
45
46
|
end
|
46
47
|
end
|
48
|
+
=end
|
47
49
|
|
48
50
|
require 'tokyo_tyrant'
|
49
|
-
t = TokyoTyrant::DB.new('127.0.0.1',
|
51
|
+
t = TokyoTyrant::DB.new('127.0.0.1', 1978)
|
50
52
|
t.clear
|
51
53
|
|
52
54
|
2.times { puts }
|
@@ -62,6 +64,23 @@ Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
67
|
+
m = TokyoTyrant::MDB.new
|
68
|
+
m.clear
|
69
|
+
|
70
|
+
2.times { puts }
|
71
|
+
puts 'MDB'
|
72
|
+
|
73
|
+
Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
74
|
+
b.report('inserting data') do
|
75
|
+
data.each_with_index { |e, i| m[i] = e }
|
76
|
+
end
|
77
|
+
|
78
|
+
b.report('reading data') do
|
79
|
+
data.each_with_index { |e, i| nothing = m[i] }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
=begin
|
65
84
|
require 'memcached'
|
66
85
|
m = Memcached.new('127.0.0.1:45000')
|
67
86
|
m.flush
|
@@ -112,3 +131,4 @@ Benchmark.benchmark(' ' * 20 + Benchmark::Tms::CAPTION, 20) do |b|
|
|
112
131
|
data.each_with_index { |e, i| nothing = mc.get(i.to_s) }
|
113
132
|
end
|
114
133
|
end
|
134
|
+
=end
|
data/ext/tokyo_tyrant.c
CHANGED
@@ -128,6 +128,7 @@ VALUE eTokyoTyrantError;
|
|
128
128
|
VALUE cDB;
|
129
129
|
VALUE cTable;
|
130
130
|
VALUE cQuery;
|
131
|
+
VALUE cMDB;
|
131
132
|
|
132
133
|
void Init_tokyo_tyrant(){
|
133
134
|
mTokyoTyrant = rb_define_module("TokyoTyrant");
|
@@ -144,4 +145,7 @@ void Init_tokyo_tyrant(){
|
|
144
145
|
|
145
146
|
cQuery = rb_define_class_under(mTokyoTyrant, "Query", rb_cObject);
|
146
147
|
init_query();
|
148
|
+
|
149
|
+
cMDB = rb_define_class_under(mTokyoTyrant, "MDB", rb_cObject);
|
150
|
+
init_utils();
|
147
151
|
}
|
data/ext/tokyo_tyrant.h
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
#include <tokyo_tyrant_db.h>
|
12
12
|
#include <tokyo_tyrant_table.h>
|
13
13
|
#include <tokyo_tyrant_query.h>
|
14
|
+
#include <tokyo_utils.h>
|
14
15
|
|
15
16
|
#define RDBVNDATA "@rdb"
|
16
17
|
#define RDBQRYVNDATA "@rdbquery"
|
@@ -19,6 +20,7 @@
|
|
19
20
|
#define TTPUTKEEP 1
|
20
21
|
#define TTPUTCAT 2
|
21
22
|
#define TTPUTNR 3
|
23
|
+
#define MDBVNDATA "@mdb"
|
22
24
|
|
23
25
|
#if !defined(RSTRING_PTR)
|
24
26
|
#define RSTRING_PTR(TC_s) (RSTRING(TC_s)->ptr)
|
@@ -35,6 +37,7 @@ extern VALUE eTokyoTyrantError;
|
|
35
37
|
extern VALUE cDB;
|
36
38
|
extern VALUE cTable;
|
37
39
|
extern VALUE cQuery;
|
40
|
+
extern VALUE cMDB;
|
38
41
|
|
39
42
|
extern VALUE StringRaw(const char *buf, int bsiz);
|
40
43
|
extern VALUE StringValueEx(VALUE vobj);
|
data/ext/tokyo_tyrant_module.c
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#include <
|
1
|
+
#include <tokyo_tyrant_module.h>
|
2
2
|
|
3
3
|
extern TCRDB *mTokyoTyrant_getdb(VALUE vself){
|
4
4
|
TCRDB *db;
|
@@ -438,7 +438,7 @@ void init_mod(){
|
|
438
438
|
rb_define_alias(mTokyoTyrant, "clear", "vanish");
|
439
439
|
rb_define_method(mTokyoTyrant, "copy", mTokyoTyrant_copy, 1);
|
440
440
|
rb_define_method(mTokyoTyrant, "restore", mTokyoTyrant_restore, 2);
|
441
|
-
rb_define_method(mTokyoTyrant, "setmst", mTokyoTyrant_setmst,
|
441
|
+
rb_define_method(mTokyoTyrant, "setmst", mTokyoTyrant_setmst, 4);
|
442
442
|
rb_define_method(mTokyoTyrant, "rnum", mTokyoTyrant_rnum, 0);
|
443
443
|
rb_define_alias(mTokyoTyrant, "count", "rnum");
|
444
444
|
rb_define_method(mTokyoTyrant, "empty?", mTokyoTyrant_empty, 0);
|
data/ext/tokyo_utils.c
ADDED
@@ -0,0 +1,362 @@
|
|
1
|
+
#include <tokyo_utils.h>
|
2
|
+
|
3
|
+
extern TCMDB *cMDB_getdb(VALUE vself){
|
4
|
+
TCMDB *db;
|
5
|
+
Data_Get_Struct(rb_iv_get(vself, MDBVNDATA), TCMDB, db);
|
6
|
+
return db;
|
7
|
+
}
|
8
|
+
|
9
|
+
static void cMDB_free(TCMDB *db){
|
10
|
+
tcmdbdel(db);
|
11
|
+
}
|
12
|
+
|
13
|
+
static VALUE cMDB_bnum(VALUE vself){
|
14
|
+
return rb_iv_get(vself, "@bnum");
|
15
|
+
}
|
16
|
+
|
17
|
+
static VALUE cMDB_initialize(int argc, VALUE *argv, VALUE vself){
|
18
|
+
VALUE bnum;
|
19
|
+
TCMDB *db;
|
20
|
+
|
21
|
+
rb_scan_args(argc, argv, "0", &bnum);
|
22
|
+
if(NIL_P(bnum)){
|
23
|
+
db = tcmdbnew();
|
24
|
+
} else {
|
25
|
+
db = tcmdbnew2(bnum);
|
26
|
+
}
|
27
|
+
|
28
|
+
rb_iv_set(vself, "@bnum", bnum);
|
29
|
+
|
30
|
+
rb_iv_set(vself, MDBVNDATA, Data_Wrap_Struct(rb_cObject, 0, cMDB_free, db));
|
31
|
+
|
32
|
+
return Qtrue;
|
33
|
+
}
|
34
|
+
|
35
|
+
static VALUE cMDB_put_method(VALUE vself, VALUE vkey, VALUE vstr, int method){
|
36
|
+
bool res;
|
37
|
+
TCMDB *db = cMDB_getdb(vself);
|
38
|
+
|
39
|
+
vkey = StringValueEx(vkey);
|
40
|
+
vstr = StringValueEx(vstr);
|
41
|
+
|
42
|
+
res = true;
|
43
|
+
switch(method){
|
44
|
+
case TTPUT:
|
45
|
+
tcmdbput(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vstr), RSTRING_LEN(vstr));
|
46
|
+
break;
|
47
|
+
case TTPUTKEEP:
|
48
|
+
res = tcmdbputkeep(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vstr), RSTRING_LEN(vstr));
|
49
|
+
break;
|
50
|
+
case TTPUTCAT:
|
51
|
+
tcmdbputcat(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vstr), RSTRING_LEN(vstr));
|
52
|
+
break;
|
53
|
+
default:
|
54
|
+
res = false;
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
|
58
|
+
return res ? Qtrue : Qfalse;
|
59
|
+
}
|
60
|
+
|
61
|
+
static VALUE cMDB_put(VALUE vself, VALUE vkey, VALUE vstr){
|
62
|
+
return cMDB_put_method(vself, vkey, vstr, TTPUT);
|
63
|
+
}
|
64
|
+
|
65
|
+
static VALUE cMDB_putkeep(VALUE vself, VALUE vkey, VALUE vstr){
|
66
|
+
return cMDB_put_method(vself, vkey, vstr, TTPUTKEEP);
|
67
|
+
}
|
68
|
+
|
69
|
+
static VALUE cMDB_putcat(VALUE vself, VALUE vkey, VALUE vstr){
|
70
|
+
return cMDB_put_method(vself, vkey, vstr, TTPUTCAT);
|
71
|
+
}
|
72
|
+
|
73
|
+
static VALUE cMDB_get(VALUE vself, VALUE vkey){
|
74
|
+
VALUE vval;
|
75
|
+
char *buf;
|
76
|
+
int bsiz;
|
77
|
+
TCMDB *db = cMDB_getdb(vself);
|
78
|
+
|
79
|
+
// this is ugly
|
80
|
+
vkey = StringValueEx(vkey);
|
81
|
+
if(!(buf = tcmdbget(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &bsiz))){
|
82
|
+
return Qnil;
|
83
|
+
} else {
|
84
|
+
vval = StringRaw(buf, bsiz);
|
85
|
+
}
|
86
|
+
|
87
|
+
tcfree(buf);
|
88
|
+
return vval;
|
89
|
+
}
|
90
|
+
|
91
|
+
static VALUE cMDB_vsiz(VALUE vself, VALUE vkey){
|
92
|
+
TCMDB *db = cMDB_getdb(vself);
|
93
|
+
|
94
|
+
vkey = StringValueEx(vkey);
|
95
|
+
return INT2NUM(tcmdbvsiz(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey)));
|
96
|
+
}
|
97
|
+
|
98
|
+
static VALUE cMDB_fetch(int argc, VALUE *argv, VALUE vself){
|
99
|
+
VALUE vkey, vrv, vforce;
|
100
|
+
rb_scan_args(argc, argv, "11", &vkey, &vforce);
|
101
|
+
if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
|
102
|
+
if(vforce == Qnil) vforce = Qfalse;
|
103
|
+
|
104
|
+
if(vforce != Qfalse || (vrv = cMDB_get(vself, vkey)) == Qnil){
|
105
|
+
vrv = rb_yield(vkey);
|
106
|
+
cMDB_put(vself, vkey, vrv);
|
107
|
+
}
|
108
|
+
return vrv;
|
109
|
+
}
|
110
|
+
|
111
|
+
static VALUE cMDB_each(VALUE vself){
|
112
|
+
VALUE vrv;
|
113
|
+
if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
|
114
|
+
TCMDB *db = cMDB_getdb(vself);
|
115
|
+
vrv = Qnil;
|
116
|
+
tcmdbiterinit(db);
|
117
|
+
int ksiz;
|
118
|
+
char *kbuf;
|
119
|
+
while((kbuf = tcmdbiternext(db, &ksiz)) != NULL){
|
120
|
+
int vsiz;
|
121
|
+
char *vbuf = tcmdbget(db, kbuf, ksiz, &vsiz);
|
122
|
+
vrv = rb_yield_values(2, rb_str_new2(kbuf), StringRaw(vbuf, vsiz));
|
123
|
+
tcfree(vbuf);
|
124
|
+
tcfree(kbuf);
|
125
|
+
}
|
126
|
+
return vrv;
|
127
|
+
}
|
128
|
+
|
129
|
+
static VALUE cMDB_each_value(VALUE vself){
|
130
|
+
VALUE vrv;
|
131
|
+
if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
|
132
|
+
TCMDB *db = cMDB_getdb(vself);
|
133
|
+
vrv = Qnil;
|
134
|
+
tcmdbiterinit(db);
|
135
|
+
int ksiz;
|
136
|
+
char *kbuf;
|
137
|
+
while((kbuf = tcmdbiternext(db, &ksiz)) != NULL){
|
138
|
+
int vsiz;
|
139
|
+
char *vbuf = tcmdbget(db, kbuf, ksiz, &vsiz);
|
140
|
+
vrv = rb_yield_values(1, StringRaw(vbuf, vsiz));
|
141
|
+
tcfree(vbuf);
|
142
|
+
tcfree(kbuf);
|
143
|
+
}
|
144
|
+
return vrv;
|
145
|
+
}
|
146
|
+
|
147
|
+
static VALUE cMDB_values(VALUE vself){
|
148
|
+
VALUE vary;
|
149
|
+
TCMDB *db = cMDB_getdb(vself);
|
150
|
+
vary = rb_ary_new2(tcmdbrnum(db));
|
151
|
+
tcmdbiterinit(db);
|
152
|
+
int ksiz;
|
153
|
+
char *kbuf;
|
154
|
+
while((kbuf = tcmdbiternext(db, &ksiz)) != NULL){
|
155
|
+
int vsiz;
|
156
|
+
char *vbuf = tcmdbget(db, kbuf, ksiz, &vsiz);
|
157
|
+
rb_ary_push(vary, StringRaw(vbuf, vsiz));
|
158
|
+
tcfree(vbuf);
|
159
|
+
tcfree(kbuf);
|
160
|
+
}
|
161
|
+
return vary;
|
162
|
+
}
|
163
|
+
|
164
|
+
static VALUE cMDB_out(VALUE vself, VALUE vkey){
|
165
|
+
TCMDB *db = cMDB_getdb(vself);
|
166
|
+
|
167
|
+
vkey = StringValueEx(vkey);
|
168
|
+
return tcmdbout(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) ? Qtrue : Qfalse;
|
169
|
+
}
|
170
|
+
|
171
|
+
static VALUE cMDB_check(VALUE vself, VALUE vkey){
|
172
|
+
TCMDB *db = cMDB_getdb(vself);
|
173
|
+
|
174
|
+
vkey = StringValueEx(vkey);
|
175
|
+
return tcmdbvsiz(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey)) >= 0 ? Qtrue : Qfalse;
|
176
|
+
}
|
177
|
+
|
178
|
+
static VALUE cMDB_iterinit(VALUE vself){
|
179
|
+
TCMDB *db = cMDB_getdb(vself);
|
180
|
+
|
181
|
+
tcmdbiterinit(db);
|
182
|
+
return Qtrue;
|
183
|
+
}
|
184
|
+
|
185
|
+
static VALUE cMDB_iternext(VALUE vself){
|
186
|
+
VALUE vval;
|
187
|
+
char *vbuf;
|
188
|
+
TCMDB *db = cMDB_getdb(vself);
|
189
|
+
|
190
|
+
if(!(vbuf = tcmdbiternext2(db))) return Qnil;
|
191
|
+
vval = rb_str_new2(vbuf);
|
192
|
+
tcfree(vbuf);
|
193
|
+
|
194
|
+
return vval;
|
195
|
+
}
|
196
|
+
|
197
|
+
static VALUE cMDB_fwmkeys(int argc, VALUE *argv, VALUE vself){
|
198
|
+
VALUE vprefix, vmax, vary;
|
199
|
+
TCLIST *keys;
|
200
|
+
int max;
|
201
|
+
TCMDB *db = cMDB_getdb(vself);
|
202
|
+
rb_scan_args(argc, argv, "11", &vprefix, &vmax);
|
203
|
+
|
204
|
+
vprefix = StringValueEx(vprefix);
|
205
|
+
max = (vmax == Qnil) ? -1 : NUM2INT(vmax);
|
206
|
+
keys = tcmdbfwmkeys(db, RSTRING_PTR(vprefix), RSTRING_LEN(vprefix), max);
|
207
|
+
vary = listtovary(keys);
|
208
|
+
tclistdel(keys);
|
209
|
+
return vary;
|
210
|
+
}
|
211
|
+
|
212
|
+
static VALUE cMDB_keys(VALUE vself){
|
213
|
+
/*
|
214
|
+
VALUE vary;
|
215
|
+
char *kxstr;
|
216
|
+
TCMDB *db = cMDB_getdb(vself);
|
217
|
+
vary = rb_ary_new2(tcmdbrnum(db));
|
218
|
+
tcmdbiterinit(db);
|
219
|
+
while((kxstr = tcmdbiternext2(db)) != NULL){
|
220
|
+
rb_ary_push(vary, rb_str_new2(kxstr));
|
221
|
+
}
|
222
|
+
return vary;
|
223
|
+
*/
|
224
|
+
|
225
|
+
// Using forward matching keys with an empty string is 100x faster than iternext+get
|
226
|
+
VALUE vary;
|
227
|
+
TCLIST *keys;
|
228
|
+
char *prefix;
|
229
|
+
TCMDB *db = cMDB_getdb(vself);
|
230
|
+
prefix = "";
|
231
|
+
keys = tcmdbfwmkeys2(db, prefix, -1);
|
232
|
+
vary = listtovary(keys);
|
233
|
+
tclistdel(keys);
|
234
|
+
return vary;
|
235
|
+
}
|
236
|
+
|
237
|
+
static VALUE cMDB_addint(VALUE vself, VALUE vkey, int inum){
|
238
|
+
TCMDB *db = cMDB_getdb(vself);
|
239
|
+
vkey = StringValueEx(vkey);
|
240
|
+
|
241
|
+
inum = tcmdbaddint(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), inum);
|
242
|
+
return inum == INT_MIN ? Qnil : INT2NUM(inum);
|
243
|
+
}
|
244
|
+
|
245
|
+
static VALUE cMDB_add_int(int argc, VALUE *argv, VALUE vself){
|
246
|
+
VALUE vkey, vnum;
|
247
|
+
int inum = 1;
|
248
|
+
|
249
|
+
rb_scan_args(argc, argv, "11", &vkey, &vnum);
|
250
|
+
vkey = StringValueEx(vkey);
|
251
|
+
if(NIL_P(vnum)) vnum = INT2NUM(inum);
|
252
|
+
|
253
|
+
return cMDB_addint(vself, vkey, NUM2INT(vnum));
|
254
|
+
}
|
255
|
+
|
256
|
+
static VALUE cMDB_get_int(VALUE vself, VALUE vkey){
|
257
|
+
return cMDB_addint(vself, vkey, 0);
|
258
|
+
}
|
259
|
+
|
260
|
+
static VALUE cMDB_adddouble(VALUE vself, VALUE vkey, double dnum){
|
261
|
+
TCMDB *db = cMDB_getdb(vself);
|
262
|
+
|
263
|
+
vkey = StringValueEx(vkey);
|
264
|
+
dnum = tcmdbadddouble(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), dnum);
|
265
|
+
return isnan(dnum) ? Qnil : rb_float_new(dnum);
|
266
|
+
}
|
267
|
+
|
268
|
+
static VALUE cMDB_add_double(int argc, VALUE *argv, VALUE vself){
|
269
|
+
VALUE vkey, vnum;
|
270
|
+
double dnum = 1.0;
|
271
|
+
|
272
|
+
rb_scan_args(argc, argv, "11", &vkey, &vnum);
|
273
|
+
vkey = StringValueEx(vkey);
|
274
|
+
if(NIL_P(vnum)) vnum = rb_float_new(dnum);
|
275
|
+
|
276
|
+
return cMDB_adddouble(vself, vkey, NUM2DBL(vnum));
|
277
|
+
}
|
278
|
+
|
279
|
+
static VALUE cMDB_get_double(VALUE vself, VALUE vkey){
|
280
|
+
return cMDB_adddouble(vself, vkey, 0.0);
|
281
|
+
}
|
282
|
+
|
283
|
+
static VALUE cMDB_vanish(VALUE vself){
|
284
|
+
TCMDB *db = cMDB_getdb(vself);
|
285
|
+
|
286
|
+
tcmdbvanish(db);
|
287
|
+
return Qtrue;
|
288
|
+
}
|
289
|
+
|
290
|
+
static VALUE cMDB_rnum(VALUE vself){
|
291
|
+
TCMDB *db = cMDB_getdb(vself);
|
292
|
+
|
293
|
+
return LL2NUM(tcmdbrnum(db));
|
294
|
+
}
|
295
|
+
|
296
|
+
static VALUE cMDB_empty(VALUE vself){
|
297
|
+
TCMDB *db = cMDB_getdb(vself);
|
298
|
+
|
299
|
+
return tcmdbrnum(db) < 1 ? Qtrue : Qfalse;
|
300
|
+
}
|
301
|
+
|
302
|
+
static VALUE cMDB_size(VALUE vself){
|
303
|
+
TCMDB *db = cMDB_getdb(vself);
|
304
|
+
|
305
|
+
return LL2NUM(tcmdbmsiz(db));
|
306
|
+
}
|
307
|
+
|
308
|
+
static VALUE cMDB_each_key(VALUE vself){
|
309
|
+
VALUE vrv;
|
310
|
+
char *kxstr;
|
311
|
+
if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
|
312
|
+
TCMDB *db = cMDB_getdb(vself);
|
313
|
+
vrv = Qnil;
|
314
|
+
tcmdbiterinit(db);
|
315
|
+
while((kxstr = tcmdbiternext2(db)) != NULL){
|
316
|
+
vrv = rb_yield_values(1, rb_str_new2(kxstr));
|
317
|
+
}
|
318
|
+
return vrv;
|
319
|
+
}
|
320
|
+
|
321
|
+
void init_utils(){
|
322
|
+
rb_define_private_method(cMDB, "initialize", cMDB_initialize, -1);
|
323
|
+
rb_define_method(cMDB, "bnum", cMDB_bnum, 0);
|
324
|
+
rb_define_method(cMDB, "put", cMDB_put, 2);
|
325
|
+
rb_define_alias(cMDB, "[]=", "put");
|
326
|
+
rb_define_method(cMDB, "putkeep", cMDB_putkeep, 2);
|
327
|
+
rb_define_method(cMDB, "putcat", cMDB_putcat, 2);
|
328
|
+
rb_define_method(cMDB, "get", cMDB_get, 1);
|
329
|
+
rb_define_alias(cMDB, "[]", "get");
|
330
|
+
rb_define_method(cMDB, "vsiz", cMDB_vsiz, 1);
|
331
|
+
rb_define_method(cMDB, "out", cMDB_out, 1);
|
332
|
+
rb_define_method(cMDB, "check", cMDB_check, 1);
|
333
|
+
rb_define_alias(cMDB, "has_key?", "check");
|
334
|
+
rb_define_alias(cMDB, "key?", "check");
|
335
|
+
rb_define_alias(cMDB, "include?", "check");
|
336
|
+
rb_define_alias(cMDB, "member?", "check");
|
337
|
+
rb_define_method(cMDB, "iterinit", cMDB_iterinit, 0);
|
338
|
+
rb_define_method(cMDB, "iternext", cMDB_iternext, 0);
|
339
|
+
rb_define_method(cMDB, "fwmkeys", cMDB_fwmkeys, -1);
|
340
|
+
rb_define_method(cMDB, "keys", cMDB_keys, 0);
|
341
|
+
rb_define_method(cMDB, "add_int", cMDB_add_int, -1);
|
342
|
+
rb_define_alias(cMDB, "addint", "add_int");
|
343
|
+
rb_define_alias(cMDB, "increment", "add_int");
|
344
|
+
rb_define_method(cMDB, "get_int", cMDB_get_int, 1);
|
345
|
+
rb_define_method(cMDB, "add_double", cMDB_add_double, -1);
|
346
|
+
rb_define_alias(cMDB, "adddouble", "add_double");
|
347
|
+
rb_define_method(cMDB, "get_double", cMDB_get_double, 1);
|
348
|
+
rb_define_method(cMDB, "vanish", cMDB_vanish, 0);
|
349
|
+
rb_define_alias(cMDB, "clear", "vanish");
|
350
|
+
rb_define_method(cMDB, "rnum", cMDB_rnum, 0);
|
351
|
+
rb_define_alias(cMDB, "count", "rnum");
|
352
|
+
rb_define_alias(cMDB, "length", "rnum");
|
353
|
+
rb_define_method(cMDB, "size", cMDB_size, 0);
|
354
|
+
rb_define_method(cMDB, "empty?", cMDB_empty, 0);
|
355
|
+
rb_define_method(cMDB, "each_key", cMDB_each_key, 0);
|
356
|
+
|
357
|
+
rb_define_method(cMDB, "fetch", cMDB_fetch, -1);
|
358
|
+
rb_define_method(cMDB, "each", cMDB_each, 0);
|
359
|
+
rb_define_alias(cMDB, "each_pair", "each");
|
360
|
+
rb_define_method(cMDB, "each_value", cMDB_each_value, 0);
|
361
|
+
rb_define_method(cMDB, "values", cMDB_values, 0);
|
362
|
+
}
|
data/ext/tokyo_utils.h
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'tokyo_tyrant'
|
2
|
+
# require 'hash_ring'
|
3
|
+
require 'fast_hash_ring'
|
4
|
+
|
5
|
+
module TokyoTyrant
|
6
|
+
module Balancer
|
7
|
+
class Base
|
8
|
+
def initialize(servers = [], weights = {})
|
9
|
+
servers.collect! do |server|
|
10
|
+
host, port = server.split(':')
|
11
|
+
klass.new(host, port.to_i)
|
12
|
+
end
|
13
|
+
@servers = servers
|
14
|
+
# @ring = HashRing.new(servers, weights)
|
15
|
+
@ring = FastHashRing.new(servers, weights)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ring
|
19
|
+
@ring
|
20
|
+
end
|
21
|
+
|
22
|
+
def servers
|
23
|
+
@servers
|
24
|
+
end
|
25
|
+
|
26
|
+
def db_for_key(key)
|
27
|
+
ring.get_node(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Delegate Methods
|
31
|
+
def get(key)
|
32
|
+
db = db_for_key(key)
|
33
|
+
db.get(key)
|
34
|
+
end
|
35
|
+
alias :[] :get
|
36
|
+
|
37
|
+
def add_int(key, i = 1)
|
38
|
+
db = db_for_key(key)
|
39
|
+
db.add_int(key, i)
|
40
|
+
end
|
41
|
+
alias :addint :add_int
|
42
|
+
alias :increment :add_int
|
43
|
+
|
44
|
+
def get_int(key)
|
45
|
+
db = db_for_key(key)
|
46
|
+
db.get_int(key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_double(key, i = 1.0)
|
50
|
+
db = db_for_key(key)
|
51
|
+
db.add_double(key, i)
|
52
|
+
end
|
53
|
+
alias :adddouble :add_double
|
54
|
+
|
55
|
+
def get_double(key)
|
56
|
+
db = db_for_key(key)
|
57
|
+
db.get_double(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def put(key, value)
|
61
|
+
db = db_for_key(key)
|
62
|
+
db.put(key, value)
|
63
|
+
end
|
64
|
+
alias :[]= :put
|
65
|
+
|
66
|
+
def putkeep(key, value)
|
67
|
+
db = db_for_key(key)
|
68
|
+
db.putkeep(key, value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def putcat(key, value)
|
72
|
+
db = db_for_key(key)
|
73
|
+
db.putcat(key, value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def putshl(key, value, width)
|
77
|
+
db = db_for_key(key)
|
78
|
+
db.putshl(key, value, width)
|
79
|
+
end
|
80
|
+
|
81
|
+
def putnr(key, value)
|
82
|
+
db = db_for_key(key)
|
83
|
+
db.putnr(key, value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def vsiz(key)
|
87
|
+
db = db_for_key(key)
|
88
|
+
db.vsiz(key)
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch(key, &block)
|
92
|
+
db = db_for_key(key)
|
93
|
+
db.fetch(key, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def out(key)
|
97
|
+
db = db_for_key(key)
|
98
|
+
db.out(key)
|
99
|
+
end
|
100
|
+
alias :delete :out
|
101
|
+
|
102
|
+
# Aggregate Methods
|
103
|
+
def close
|
104
|
+
@servers.all?{ |server| server.close }
|
105
|
+
end
|
106
|
+
|
107
|
+
def rnum
|
108
|
+
@servers.collect{ |server| server.rnum }.inject(nil){ |sum,x| sum ? sum+x : x }
|
109
|
+
end
|
110
|
+
alias :count :rnum
|
111
|
+
alias :size :rnum
|
112
|
+
alias :length :rnum
|
113
|
+
|
114
|
+
def empty?
|
115
|
+
@servers.all?{ |server| server.empty? }
|
116
|
+
end
|
117
|
+
|
118
|
+
def vanish
|
119
|
+
@servers.all?{ |server| server.vanish }
|
120
|
+
end
|
121
|
+
alias :clear :vanish
|
122
|
+
|
123
|
+
def sync
|
124
|
+
@servers.each{ |server| server.sync }
|
125
|
+
end
|
126
|
+
|
127
|
+
def optimize(*args)
|
128
|
+
@servers.all?{ |server| server.optimize(*args) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def check(key)
|
132
|
+
@servers.any?{ |server| server.check(key) }
|
133
|
+
end
|
134
|
+
alias :has_key? :check
|
135
|
+
alias :key? :check
|
136
|
+
alias :include? :check
|
137
|
+
alias :member? :check
|
138
|
+
|
139
|
+
def set_index(name, type)
|
140
|
+
@servers.all?{ |server| server.set_index(name, type) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def fwmkeys(prefix, max = -1)
|
144
|
+
@servers.collect{ |server| server.fwmkeys(prefix, max) }.flatten
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_keys_with_prefix(prefix, max = -1)
|
148
|
+
@servers.each{ |server| server.delete_keys_with_prefix(prefix, max) }
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
alias :dfwmkeys :delete_keys_with_prefix
|
152
|
+
|
153
|
+
def keys
|
154
|
+
@servers.collect{ |server| server.keys }.flatten.uniq
|
155
|
+
end
|
156
|
+
|
157
|
+
def values
|
158
|
+
@servers.collect{ |server| server.values }.flatten.uniq
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
module TokyoTyrant
|
165
|
+
module Balancer
|
166
|
+
class DB < Base
|
167
|
+
def klass
|
168
|
+
TokyoTyrant::DB
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module TokyoTyrant
|
175
|
+
module Balancer
|
176
|
+
class Table < Base
|
177
|
+
def klass
|
178
|
+
TokyoTyrant::Table
|
179
|
+
end
|
180
|
+
|
181
|
+
def find(&block)
|
182
|
+
queries = @servers.collect{ |server|
|
183
|
+
server.prepare_query(&block)
|
184
|
+
}
|
185
|
+
TokyoTyrant::Query.parallel_search(*queries)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/spec/spec_base.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.join('spec_base') unless $root
|
3
|
+
|
4
|
+
describe TokyoTyrant::Balancer::DB, "with an open database" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@db = TokyoTyrant::Balancer::DB.new(['127.0.0.1:45000'])
|
8
|
+
@db.clear
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not be nil" do
|
12
|
+
@db.should.not.be.nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should close" do
|
16
|
+
@db.close.should.be.true
|
17
|
+
begin
|
18
|
+
@db.close
|
19
|
+
rescue => e
|
20
|
+
e.message.should == 'close error: invalid operation'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should optimize" do
|
25
|
+
@db.optimize.should.be.true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should save a value" do
|
29
|
+
@db['salad'] = 'bacon bits'
|
30
|
+
@db['salad'].should == 'bacon bits'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return a value" do
|
34
|
+
@db['salad'] = 'bacon bits'
|
35
|
+
@db['salad'].should == 'bacon bits'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should accept binary data" do
|
39
|
+
s = "mango#{0.chr}salsa"
|
40
|
+
@db.put(s, s).should.be.true
|
41
|
+
@db[s].should.equal(s)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should out a value" do
|
45
|
+
k = 'tomato'
|
46
|
+
@db[k] = 'green'
|
47
|
+
@db.out(k).should.be.true
|
48
|
+
@db[k].should.be.nil
|
49
|
+
@db.out(k).should.be.false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should get a value size" do
|
53
|
+
k = 'cereal'
|
54
|
+
v = 'granola'
|
55
|
+
@db[k] = v
|
56
|
+
@db.vsiz(k).should == v.size
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should check a key" do
|
60
|
+
k = 'fruit'
|
61
|
+
@db[k] = 'banana'
|
62
|
+
@db.check(k).should.be.true
|
63
|
+
@db.out(k)
|
64
|
+
@db.check(k).should.be.false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should get forward matching keys" do
|
68
|
+
@db['apples/royalgala'] = '4173'
|
69
|
+
@db['apples/grannysmith'] = '4139'
|
70
|
+
@db['bananas/yellow'] = '4011'
|
71
|
+
@db['oranges/shamouti'] = '3027'
|
72
|
+
@db['grapefruit/deepred'] = '4288'
|
73
|
+
@db.fwmkeys('apples').sort.should == ["apples/grannysmith", "apples/royalgala"]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should delete forward matching keys" do
|
77
|
+
@db['apples/royalgala'] = '4173'
|
78
|
+
@db['apples/grannysmith'] = '4139'
|
79
|
+
@db['bananas/yellow'] = '4011'
|
80
|
+
@db['oranges/shamouti'] = '3027'
|
81
|
+
@db['grapefruit/deepred'] = '4288'
|
82
|
+
@db.delete_keys_with_prefix('apples').should == nil
|
83
|
+
@db.fwmkeys('apples').should.be.empty
|
84
|
+
@db.keys.sort.should == ['bananas/yellow', 'grapefruit/deepred', 'oranges/shamouti']
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should get all keys" do
|
88
|
+
keys = %w[appetizers entree dessert]
|
89
|
+
values = %w[chips chicken\ caeser\ salad ice\ cream]
|
90
|
+
keys.each_with_index do |k,i|
|
91
|
+
@db[k] = values[i]
|
92
|
+
end
|
93
|
+
@db.keys.should == keys
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should get all values" do
|
97
|
+
keys = %w[appetizers entree dessert]
|
98
|
+
values = %w[chips chicken\ caeser\ salad ice\ cream]
|
99
|
+
keys.each_with_index do |k,i|
|
100
|
+
@db[k] = values[i]
|
101
|
+
end
|
102
|
+
@db.values.should == values
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should vanish all records" do
|
106
|
+
@db['chocolate'] = 'dark'
|
107
|
+
@db['tea'] = 'earl grey'
|
108
|
+
@db.empty?.should.be.false
|
109
|
+
@db.vanish.should.be.true
|
110
|
+
@db.empty?.should.be.true
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should count records" do
|
114
|
+
@db['hummus'] = 'chickpeas'
|
115
|
+
@db['falafel'] = 'chickpeas'
|
116
|
+
@db.rnum.should == 2
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should fetch a record" do
|
120
|
+
@db.out('beer')
|
121
|
+
@db.fetch('beer'){ 'heineken' }.should == 'heineken'
|
122
|
+
@db['beer'].should == 'heineken'
|
123
|
+
@db.fetch('beer'){ 'becks' }.should == 'heineken'
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should add serialized integer values" do
|
127
|
+
key = 'counter'
|
128
|
+
@db.out(key)
|
129
|
+
@db.add_int(key, 1).should == 1
|
130
|
+
@db.add_int(key, 1).should == 2
|
131
|
+
@db.get_int(key).should == 2
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should increment integer values" do
|
135
|
+
key = 'counter'
|
136
|
+
@db.out(key)
|
137
|
+
@db.increment(key).should == 1
|
138
|
+
@db.increment(key, 2).should == 3
|
139
|
+
@db.get_int(key).should == 3
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should add serialized double values" do
|
143
|
+
key = 'counter'
|
144
|
+
@db.out(key)
|
145
|
+
@db.add_double(key, 1.0).should.be.close?(1.0, 0.005)
|
146
|
+
@db.add_double(key, 1.0).should.be.close?(2.0, 0.005)
|
147
|
+
@db.get_double(key).should.be.close?(2.0, 0.005)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should serialize objects that respond to to_tokyo_tyrant" do
|
151
|
+
class Thing
|
152
|
+
def to_tokyo_tyrant
|
153
|
+
"success"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@db["to_tokyo_tyrant"] = Thing.new
|
158
|
+
@db["to_tokyo_tyrant"].should == "success"
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.join('spec_base') unless $root
|
3
|
+
|
4
|
+
describe TokyoTyrant::Balancer::Table, "with an open database" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@db = TokyoTyrant::Balancer::Table.new(['127.0.0.1:45001'])
|
8
|
+
@db.clear
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not be nil" do
|
12
|
+
@db.should.not.be.nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should close" do
|
16
|
+
@db.close.should.be.true
|
17
|
+
begin
|
18
|
+
@db.close
|
19
|
+
rescue => e
|
20
|
+
e.message.should == 'close error: invalid operation'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should optimize" do
|
25
|
+
@db.optimize.should.be.true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should save a value" do
|
29
|
+
value = { 'lettuce' => 'Red Leaf', 'dressing' => 'ranch', 'extra' => 'bacon bits' }
|
30
|
+
@db['salad'] = value
|
31
|
+
@db['salad'].should == value
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return a value" do
|
35
|
+
value = { 'lettuce' => 'Red Leaf', 'dressing' => 'ranch', 'extra' => 'bacon bits' }
|
36
|
+
@db['salad'] = value
|
37
|
+
@db['salad'].should == value
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should accept binary data" do
|
41
|
+
s = "mango#{0.chr}salsa"
|
42
|
+
h = { s => s }
|
43
|
+
@db.put(s, h).should.be.true
|
44
|
+
@db[s].should.equal(h)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should out a value" do
|
48
|
+
k = 'tomato'
|
49
|
+
@db[k] = { 'color' => 'red', 'variety' => 'beefy' }
|
50
|
+
@db.out(k).should.be.true
|
51
|
+
@db[k].should.be.nil
|
52
|
+
@db.out(k).should.be.false
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should check a key" do
|
56
|
+
k = 'fruit'
|
57
|
+
@db[k] = { 'name' => 'banana', 'code' => '4011' }
|
58
|
+
@db.check(k).should.be.true
|
59
|
+
@db.out(k)
|
60
|
+
@db.check(k).should.be.false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should get forward matching keys" do
|
64
|
+
@db['apples/royalgala'] = { 'code' => '4173', 'color' => 'red-yellow' }
|
65
|
+
@db['apples/grannysmith'] = { 'code' => '4139', 'color' => 'green' }
|
66
|
+
@db['bananas/yellow'] = { 'code' => '4011', 'color' => 'yellow' }
|
67
|
+
@db['oranges/shamouti'] = { 'code' => '3027', 'color' => 'orange' }
|
68
|
+
@db['grapefruit/deepred'] = { 'code' => '4288', 'color' => 'yellow/pink' }
|
69
|
+
@db.fwmkeys('apples').sort.should == ["apples/grannysmith", "apples/royalgala"]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should delete forward matching keys" do
|
73
|
+
@db['apples/royalgala'] = { 'code' => '4173', 'color' => 'red-yellow' }
|
74
|
+
@db['apples/grannysmith'] = { 'code' => '4139', 'color' => 'green' }
|
75
|
+
@db['bananas/yellow'] = { 'code' => '4011', 'color' => 'yellow' }
|
76
|
+
@db['oranges/shamouti'] = { 'code' => '3027', 'color' => 'orange' }
|
77
|
+
@db['grapefruit/deepred'] = { 'code' => '4288', 'color' => 'yellow/pink' }
|
78
|
+
@db.delete_keys_with_prefix('apples').should == nil
|
79
|
+
@db.fwmkeys('apples').should.be.empty
|
80
|
+
@db.keys.sort.should == ['bananas/yellow', 'grapefruit/deepred', 'oranges/shamouti']
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should get all keys" do
|
84
|
+
keys = %w[appetizers entree dessert]
|
85
|
+
values = [{ 'cheap' => 'chips', 'expensive' => 'sample everything platter' },
|
86
|
+
{ 'cheap' => 'hoagie', 'expensive' => 'steak' },
|
87
|
+
{ 'cheap' => 'water ice', 'expensive' => 'cheesecake' }]
|
88
|
+
|
89
|
+
keys.each_with_index do |k,i|
|
90
|
+
@db[k] = values[i]
|
91
|
+
end
|
92
|
+
@db.keys.should == keys
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should get all values" do
|
96
|
+
keys = %w[appetizers entree dessert]
|
97
|
+
values = [{ 'cheap' => 'chips', 'expensive' => 'sample everything platter' },
|
98
|
+
{ 'cheap' => 'hoagie', 'expensive' => 'steak' },
|
99
|
+
{ 'cheap' => 'water ice', 'expensive' => 'cheesecake' }]
|
100
|
+
|
101
|
+
keys.each_with_index do |k,i|
|
102
|
+
@db[k] = values[i]
|
103
|
+
end
|
104
|
+
@db.values.should == values
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should vanish all records" do
|
108
|
+
@db['chocolate'] = { 'type' => 'dark' }
|
109
|
+
@db['tea'] = { 'type' => 'earl grey' }
|
110
|
+
@db.empty?.should.be.false
|
111
|
+
@db.vanish.should.be.true
|
112
|
+
@db.empty?.should.be.true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should count records" do
|
116
|
+
@db['hummus'] = { 'ingredients' => 'chickpeas,garlic' }
|
117
|
+
@db['falafel'] = { 'ingredients' => 'chickpeas,herbs' }
|
118
|
+
@db.rnum.should == 2
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should fetch a record" do
|
122
|
+
@db.out('beer')
|
123
|
+
@db.fetch('beer'){{ 'import' => 'heineken' }}.should == { 'import' => 'heineken' }
|
124
|
+
@db['beer'].should == { 'import' => 'heineken' }
|
125
|
+
@db.fetch('beer'){{ 'import' => 'becks' }}.should == { 'import' => 'heineken' }
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should add serialized integer values" do
|
129
|
+
key = 'counter'
|
130
|
+
@db.out(key)
|
131
|
+
@db[key] = { 'title' => 'Bean Counter' }
|
132
|
+
@db.add_int(key, 1).should == 1
|
133
|
+
@db.add_int(key, 1).should == 2
|
134
|
+
@db.get_int(key).should == 2
|
135
|
+
@db[key].should == { 'title' => 'Bean Counter', '_num' => "2" }
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should increment integer values" do
|
139
|
+
key = 'counter'
|
140
|
+
@db.out(key)
|
141
|
+
@db[key] = { 'title' => 'Bean Counter' }
|
142
|
+
@db.increment(key).should == 1
|
143
|
+
@db.increment(key, 2).should == 3
|
144
|
+
@db.get_int(key).should == 3
|
145
|
+
@db[key].should == { 'title' => 'Bean Counter', '_num' => "3" }
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should add serialized double values" do
|
149
|
+
key = 'counter'
|
150
|
+
@db.out(key)
|
151
|
+
@db[key] = { 'title' => 'Bean Counter' }
|
152
|
+
@db.add_double(key, 1.0).should.be.close?(1.0, 0.005)
|
153
|
+
@db.add_double(key, 1.0).should.be.close?(2.0, 0.005)
|
154
|
+
@db.get_double(key).should.be.close?(2.0, 0.005)
|
155
|
+
@db[key].should == { 'title' => 'Bean Counter', '_num' => "2" }
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should set an index" do
|
159
|
+
key = 'counter'
|
160
|
+
50.times do |i|
|
161
|
+
@db["key#{i}"] = { 'title' => %w{rice beans corn}.sort_by{rand}.first,
|
162
|
+
'description' => 'a whole protein' }
|
163
|
+
end
|
164
|
+
@db.set_index('title', :lexical).should.be.true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should serialize objects that respond to to_tokyo_tyrant" do
|
168
|
+
class Thing
|
169
|
+
def to_tokyo_tyrant
|
170
|
+
"success"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
@db["to_tokyo_tyrant"] = { "thing" => Thing.new }
|
175
|
+
@db["to_tokyo_tyrant"].should == { "thing" => "success" }
|
176
|
+
end
|
177
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-tokyotyrant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: "0.4"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flinn
|
@@ -9,10 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-29 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: fast_hash_ring
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.1.1
|
24
|
+
version:
|
16
25
|
description:
|
17
26
|
email: flinn@actsasflinn.com
|
18
27
|
executables: []
|
@@ -35,15 +44,21 @@ files:
|
|
35
44
|
- ext/tokyo_tyrant_query.h
|
36
45
|
- ext/tokyo_tyrant_table.c
|
37
46
|
- ext/tokyo_tyrant_table.h
|
47
|
+
- ext/tokyo_utils.c
|
48
|
+
- ext/tokyo_utils.h
|
49
|
+
- lib/tokyo_tyrant/balancer.rb
|
38
50
|
- spec/ext.lua
|
39
51
|
- spec/plu_db.rb
|
40
52
|
- spec/spec.rb
|
41
53
|
- spec/spec_base.rb
|
42
54
|
- spec/start_tyrants.sh
|
43
55
|
- spec/stop_tyrants.sh
|
56
|
+
- spec/tokyo_tyrant_balancer_db_spec.rb
|
57
|
+
- spec/tokyo_tyrant_balancer_table_spec.rb
|
44
58
|
- spec/tokyo_tyrant_query_spec.rb
|
45
59
|
- spec/tokyo_tyrant_spec.rb
|
46
60
|
- spec/tokyo_tyrant_table_spec.rb
|
61
|
+
- benchmarks/balancer.rb
|
47
62
|
- benchmarks/bulk_db.rb
|
48
63
|
- benchmarks/bulk_table.rb
|
49
64
|
- benchmarks/db.rb
|