ruby-tokyotyrant 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,12 +6,15 @@
6
6
  #include <math.h>
7
7
  #include <time.h>
8
8
  #include <locale.h>
9
+ #include <stdio.h>
10
+ #include <unistd.h>
9
11
  #include <tcrdb.h>
10
12
  #include <tokyo_tyrant_module.h>
11
13
  #include <tokyo_tyrant_db.h>
14
+ #include <tokyo_tyrant_bdb.h>
12
15
  #include <tokyo_tyrant_table.h>
13
16
  #include <tokyo_tyrant_query.h>
14
- #include <tokyo_utils.h>
17
+ #include <tokyo_tyrant_consistent_hash.h>
15
18
 
16
19
  #define RDBVNDATA "@rdb"
17
20
  #define RDBQRYVNDATA "@rdbquery"
@@ -20,7 +23,6 @@
20
23
  #define TTPUTKEEP 1
21
24
  #define TTPUTCAT 2
22
25
  #define TTPUTNR 3
23
- #define MDBVNDATA "@mdb"
24
26
 
25
27
  #if !defined(RSTRING_PTR)
26
28
  #define RSTRING_PTR(TC_s) (RSTRING(TC_s)->ptr)
@@ -33,11 +35,22 @@
33
35
  #endif
34
36
 
35
37
  extern VALUE mTokyoTyrant;
38
+
36
39
  extern VALUE eTokyoTyrantError;
40
+ extern VALUE eTokyoTyrantErrorInvalid;
41
+ extern VALUE eTokyoTyrantErrorNoHost;
42
+ extern VALUE eTokyoTyrantErrorRefused;
43
+ extern VALUE eTokyoTyrantErrorSend;
44
+ extern VALUE eTokyoTyrantErrorReceive;
45
+ extern VALUE eTokyoTyrantErrorKeep;
46
+ extern VALUE eTokyoTyrantErrorNoRecord;
47
+ extern VALUE eTokyoTyrantErrorMisc;
48
+
37
49
  extern VALUE cDB;
50
+ extern VALUE cBDB;
38
51
  extern VALUE cTable;
39
52
  extern VALUE cQuery;
40
- extern VALUE cMDB;
53
+ extern VALUE cConstistentHash;
41
54
 
42
55
  extern VALUE StringRaw(const char *buf, int bsiz);
43
56
  extern VALUE StringValueEx(VALUE vobj);
@@ -47,5 +60,7 @@ extern TCMAP *vhashtomap(VALUE vhash);
47
60
  extern VALUE maptovhash(TCMAP *map);
48
61
  extern TCMAP *varytomap(VALUE vhash);
49
62
  extern TCLIST *vhashtolist(VALUE vhash);
63
+ extern TCLIST *vhashtoputlist(VALUE vhash);
64
+ extern VALUE listtovhash(TCLIST *list);
50
65
 
51
66
  #endif
@@ -1,18 +1,15 @@
1
1
  require 'tokyo_tyrant'
2
- # require 'hash_ring'
3
- require 'fast_hash_ring'
4
2
 
5
3
  module TokyoTyrant
6
4
  module Balancer
7
5
  class Base
8
- def initialize(servers = [], weights = {})
6
+ def initialize(servers = [])
9
7
  servers.collect! do |server|
10
8
  host, port = server.split(':')
11
9
  klass.new(host, port.to_i)
12
10
  end
13
11
  @servers = servers
14
- # @ring = HashRing.new(servers, weights)
15
- @ring = FastHashRing.new(servers, weights)
12
+ @ring = TokyoTyrant::ConstistentHash.new(servers)
16
13
  end
17
14
 
18
15
  def ring
@@ -24,7 +21,7 @@ module TokyoTyrant
24
21
  end
25
22
 
26
23
  def db_for_key(key)
27
- ring.get_node(key)
24
+ ring.db_for_key(key)
28
25
  end
29
26
 
30
27
  # Delegate Methods
