ls4 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +661 -0
  3. data/ChangeLog +9 -0
  4. data/NOTICE +8 -0
  5. data/README.rdoc +61 -0
  6. data/bin/ls4-cs +3 -0
  7. data/bin/ls4-ds +3 -0
  8. data/bin/ls4-gw +3 -0
  9. data/bin/ls4-standalone +3 -0
  10. data/bin/ls4cmd +3 -0
  11. data/bin/ls4ctl +3 -0
  12. data/bin/ls4rpc +3 -0
  13. data/bin/ls4stat +3 -0
  14. data/bin/ls4top +3 -0
  15. data/lib/ls4/command/cmd.rb +241 -0
  16. data/lib/ls4/command/cs.rb +190 -0
  17. data/lib/ls4/command/ctl.rb +278 -0
  18. data/lib/ls4/command/ds.rb +335 -0
  19. data/lib/ls4/command/gw.rb +256 -0
  20. data/lib/ls4/command/rpc.rb +172 -0
  21. data/lib/ls4/command/standalone.rb +318 -0
  22. data/lib/ls4/command/stat.rb +244 -0
  23. data/lib/ls4/command/top.rb +291 -0
  24. data/lib/ls4/default.rb +26 -0
  25. data/lib/ls4/lib/cclog.rb +220 -0
  26. data/lib/ls4/lib/ebus.rb +553 -0
  27. data/lib/ls4/lib/vbcode.rb +228 -0
  28. data/lib/ls4/logic/fault_detector.rb +212 -0
  29. data/lib/ls4/logic/membership.rb +253 -0
  30. data/lib/ls4/logic/node.rb +66 -0
  31. data/lib/ls4/logic/okey.rb +45 -0
  32. data/lib/ls4/logic/tsv_data.rb +81 -0
  33. data/lib/ls4/logic/weight.rb +166 -0
  34. data/lib/ls4/service/balance.rb +62 -0
  35. data/lib/ls4/service/base.rb +29 -0
  36. data/lib/ls4/service/bus.rb +37 -0
  37. data/lib/ls4/service/config.rb +63 -0
  38. data/lib/ls4/service/config_cs.rb +33 -0
  39. data/lib/ls4/service/config_ds.rb +56 -0
  40. data/lib/ls4/service/config_gw.rb +42 -0
  41. data/lib/ls4/service/data_client.rb +122 -0
  42. data/lib/ls4/service/data_server.rb +168 -0
  43. data/lib/ls4/service/data_server_url.rb +83 -0
  44. data/lib/ls4/service/gateway.rb +375 -0
  45. data/lib/ls4/service/gateway_ro.rb +91 -0
  46. data/lib/ls4/service/gw_http.rb +821 -0
  47. data/lib/ls4/service/heartbeat.rb +182 -0
  48. data/lib/ls4/service/log.rb +81 -0
  49. data/lib/ls4/service/master_select.rb +148 -0
  50. data/lib/ls4/service/mds.rb +292 -0
  51. data/lib/ls4/service/mds_cache.rb +294 -0
  52. data/lib/ls4/service/mds_cache_mem.rb +63 -0
  53. data/lib/ls4/service/mds_cache_memcached.rb +65 -0
  54. data/lib/ls4/service/mds_ha.rb +176 -0
  55. data/lib/ls4/service/mds_memcache.rb +209 -0
  56. data/lib/ls4/service/mds_tc.rb +508 -0
  57. data/lib/ls4/service/mds_tt.rb +472 -0
  58. data/lib/ls4/service/membership.rb +331 -0
  59. data/lib/ls4/service/process.rb +90 -0
  60. data/lib/ls4/service/rpc.rb +50 -0
  61. data/lib/ls4/service/rpc_cs.rb +101 -0
  62. data/lib/ls4/service/rpc_ds.rb +96 -0
  63. data/lib/ls4/service/rpc_gw.rb +255 -0
  64. data/lib/ls4/service/rts.rb +94 -0
  65. data/lib/ls4/service/rts_file.rb +76 -0
  66. data/lib/ls4/service/rts_memory.rb +55 -0
  67. data/lib/ls4/service/slave.rb +132 -0
  68. data/lib/ls4/service/stat.rb +91 -0
  69. data/lib/ls4/service/stat_cs.rb +25 -0
  70. data/lib/ls4/service/stat_ds.rb +40 -0
  71. data/lib/ls4/service/stat_gw.rb +25 -0
  72. data/lib/ls4/service/storage.rb +116 -0
  73. data/lib/ls4/service/storage_dir.rb +201 -0
  74. data/lib/ls4/service/sync.rb +206 -0
  75. data/lib/ls4/service/time_check.rb +80 -0
  76. data/lib/ls4/service/ulog.rb +159 -0
  77. data/lib/ls4/service/ulog_file.rb +398 -0
  78. data/lib/ls4/service/ulog_memory.rb +53 -0
  79. data/lib/ls4/service/weight.rb +134 -0
  80. data/lib/ls4/version.rb +5 -0
  81. data/test/01_add_get_remove.rt +84 -0
  82. data/test/02_read.rt +61 -0
  83. data/test/03_getd_readd.rt +69 -0
  84. data/test/04_version_time.rt +170 -0
  85. data/test/05_version_name.rt +161 -0
  86. data/test/06_http_get_set_remove_1.rt +119 -0
  87. data/test/07_http_get_set_remove_2.rt +116 -0
  88. data/test/08_read_only_time.rt +177 -0
  89. data/test/09_read_only_name.rt +173 -0
  90. data/test/10_http_get_set_remove_3.rt +73 -0
  91. data/test/11_mds_cache_memcached.rt +88 -0
  92. data/test/12_mds_cache_local_memory.rt +86 -0
  93. data/test/13_memcache_mds.rt +84 -0
  94. data/test/14_delete.rt +63 -0
  95. data/test/15_standalone.rt +71 -0
  96. data/test/chukan.rb +516 -0
  97. data/test/common.rb +250 -0
  98. data/test/load_test.rb +79 -0
  99. data/test/load_test_offload.rb +86 -0
  100. metadata +295 -0
