ls4 0.9.0

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