ruby-tokyotyrant 0.4 → 0.5

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