@@ -0,0 +1,106 @@
1
+ require 'tokyo_tyrant/balancer'
2
+
3
+ module ActiveSupport
4
+ module Cache
5
+ # A cache store implementation which stores data in Tokyo Tyrant:
6
+ # http://1978th.net/tokyotyrant/
7
+ #
8
+ # Special features:
9
+ # - Clustering and load balancing. One can specify multiple servers,
10
+ # and TokyoTyrantStore will load balance between all available servers.
11
+ # - Per-request in memory cache for all communication with the Tokyo Tyrant server(s).
12
+ class TokyoTyrantStore < Store
13
+ def self.build_servers(*addresses)
14
+ addresses = addresses.flatten
15
+ options = addresses.extract_options!
16
+ addresses = ["127.0.0.1:1978"] if addresses.empty?
17
+ TokyoTyrant::Balancer::DB.new(addresses, options)
18
+ end
19
+
20
+ # Creates a new TokyoTyrantStore object, with the given server
21
+ # addresses. Each address is either a host name, or a host-with-port string
22
+ # in the form of "host_name:port". For example:
23
+ #
24
+ # ActiveSupport::Cache::TokyoTyrantStore.new("localhost", "server-downstairs.localnetwork:8229")
25
+ #
26
+ # If no addresses are specified, then TokyoTyrantStore will connect to
27
+ # localhost port 1978 (the default tokyo tyrant port).
28
+ def initialize(*addresses)
29
+ if addresses.first.respond_to?(:get)
30
+ @data = addresses.first
31
+ else
32
+ @data = self.class.build_servers(*addresses)
33
+ end
34
+
35
+ extend Strategy::LocalCache
36
+ end
37
+
38
+ def read(key, options = nil) # :nodoc:
39
+ super
40
+ @data.get(key)
41
+ rescue TokyoTyrantError => e
42
+ logger.error("TokyoTyrantError (#{e}): #{e.message}")
43
+ nil
44
+ end
45
+
46
+ # Writes a value to the cache.
47
+ #
48
+ # Possible options:
49
+ # - +:unless_exist+ - set to true if you don't want to update the cache
50
+ # if the key is already set.
51
+ # the cache. See ActiveSupport::Cache::Store#write for an example.
52
+ def write(key, value, options = nil)
53
+ super
54
+ method = options && options[:unless_exist] ? :putkeep : :put
55
+ @data.send(method, key, value)
56
+ rescue TokyoTyrantError => e
57
+ logger.error("TokyoTyrantError (#{e}): #{e.message}")
58
+ false
59
+ end
60
+
61
+ def delete(key, options = nil) # :nodoc:
62
+ super
63
+ @data.delete(key)
64
+ rescue TokyoTyrantError => e
65
+ logger.error("TokyoTyrantError (#{e}): #{e.message}")
66
+ false
67
+ end
68
+
69
+ def exist?(key, options = nil) # :nodoc:
70
+ @data.has_key?(key)
71
+ end
72
+
73
+ def increment(key, amount = 1) # :nodoc:
74
+ log("incrementing", key, amount)
75
+ @data.add_int(key, amount)
76
+ rescue TokyoTyrantError
77
+ nil
78
+ end
79
+
80
+ def decrement(key, amount = 1) # :nodoc:
81
+ log("decrement", key, amount)
82
+ @data.add_int(key, amount * -1)
83
+ rescue TokyoTyrantError
84
+ nil
85
+ end
86
+
87
+ def integer(key) # :nodoc:
88
+ log("integer", key)
89
+ @data.get_int(integer)
90
+ rescue TokyoTyrantError
91
+ nil
92
+ end
93
+
94
+ def delete_matched(matcher, options = nil) # :nodoc:
95
+ super
96
+ @data.delete_keys_with_prefix(matcher)
97
+ rescue TokyoTyrantError
98
+ nil
99
+ end
100
+
101
+ def clear
102
+ @data.clear
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,138 @@
1
+ #include <tokyo_tyrant_bdb.h>
2
+
3
+ static VALUE cBDB_put_method(VALUE vself, VALUE vkey, VALUE vval, char *command, bool bang){
4
+ VALUE vres;
5
+ TCLIST *list, *result;
6
+ TCRDB *db = mTokyoTyrant_getdb(vself);
7
+
8
+ vkey = StringValueEx(vkey);
9
+ vval = StringValueEx(vval);
10
+
11
+ list = tclistnew2(2);
12
+ tclistpush(list, RSTRING_PTR(vkey), RSTRING_LEN(vkey));
13
+ tclistpush(list, RSTRING_PTR(vval), RSTRING_LEN(vval));
14
+ if ((result = tcrdbmisc(db, command, 0, list)) != NULL){
15
+ if (tclistnum(result) == 0){
16
+ vres = Qtrue;
17
+ } else {
18
+ vres = listtovary(result);
19
+ }
20
+ } else {
21
+ if (bang) mTokyoTyrant_exception(vself, NULL);
22
+ vres = Qfalse;
23
+ }
24
+ tclistdel(list);
25
+ return vres;
26
+ }
27
+
28
+ static VALUE cBDB_putlist(VALUE vself, VALUE vhash){
29
+ VALUE vary;
30
+ TCLIST *list, *result;
31
+ TCRDB *db = mTokyoTyrant_getdb(vself);
32
+ Check_Type(vhash, T_HASH);
33
+
34
+ list = vhashtoputlist(vhash);
35
+ if ((result = tcrdbmisc(db, "putlist", 0, list)) != NULL){
36
+ vary = listtovary(result);
37
+ tclistdel(result);
38
+ } else {
39
+ vary = rb_ary_new();
40
+ }
41
+ tclistdel(list);
42
+
43
+ return vary;
44
+ }
45
+
46
+ static VALUE cBDB_getlist(int argc, VALUE *argv, VALUE vself){
47
+ VALUE vkeys, vvalue, vhash;
48
+ TCLIST *list, *result;
49
+ TCRDB *db = mTokyoTyrant_getdb(vself);
50
+ rb_scan_args(argc, argv, "*", &vkeys);
51
+
52
+ // I really hope there is a better way to do this
53
+ if (RARRAY_LEN(vkeys) == 1) {
54
+ vvalue = rb_ary_entry(vkeys, 0);
55
+ switch (TYPE(vvalue)){
56
+ case T_STRING:
57
+ case T_FIXNUM:
58
+ break;
59
+ case T_ARRAY:
60
+ vkeys = vvalue;
61
+ break;
62
+ case T_OBJECT:
63
+ vkeys = rb_convert_type(vvalue, T_ARRAY, "Array", "to_a");
64
+ break;
65
+ }
66
+ }
67
+ Check_Type(vkeys, T_ARRAY);
68
+
69
+ list = varytolist(vkeys);
70
+ result = tcrdbmisc(db, "getlist", RDBMONOULOG, list);
71
+ tclistdel(list);
72
+ vhash = listtovhash(result);
73
+ tclistdel(result);
74
+ return vhash;
75
+ }
76
+
77
+ static VALUE cBDB_each(VALUE vself){
78
+ VALUE vrv;
79
+ const char *kbuf, *vbuf;
80
+ int ksiz, vsiz;
81
+ TCLIST *result;
82
+ TCRDB *db = mTokyoTyrant_getdb(vself);
83
+ vrv = Qnil;
84
+
85
+ if(rb_block_given_p() != Qtrue) rb_raise(rb_eArgError, "no block given");
86
+
87
+ tcrdbiterinit(db);
88
+ tcrdbmisc(db, "iterinit", RDBMONOULOG, tclistnew());
89
+ while((result = tcrdbmisc(db, "iternext", RDBMONOULOG, tclistnew())) != NULL){
90
+ if (tclistnum(result) == 2) {
91
+ kbuf = tclistval(result, 0, &ksiz);
92
+ vbuf = tclistval(result, 1, &vsiz);
93
+ vrv = rb_yield_values(2, rb_str_new(kbuf, ksiz), rb_str_new(vbuf, vsiz));
94
+ }
95
+ tclistdel(result);
96
+ }
97
+ return vrv;
98
+ }
99
+
100
+ static VALUE cBDB_values(VALUE vself){
101
+ VALUE vary;
102
+ const char *vbuf;
103
+ int vsiz;
104
+ TCLIST *result;
105
+ TCRDB *db = mTokyoTyrant_getdb(vself);
106
+
107
+ vary = rb_ary_new();
108
+ tcrdbiterinit(db);
109
+ tcrdbmisc(db, "iterinit", RDBMONOULOG, tclistnew());
110
+ while((result = tcrdbmisc(db, "iternext", RDBMONOULOG, tclistnew())) != NULL){
111
+ if (tclistnum(result) == 2){
112
+ vbuf = tclistval(result, 1, &vsiz);
113
+ vary = rb_ary_push(vary, rb_str_new(vbuf, vsiz));
114
+ }
115
+ tclistdel(result);
116
+ }
117
+
118
+ return vary;
119
+ }
120
+
121
+ static VALUE cBDB_putdup(VALUE vself, VALUE vkey, VALUE vval){
122
+ return cBDB_put_method(vself, vkey, vval, "putdup", false);
123
+ }
124
+
125
+ static VALUE cBDB_putdup_bang(VALUE vself, VALUE vkey, VALUE vval){
126
+ return cBDB_put_method(vself, vkey, vval, "putdup", true);
127
+ }
128
+
129
+ void init_bdb(){
130
+ rb_define_method(cBDB, "putlist", cBDB_putlist, 1);
131
+ rb_define_alias(cBDB, "mput", "putlist");
132
+ rb_define_method(cBDB, "getlist", cBDB_getlist, -1);
133
+ rb_define_alias(cBDB, "mget", "putlist");
134
+ rb_define_method(cBDB, "each", cBDB_each, 0);
135
+ rb_define_method(cBDB, "values", cBDB_values, 0);
136
+ rb_define_method(cBDB, "putdup", cBDB_putdup, 2);
137
+ rb_define_method(cBDB, "putdup!", cBDB_putdup_bang, 2);
138
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef RUBY_TOKYOTYRANT_BDB
2
+ #define RUBY_TOKYOTYRANT_BDB
3
+
4
+ #include <tokyo_tyrant.h>
5
+
6
+ void init_bdb();
7
+
8
+ #endif
@@ -0,0 +1,34 @@
1
+ #include <tokyo_tyrant_consistent_hash.h>
2
+
3
+ static void cConstistentHash_free(TCCHIDX *idx){
4
+ tcchidxdel(idx);
5
+ }
6
+
7
+ static VALUE cConstistentHash_initialize(VALUE self, VALUE dbs){
8
+ TCCHIDX *idx;
9
+
10
+ Check_Type(dbs, T_ARRAY);
11
+ idx = tcchidxnew(RARRAY_LEN(dbs));
12
+
13
+ rb_iv_set(self, "@dbs", dbs);
14
+ rb_iv_set(self, "@idx", Data_Wrap_Struct(rb_cObject, 0, cConstistentHash_free, idx));
15
+
16
+ return Qtrue;
17
+ }
18
+
19
+ static VALUE cConstistentHash_db_for_key(VALUE self, VALUE key){
20
+ VALUE dbs;
21
+ TCCHIDX *idx;
22
+ int hash;
23
+
24
+ Data_Get_Struct(rb_iv_get(self, "@idx"), TCCHIDX, idx);
25
+ hash = tcchidxhash(idx, RSTRING_PTR(key), RSTRING_LEN(key));
26
+ dbs = rb_iv_get(self, "@dbs");
27
+
28
+ return rb_ary_entry(dbs, hash);
29
+ }
30
+
31
+ void init_consistent_hash(){
32
+ rb_define_private_method(cConstistentHash, "initialize", cConstistentHash_initialize, 1);
33
+ rb_define_method(cConstistentHash, "db_for_key", cConstistentHash_db_for_key, 1);
34
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef RUBY_TOKYOTYRANT_CONSISTENT_HASH
2
+ #define RUBY_TOKYOTYRANT_CONSISTENT_HASH
3
+
4
+ #include <tokyo_tyrant.h>
5
+
6
+ void init_consistent_hash();
7
+
8
+ #endif
@@ -25,7 +25,7 @@ static VALUE cDB_put_method(VALUE vself, VALUE vkey, VALUE vstr, int method){
25
25
  break;
26
26
  }
27
27
 
28
- if(!res) mTokyoTyrant_exception(vself);
28
+ if(!res) mTokyoTyrant_exception(vself, NULL);
29
29
 
30
30
  return Qtrue;
31
31
  }
@@ -68,7 +68,7 @@ static VALUE cDB_putshl(VALUE vself, VALUE vkey, VALUE vstr, VALUE vwidth){
68
68
 
69
69
  res = tcrdbputshl(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), RSTRING_PTR(vstr), RSTRING_LEN(vstr), FIXNUM_P(vwidth));
70
70
 
71
- if(!res) mTokyoTyrant_exception(vself);
71
+ if(!res) mTokyoTyrant_exception(vself, NULL);
72
72
 
73
73
  return Qtrue;
74
74
  }
