ruby-tokyotyrant 0.3.1 → 0.4
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.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
|