@@ -0,0 +1,209 @@
1
+ #
2
+ # LS4
3
+ # Copyright (C) 2010-2011 FURUHASHI Sadayuki
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ module LS4
19
+
20
+
21
+ class MemcacheMDS < MDS
22
+ MDSSelector.register(:mc, self)
23
+
24
+ class HADB < BasicHADB
25
+ DEFAULT_PORT = 11211
26
+
27
+ def open_db(addr)
28
+ MemCache.new(addr.to_s, {:urlencode => false, :compression => :false, :multithread => true, :timeout => 5.0})
29
+ end
30
+
31
+ def ensure_db(db, addr)
32
+ true
33
+ end
34
+
35
+ def error_result?(db, result)
36
+ nil
37
+ end
38
+ end
39
+
40
+ def initialize
41
+ require 'memcache'
42
+ end
43
+
44
+ def open(expr)
45
+ @hadb = HADB.new(expr)
46
+ end
47
+
48
+ def close
49
+ @hadb.close
50
+ end
51
+
52
+ def get_okey(key, version=nil, &cb)
53
+ e = get_impl(key, version)
54
+ if e
55
+ cb.call(e.to_okey(key), nil) rescue nil
56
+ else
57
+ cb.call(nil, nil) rescue nil
58
+ end
59
+ rescue
60
+ cb.call(nil, $!) rescue nil
61
+ end
62
+
63
+ def get_attrs(key, version=nil, &cb)
64
+ e = get_impl(key, version)
65
+ if e
66
+ cb.call(e.attrs, nil) rescue nil
67
+ else
68
+ cb.call(nil, nil) rescue nil
69
+ end
70
+ rescue
71
+ cb.call(nil, $!) rescue nil
72
+ end
73
+
74
+ def get_okey_attrs(key, version=nil, &cb)
75
+ e = get_impl(key, version)
76
+ if e
77
+ cb.call([e.to_okey(key), e.attrs], nil) rescue nil
78
+ else
79
+ cb.call(nil, nil) rescue nil
80
+ end
81
+ rescue
82
+ cb.call(nil, $!) rescue nil
83
+ end
84
+
85
+ def add(key, attrs={}, vname=nil, &cb)
86
+ okey = set_impl(key, attrs, vname)
87
+ cb.call(okey, nil) rescue nil
88
+ rescue
89
+ cb.call(nil, $!) rescue nil
90
+ end
91
+
92
+ def update_attrs(key, attrs, &cb)
93
+ okey = update_impl(key) {|old_attrs|
94
+ attrs
95
+ }
96
+ cb.call(okey, nil) rescue nil
97
+ rescue
98
+ cb.call(nil, $!) rescue nil
99
+ end
100
+
101
+ def remove(key, &cb)
102
+ e = get_impl(key)
103
+
104
+ if e
105
+ result = nil
106
+ @hadb.write(key) {|mc|
107
+ result = mc.delete(key)
108
+ }
109
+ #if result !~ /DELETED/
110
+ # TODO
111
+ #end
112
+
113
+ cb.call(e.to_okey(key), nil) rescue nil
114
+ else
115
+ cb.call(nil, nil) rescue nil
116
+ end
117
+
118
+ rescue
119
+ cb.call(nil, $!) rescue nil
120
+ end
121
+
122
+ def delete(key, version=nil, &cb)
123
+ raise "version is not supported on memcache MDS" if version
124
+ remove(key, &cb)
125
+ end
126
+
127
+ private
128
+ class Entry
129
+ def initialize(rsid=nil, vtime=nil, attrs=nil)
130
+ @rsid = rsid
131
+ @vtime = vtime
132
+ @attrs = attrs
133
+ end
134
+
135
+ attr_accessor :rsid
136
+ attr_accessor :vtime
137
+ attr_accessor :attrs
138
+
139
+ def to_okey(key)
140
+ ObjectKey.new(key, @vtime, @rsid)
141
+ end
142
+
143
+ def to_msgpack(out = '')
144
+ [@rsid, @vtime, @attrs].to_msgpack(out)
145
+ end
146
+
147
+ def from_msgpack(obj)
148
+ @rsid = obj[0]
149
+ @vtime = obj[1]
150
+ @attrs = obj[2]
151
+ self
152
+ end
153
+ end
154
+
155
+ def get_impl(key, version=nil)
156
+ raise "version is not supported on memcache MDS" if version
157
+
158
+ raw = nil
159
+ @hadb.read(key) {|mc|
160
+ raw = mc.get(key, true)
161
+ }
162
+
163
+ if raw
164
+ obj = MessagePack.unpack(raw)
165
+ e = Entry.new.from_msgpack(obj)
166
+ return e
167
+ else
168
+ return nil
169
+ end
170
+ end
171
+
172
+ def set_impl(key, attrs, vname=nil)
173
+ raise "version is not supported on memcache MDS" if vname
174
+
175
+ okey = new_okey(key)
176
+ e = Entry.new(okey.rsid, okey.vtime, attrs)
177
+ raw = e.to_msgpack
178
+ result = nil
179
+ @hadb.write(key) {|mc|
180
+ # TODO add/cas?
181
+ result = mc.set(key, raw, 0, true)
182
+ }
183
+
184
+ #if result !~ /STORED/
185
+ # TODO
186
+ #end
187
+
188
+ return okey
189
+ end
190
+
191
+ def update_impl(key, &modifier)
192
+ e = get_impl(key)
193
+
194
+ if e
195
+ e.attrs = modifier.call(e.attrs)
196
+ raw = e.to_msgpack
197
+ @hadb.write(key) {|mc|
198
+ # TODO cas?
199
+ mc.set(key, raw, 0, true)
200
+ }
201
+ return e.to_okey(key)
202
+ else
203
+ return nil
204
+ end
205
+ end
206
+ end
207
+
208
+
209
+ end
@@ -0,0 +1,508 @@
1
+ #
2
+ # LS4
3
+ # Copyright (C) 2010-2011 FURUHASHI Sadayuki
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ module LS4
19
+
20
+
21
+ class LocalTokyoCabinetMDS < MDS
22
+ MDSSelector.register(:local, self)
23
+
24
+ COL_PK = ''
25
+ COL_KEY = '_key'
26
+ COL_VTIME = '_time'
27
+ COL_RSID = '_rsid'
28
+ COL_VNAME = '_vname'
29
+ COL_REMOVED = '_removed'
30
+
31
+ COLS_RESERVED = [COL_PK, COL_KEY, COL_VTIME, COL_RSID, COL_VNAME, COL_REMOVED]
32
+ COLS_REQUIRED = [COL_PK, COL_KEY, COL_VTIME, COL_RSID]
33
+
34
+ def self.define_consts
35
+ unless const_defined?(:QRY)
36
+ require 'tokyocabinet'
37
+ const_set(:QRY, TokyoCabinet::TDBQRY)
38
+ HADB.const_set(:TDB, TokyoCabinet::TDB)
39
+ HADB.const_set(:FATAL_ERROR, [
40
+ TokyoCabinet::TDB::ETHREAD,
41
+ TokyoCabinet::TDB::EINVALID,
42
+ TokyoCabinet::TDB::ENOFILE,
43
+ TokyoCabinet::TDB::ENOPERM,
44
+ TokyoCabinet::TDB::EMETA,
45
+ TokyoCabinet::TDB::ERHEAD,
46
+ TokyoCabinet::TDB::EOPEN,
47
+ TokyoCabinet::TDB::ECLOSE,
48
+ TokyoCabinet::TDB::ETRUNC,
49
+ TokyoCabinet::TDB::ESYNC,
50
+ TokyoCabinet::TDB::ESTAT,
51
+ TokyoCabinet::TDB::ESEEK,
52
+ TokyoCabinet::TDB::EREAD,
53
+ TokyoCabinet::TDB::EWRITE,
54
+ TokyoCabinet::TDB::EMMAP,
55
+ TokyoCabinet::TDB::ELOCK,
56
+ TokyoCabinet::TDB::EUNLINK,
57
+ TokyoCabinet::TDB::ERENAME,
58
+ TokyoCabinet::TDB::EMKDIR,
59
+ TokyoCabinet::TDB::ERMDIR,
60
+ #TokyoCabinet::TDB::EKEEP,
61
+ #TokyoCabinet::TDB::ENOREC,
62
+ TokyoCabinet::TDB::EMISC
63
+ ])
64
+ end
65
+ end
66
+
67
+ class HADB < BasicHADB
68
+ def parse_addr(addr)
69
+ addr
70
+ end
71
+
72
+ def open_db(addr)
73
+ db = TDB.new
74
+ db.instance_eval("@enc = 'ASCII-8BIT'") # FIXME
75
+ unless db.open(addr, TDB::OCREAT|TDB::OWRITER)
76
+ $log.warn "failed to connect local TokyoCabinet MDS: #{addr}"
77
+ end
78
+ db.setindex(COL_KEY, TDB::ITLEXICAL)
79
+ db
80
+ end
81
+
82
+ def ensure_db(db, addr)
83
+ if FATAL_ERROR.include?(db.ecode)
84
+ db.close rescue nil
85
+ db.instance_eval("@ecode = ESUCCESS") # FIXME
86
+ db.open(*addr)
87
+ end
88
+ return db
89
+ rescue
90
+ return nil
91
+ end
92
+
93
+ def error_result?(db, result)
94
+ if FATAL_ERROR.include?(db.ecode)
95
+ return db.errmsg(db.ecode)
96
+ end
97
+ return nil
98
+ end
99
+ end
100
+
101
+ if defined?(Random)
102
+ def initialize
103
+ self.class.define_consts
104
+ @random = Random.new
105
+ @pid = Process.pid
106
+ end
107
+ else
108
+ # Ruby 1.8
109
+ class PseudoRandom
110
+ def rand(n)
111
+ Kernel::rand(n)
112
+ end
113
+ end
114
+ def initialize
115
+ self.class.define_consts
116
+ @random = PseudoRandom.new
117
+ @pid = Process.pid
118
+ end
119
+ end
120
+
121
+ def open(expr)
122
+ @hadb = HADB.new(expr)
123
+ end
124
+
125
+ def close
126
+ @hadb.close
127
+ end
128
+
129
+ def get_okey(key, version=nil, &cb)
130
+ map = get_impl(key, version, COLS_RESERVED)
131
+ if map && !is_removed(map)
132
+ okey = to_okey(map)
133
+ cb.call(okey, nil) rescue nil
134
+ else
135
+ cb.call(nil, nil) rescue nil
136
+ end
137
+ rescue
138
+ cb.call(nil, $!) rescue nil
139
+ end
140
+
141
+ def get_attrs(key, version=nil, &cb)
142
+ map = get_impl(key, version)
143
+ if map && !is_removed(map)
144
+ attrs = to_attrs(map)
145
+ cb.call(attrs, nil) rescue nil
146
+ else
147
+ cb.call(nil, nil) rescue nil
148
+ end
149
+ rescue
150
+ cb.call(nil, $!) rescue nil
151
+ end
152
+
153
+ def get_okey_attrs(key, version=nil, &cb)
154
+ map = get_impl(key, version)
155
+ if map && !is_removed(map)
156
+ okey = to_okey(map)
157
+ attrs = to_attrs(map)
158
+ cb.call([okey, attrs], nil) rescue nil
159
+ else
160
+ cb.call(nil, nil) rescue nil
161
+ end
162
+ rescue
163
+ cb.call(nil, $!) rescue nil
164
+ end
165
+
166
+ def add(key, attrs={}, vname=nil, &cb)
167
+ okey = add_impl(key, attrs, vname)
168
+ cb.call(okey, nil) rescue nil
169
+ rescue
170
+ cb.call(nil, $!) rescue nil
171
+ end
172
+
173
+ def update_attrs(key, attrs, &cb)
174
+ okey = update_impl(key) {|old_attrs|
175
+ attrs
176
+ }
177
+ cb.call(okey, nil) rescue nil
178
+ rescue
179
+ cb.call(nil, $!) rescue nil
180
+ end
181
+
182
+ #def merge_attrs(key, attrs, &cb)
183
+ # okey = update_impl(key) {|old_attrs|
184
+ # old_attrs.merge(attrs)
185
+ # }
186
+ # cb.call(okey, nil) rescue nil
187
+ #rescue
188
+ # cb.call(nil, $!) rescue nil
189
+ #end
190
+
191
+ def remove(key, &cb)
192
+ map = get_impl_head(key, COLS_REQUIRED)
193
+ if map && !is_removed(map)
194
+ okey = to_okey(map)
195
+
196
+ # optional: inherit rsid
197
+ rsid = map[COL_RSID].to_i
198
+
199
+ # get current vtime later than old vtime
200
+ vtime = get_current_vtime(map[COL_VTIME].to_i)
201
+
202
+ remove_okey = new_okey(key, vtime, rsid)
203
+
204
+ # insert
205
+ pk = new_pk(map[COL_PK])
206
+ map = to_map({}, remove_okey, nil, true)
207
+ @hadb.write(key) {|rdb| rdb.put(pk, map) }
208
+
209
+ cb.call(okey, nil) rescue nil
210
+
211
+ else
212
+ cb.call(nil, nil) rescue nil
213
+ end
214
+
215
+ rescue
216
+ cb.call(nil, $!) rescue nil
217
+ end
218
+
219
+ def delete(key, version=nil, &cb)
220
+ map = get_impl(key, version, COLS_RESERVED)
221
+ if map && !is_removed(map)
222
+ okey = to_okey(map)
223
+
224
+ pk = map[COL_PK]
225
+ @hadb.write(key) {|rdb| rdb.delete(pk) }
226
+
227
+ cb.call(okey, nil) rescue nil
228
+
229
+ else
230
+ cb.call(nil, nil) rescue nil
231
+ end
232
+ rescue
233
+ cb.call(nil, $!) rescue nil
234
+ end
235
+
236
+ def util_locate(key, &cb)
237
+ array = @hadb.read(key) {|rdb|
238
+ qry = QRY.new(rdb)
239
+ qry.addcond(COL_KEY, QRY::QCSTREQ, key)
240
+ qry.search.map {|key|
241
+ rdb.get(key)
242
+ map[COL_PK] = key
243
+ map
244
+ }
245
+ }
246
+
247
+ array.reject! {|map|
248
+ !is_valid_map(map) || is_removed(map)
249
+ }
250
+
251
+ array.map! {|map|
252
+ [to_okey(map), map[COL_VNAME]]
253
+ }
254
+
255
+ cb.call(array, nil) rescue nil
256
+
257
+ rescue
258
+ cb.call(nil, $!) rescue nil
259
+ end
260
+
261
+ private
262
+ def to_okey(map)
263
+ key = map[COL_KEY]
264
+ rsid = map[COL_RSID].to_i
265
+ vtime = map[COL_VTIME].to_i
266
+ new_okey(key, vtime, rsid)
267
+ end
268
+
269
+ def to_attrs(map)
270
+ map.reject {|k,v| COLS_RESERVED.include?(k) }
271
+ end
272
+
273
+ def to_map(attrs, okey, vname=nil, removed=false)
274
+ map = attrs.dup
275
+ map.delete(COL_PK)
276
+ map[COL_KEY] = okey.key
277
+ map[COL_RSID] = okey.rsid.to_s
278
+ map[COL_VTIME] = okey.vtime.to_s
279
+ if vname
280
+ map[COL_VNAME] = vname.to_s
281
+ end
282
+ if removed
283
+ map[COL_REMOVED] = "1"
284
+ end
285
+ map
286
+ end
287
+
288
+ def get_impl(key, version=nil, cols=nil)
289
+ if version == nil
290
+ get_impl_head(key, cols)
291
+ elsif version.is_a?(String)
292
+ get_impl_vname(key, version, cols)
293
+ else
294
+ get_impl_vtime(key, version, cols)
295
+ end
296
+ end
297
+
298
+ def get_impl_head(key, cols=nil)
299
+ map = @hadb.read(key) {|rdb|
300
+ qry = QRY.new(rdb)
301
+ qry.addcond(COL_KEY, QRY::QCSTREQ, key)
302
+ qry.setorder(COL_VTIME, QRY::QONUMDESC)
303
+ qry.setlimit(1)
304
+ array = qry.search.map {|key|
305
+ map = rdb.get(key)
306
+ map[COL_PK] = key
307
+ map
308
+ }
309
+ array[0]
310
+ }
311
+
312
+ if map == nil
313
+ return nil
314
+ elsif !is_valid_map(map)
315
+ return nil
316
+ else
317
+ return map
318
+ end
319
+ end
320
+
321
+ def get_impl_vname(key, vname, cols=nil)
322
+ map = @hadb.read(key) {|rdb|
323
+ qry = QRY.new(rdb)
324
+ qry.addcond(COL_KEY, QRY::QCSTREQ, key)
325
+ qry.addcond(COL_VNAME, QRY::QCSTREQ, vname)
326
+ qry.setorder(COL_VTIME, QRY::QONUMDESC)
327
+ qry.setlimit(1)
328
+ array = qry.search.map {|key|
329
+ rdb.get(key)
330
+ map[COL_PK] = key
331
+ map
332
+ }
333
+ array[0]
334
+ }
335
+
336
+ if map == nil
337
+ return nil
338
+ elsif !is_valid_map(map)
339
+ return nil
340
+ else
341
+ return map
342
+ end
343
+ end
344
+
345
+ def get_impl_vtime(key, vtime, cols)
346
+ map = @hadb.read(key) {|rdb|
347
+ qry = QRY.new(rdb)
348
+ qry.addcond(COL_KEY, QRY::QCSTREQ, key)
349
+ qry.addcond(COL_VTIME, QRY::QCNUMLE, vtime.to_s)
350
+ qry.setorder(COL_VTIME, QRY::QONUMDESC)
351
+ qry.setlimit(1)
352
+ array = qry.search.map {|key|
353
+ rdb.get(key)
354
+ map[COL_PK] = key
355
+ map
356
+ }
357
+ array[0]
358
+ }
359
+
360
+ if map == nil
361
+ return nil
362
+ elsif !is_valid_map(map)
363
+ return nil
364
+ else
365
+ return map
366
+ end
367
+ end
368
+
369
+ #def select_latest_valid(array)
370
+ # latest = select_latest(array)
371
+ # if is_valid_map(latest)
372
+ # return latest
373
+ # else
374
+ # return nil
375
+ # end
376
+ #end
377
+
378
+ #def select_latest(array)
379
+ # if array.size == 1
380
+ # return array[0]
381
+ # end
382
+ #
383
+ # max_vtime = 0
384
+ # marray = []
385
+ #
386
+ # array.each {|map|
387
+ # vtime = map[COL_VTIME].to_i
388
+ # if max_vtime < vtime
389
+ # marray.clear
390
+ # marray << map
391
+ # max_vtime = vtime
392
+ # elsif max_vtime == vtime
393
+ # marray << map
394
+ # end
395
+ # }
396
+ #
397
+ # if marray.size == 1
398
+ # return marray[0]
399
+ # else
400
+ # return marray.sort_by {|map| map[COL_PK] }.last
401
+ # end
402
+ #end
403
+
404
+ def is_valid_map(map)
405
+ COLS_REQUIRED.all? {|col| map.has_key?(col) }
406
+ end
407
+
408
+ def is_removed(map)
409
+ map.has_key?(COL_REMOVED)
410
+ end
411
+
412
+ def add_impl(key, attrs, vname=nil)
413
+ map = get_impl_head(key, COLS_REQUIRED)
414
+
415
+ if map
416
+ # optional: inherit rsid
417
+ rsid = map[COL_RSID].to_i
418
+
419
+ # get current vtime later than old vtime
420
+ vtime = get_current_vtime(map[COL_VTIME].to_i)
421
+
422
+ okey = new_okey(key, vtime, rsid)
423
+
424
+ # insert
425
+ pk = new_pk(map[COL_PK])
426
+ map = to_map(attrs, okey, vname)
427
+ @hadb.write(key) {|rdb| rdb.put(pk, map) }
428
+
429
+ else
430
+ attrs ||= {}
431
+
432
+ okey = new_okey(key)
433
+
434
+ # insert
435
+ pk = new_pk()
436
+ map = to_map(attrs, okey, vname)
437
+ @hadb.write(key) {|rdb| rdb.put(pk, map) }
438
+ end
439
+
440
+ return okey
441
+ end
442
+
443
+ def update_impl(key, &modifier)
444
+ map = get_impl_head(key, nil)
445
+
446
+ if map && !is_removed(map)
447
+ okey = to_okey(map)
448
+
449
+ # create new attrs
450
+ attrs = modifier.call( to_attrs(map) )
451
+
452
+ # reject old attributes
453
+ map.reject! {|k,v| !COLS_RESERVED.include?(k) }
454
+
455
+ # merge new attributes
456
+ map = attrs.merge(map)
457
+
458
+ # update
459
+ pk = map.delete(COL_PK)
460
+ @hadb.write(key) {|rdb| rdb.put(pk, map) }
461
+
462
+ return okey
463
+
464
+ else
465
+ return nil
466
+ end
467
+ end
468
+
469
+ # +----------+----+------+---+
470
+ # | 30 | 10 | 16 | 8 |
471
+ # +----------+----+------+---+
472
+ # UNIX time
473
+ # millisec
474
+ # rand
475
+ # generator-id
476
+ # +--------------------------+
477
+ # 64 bits
478
+ #
479
+ def new_pk(at_least=nil)
480
+ nowtime = Time.now.utc
481
+ sec = nowtime.sec
482
+ msec = nowtime.usec / 1000
483
+
484
+ if at_least && at_least.size == 8
485
+ time_u, time_d = at_least.unpack('NC')
486
+ asec = time_u>>2
487
+ amsec = (time_u&0x3)<<2 & time_d
488
+ if sec < asec || (sec == asec && msec <= amsec)
489
+ sec = asec + 1
490
+ msec = amsec
491
+ end
492
+ end
493
+
494
+ gid = @pid
495
+
496
+ r = @random.rand(2**16)
497
+ raw = [sec<<2|msec>>8, msec&0xff, r, gid].pack('NCnC')
498
+
499
+ # FIXME base64
500
+ raw = [raw].pack('m')
501
+ raw.gsub!(/[\n\=]+/,'')
502
+ raw
503
+ end
504
+ end
505
+
506
+
507
+ end
508
+