@@ -83,7 +83,7 @@ static VALUE cDB_get(VALUE vself, VALUE vkey){
83
83
  vkey = StringValueEx(vkey);
84
84
  if(!(buf = tcrdbget(db, RSTRING_PTR(vkey), RSTRING_LEN(vkey), &bsiz))){
85
85
  if ((ecode = tcrdbecode(db))) {
86
- if (ecode != TTENOREC) mTokyoTyrant_exception(vself);
86
+ if (ecode != TTENOREC) mTokyoTyrant_exception(vself, NULL);
87
87
  }
88
88
  return Qnil;
89
89
  } else {
@@ -1,4 +1,4 @@
1
- #include <tokyo_tyrant_module.h>
1
+ #include <tokyo_tyrant_db.h>
2
2
 
3
3
  extern TCRDB *mTokyoTyrant_getdb(VALUE vself){
4
4
  TCRDB *db;
@@ -6,12 +6,44 @@ extern TCRDB *mTokyoTyrant_getdb(VALUE vself){
6
6
  return db;
7
7
  }
8
8
 
9
- extern void mTokyoTyrant_exception(VALUE vself){
9
+ extern void mTokyoTyrant_exception(VALUE vself, void *message){
10
+ VALUE exception;
10
11
  int ecode;
11
12
  TCRDB *db = mTokyoTyrant_getdb(vself);
13
+ if (message == NULL) message = "%s";
12
14
 
13
15
  ecode = tcrdbecode(db);
14
- rb_raise(eTokyoTyrantError, tcrdberrmsg(ecode));
16
+ // Is there a better way to do this?
17
+ switch(ecode){
18
+ case TTEINVALID:
19
+ exception = eTokyoTyrantErrorInvalid;
20
+ break;
21
+ case TTENOHOST:
22
+ exception = eTokyoTyrantErrorNoHost;
23
+ break;
24
+ case TTEREFUSED:
25
+ exception = eTokyoTyrantErrorRefused;
26
+ break;
27
+ case TTESEND:
28
+ exception = eTokyoTyrantErrorSend;
29
+ break;
30
+ case TTERECV:
31
+ exception = eTokyoTyrantErrorReceive;
32
+ break;
33
+ case TTEKEEP:
34
+ exception = eTokyoTyrantErrorKeep;
35
+ break;
36
+ case TTENOREC:
37
+ exception = eTokyoTyrantErrorNoRecord;
38
+ break;
39
+ case TTEMISC:
40
+ exception = eTokyoTyrantErrorMisc;
41
+ break;
42
+ default:
43
+ exception = eTokyoTyrantError;
44
+ break;
45
+ }
46
+ rb_raise(exception, message, tcrdberrmsg(ecode));
15
47
  }
16
48
 
17
49
  static void mTokyoTyrant_free(TCRDB *db){
@@ -23,34 +55,36 @@ static VALUE mTokyoTyrant_server(VALUE vself){
23
55
  }
24
56
 
25
57
  static VALUE mTokyoTyrant_close(VALUE vself){
26
- int ecode;
27
58
  TCRDB *db = mTokyoTyrant_getdb(vself);
28
59
 
29
- if(!tcrdbclose(db)){
30
- ecode = tcrdbecode(db);
31
- rb_raise(eTokyoTyrantError, "close error: %s", tcrdberrmsg(ecode));
32
- }
60
+ if(!tcrdbclose(db)) mTokyoTyrant_exception(vself, "close error: %s");
33
61
  return Qtrue;
34
62
  }
35
63
 
36
64
  static VALUE mTokyoTyrant_connect(VALUE vself){
37
- VALUE host, port, timeout, retry, server;
38
- int ecode;
65
+ VALUE vhost, vport, vtimeout, vretry, vserver;
66
+ int port;
67
+ char *host;
39
68
  TCRDB *db = mTokyoTyrant_getdb(vself);
40
69
 
41
- host = rb_iv_get(vself, "@host");
42
- port = rb_iv_get(vself, "@port");
43
- timeout = rb_iv_get(vself, "@timeout");
44
- retry = rb_iv_get(vself, "@retry");
70
+ vhost = rb_iv_get(vself, "@host");
71
+ vport = rb_iv_get(vself, "@port");
72
+ vtimeout = rb_iv_get(vself, "@timeout");
73
+ vretry = rb_iv_get(vself, "@retry");
74
+ host = RSTRING_PTR(vhost);
75
+ port = FIX2INT(vport);
76
+
77
+ if(port == 0 && access(host, R_OK) < 0 && access(host, W_OK) < 0){
78
+ rb_raise(rb_eArgError, "Can't open unix socket: %s", host);
79
+ }
45
80
 
46
- if((!tcrdbtune(db, NUM2DBL(timeout), retry == Qtrue ? RDBTRECON : 0)) ||
47
- (!tcrdbopen(db, RSTRING_PTR(host), FIX2INT(port)))){
48
- ecode = tcrdbecode(db);
49
- rb_raise(eTokyoTyrantError, "open error: %s", tcrdberrmsg(ecode));
81
+ if((!tcrdbtune(db, NUM2DBL(vtimeout), vretry == Qtrue ? RDBTRECON : 0)) ||
82
+ (!tcrdbopen(db, host, port))){
83
+ mTokyoTyrant_exception(vself, "open error: %s");
50
84
  }
51
85
 
52
- server = rb_str_new2(tcrdbexpr(db));
53
- rb_iv_set(vself, "@server", server);
86
+ vserver = rb_str_new2(tcrdbexpr(db));
87
+ rb_iv_set(vself, "@server", vserver);
54
88
 
55
89
  return Qtrue;
56
90
  }
@@ -338,9 +372,18 @@ static VALUE mTokyoTyrant_size(VALUE vself){
338
372
  }
339
373
 
340
374
  static VALUE mTokyoTyrant_stat(VALUE vself){
375
+ VALUE vhash;
376
+ char *stats;
377
+ TCMAP *map;
341
378
  TCRDB *db = mTokyoTyrant_getdb(vself);
379
+ vhash = rb_hash_new();
342
380
 
343
- return rb_str_new2(tcrdbstat(db));
381
+ if ((stats = tcrdbstat(db)) != NULL){
382
+ map = tcstrsplit3(stats, "\t\n");
383
+ vhash = maptovhash(map);
384
+ tcmapdel(map);
385
+ }
386
+ return vhash;
344
387
  }
345
388
 
346
389
  static VALUE mTokyoTyrant_misc(int argc, VALUE *argv, VALUE vself){
@@ -348,13 +391,21 @@ static VALUE mTokyoTyrant_misc(int argc, VALUE *argv, VALUE vself){
348
391
  TCLIST *list, *args;
349
392
  TCRDB *db = mTokyoTyrant_getdb(vself);
350
393
  rb_scan_args(argc, argv, "13", &vname, &vopts, &vargs);
394
+ if (vopts == Qnil) vopts = INT2NUM(0);
395
+ if (vargs == Qnil) vargs = rb_ary_new();
351
396
 
397
+ Check_Type(vargs, T_ARRAY);
352
398
  args = varytolist(vargs);
353
399
  vname = StringValueEx(vname);
354
400
 
355
- list = tcrdbmisc(db, RSTRING_PTR(vname), NUM2INT(vopts), args);
356
- vary = listtovary(list);
357
- tclistdel(list);
401
+ if ((list = tcrdbmisc(db, RSTRING_PTR(vname), NUM2INT(vopts), args)) != NULL){
402
+ vary = listtovary(list);
403
+ tclistdel(list);
404
+ } else {
405
+ vary = rb_ary_new();
406
+ }
407
+ tclistdel(args);
408
+
358
409
  return vary;
359
410
  }
360
411