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,228 @@
1
+ #
2
+ # VariableByteCode
3
+ #
4
+ # Copyright (C) 2010 FURUHASHI Sadayuki. All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
+ # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
+ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
+ # SUCH DAMAGE.
28
+ #
29
+
30
+ class VariableByteCode
31
+ if "a"[0].is_a?(String) # ruby 1.9
32
+ def self.encode(value, raw="")
33
+ begin
34
+ v = value & 0b01111111 | 0b10000000
35
+ value >>= 7
36
+ raw << [v].pack('C')
37
+ end while value > 0
38
+ raw[raw.length-1] = [raw[raw.length-1].unpack('C')[0] & 0b01111111].pack('C')
39
+ raw
40
+ end
41
+ else
42
+ def self.encode(value, raw="")
43
+ begin
44
+ v = value & 0b01111111 | 0b10000000
45
+ value >>= 7
46
+ raw << [v].pack('C')
47
+ end while value > 0
48
+ raw[raw.length-1] &= 0b01111111
49
+ raw
50
+ end
51
+ end
52
+
53
+
54
+ if "a"[0].is_a?(String) # ruby 1.9
55
+ def self.decode(raw)
56
+ i = 0
57
+ value = 0
58
+ while raw.length > i
59
+ v = raw[i].unpack('C')[0]
60
+ if v & 0b10000000 != 0
61
+ v &= 0b01111111
62
+ v <<= (i*7)
63
+ value |= v
64
+ else
65
+ v <<= (i*7)
66
+ value |= v
67
+ return value
68
+ end
69
+ i += 1
70
+ end
71
+ return value
72
+ end
73
+ else
74
+ def self.decode(raw)
75
+ i = 0
76
+ value = 0
77
+ while raw.length > i
78
+ v = raw[i]
79
+ if v & 0b10000000 != 0
80
+ v &= 0b01111111
81
+ v <<= (i*7)
82
+ value |= v
83
+ else
84
+ v <<= (i*7)
85
+ value |= v
86
+ return value
87
+ end
88
+ i += 1
89
+ end
90
+ return value
91
+ end
92
+ end
93
+
94
+
95
+ if "a"[0].is_a?(String) # ruby 1.9
96
+ def self.decode_index(raw, from=0)
97
+ i = from
98
+ value = 0
99
+ while raw.length > i
100
+ v = raw[i].unpack('C')[0]
101
+ if v & 0b10000000 != 0
102
+ v &= 0b01111111
103
+ v <<= ((i-from)*7)
104
+ value |= v
105
+ else
106
+ v <<= ((i-from)*7)
107
+ value |= v
108
+ return value, i+1
109
+ end
110
+ i += 1
111
+ end
112
+ return value, i
113
+ end
114
+ else
115
+ def self.decode_index(raw, from=0)
116
+ i = from
117
+ value = 0
118
+ while raw.length > i
119
+ v = raw[i]
120
+ if v & 0b10000000 != 0
121
+ v &= 0b01111111
122
+ v <<= ((i-from)*7)
123
+ value |= v
124
+ else
125
+ v <<= ((i-from)*7)
126
+ value |= v
127
+ return value, i+1
128
+ end
129
+ i += 1
130
+ end
131
+ return value, i
132
+ end
133
+ end
134
+
135
+
136
+ if "a"[0].is_a?(String) # ruby 1.9
137
+ def self.decode_stream(io)
138
+ i = 0
139
+ value = 0
140
+ b = " "
141
+ while b = io.read(1, b)
142
+ v = b.unpack('C')[0]
143
+ if v & 0b10000000 != 0
144
+ v &= 0b01111111
145
+ v <<= (i*7)
146
+ value |= v
147
+ else
148
+ v <<= (i*7)
149
+ value |= v
150
+ return value
151
+ end
152
+ i += 1
153
+ end
154
+ return value
155
+ end
156
+ else
157
+ def self.decode_stream(io)
158
+ i = 0
159
+ value = 0
160
+ b = " "
161
+ while b = io.read(1, b)
162
+ v = b[0]
163
+ if v & 0b10000000 != 0
164
+ v &= 0b01111111
165
+ v <<= (i*7)
166
+ value |= v
167
+ else
168
+ v <<= (i*7)
169
+ value |= v
170
+ return value
171
+ end
172
+ i += 1
173
+ end
174
+ return value
175
+ end
176
+ end
177
+
178
+
179
+ def self.encode_n(array, out="")
180
+ array.each {|value|
181
+ encode(value, out)
182
+ }
183
+ out
184
+ end
185
+
186
+
187
+ def self.decode_n(raw, from=0, length=raw.length-from)
188
+ upto = from + length
189
+ upto = raw.length if upto > raw.length
190
+ values = []
191
+ while upto > from
192
+ value, from = decode_index(raw, from)
193
+ values << value
194
+ end
195
+ values
196
+ end
197
+ end
198
+
199
+
200
+ if $0 == __FILE__
201
+ puts "testing VariableByteCode ..."
202
+
203
+ def check(value)
204
+ raw = VariableByteCode.encode(value)
205
+ if VariableByteCode.decode(raw) != value
206
+ puts "VariableByteCode.decode test failed #{value}"
207
+ end
208
+ if VariableByteCode.decode_index(raw,0)[0] != value
209
+ puts "VariableByteCode.decode_index test failed #{value}"
210
+ end
211
+ end
212
+
213
+ 100000.times {
214
+ check rand(1<<64)
215
+ }
216
+
217
+ def check_n(values)
218
+ raw = VariableByteCode.encode_n(values)
219
+ if VariableByteCode.decode_n(raw) != values
220
+ puts "VariableByteCode.decode_n test failed #{values}"
221
+ end
222
+ end
223
+
224
+ 100.times {
225
+ check_n (1..rand(1<<8)).map {|i| rand(1<<64) }
226
+ }
227
+ end
228
+
@@ -0,0 +1,212 @@
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 FaultList < TSVData
22
+ def initialize
23
+ @path = nil
24
+ @fault_nids = []
25
+ super()
26
+ end
27
+
28
+ def update(nids)
29
+ @fault_nids = nids.dup
30
+ on_change
31
+ nil
32
+ end
33
+
34
+ def include?(nid)
35
+ @fault_nids.include?(nid)
36
+ end
37
+
38
+ def get_list
39
+ @fault_nids.dup
40
+ end
41
+
42
+ def to_msgpack(out = '')
43
+ @fault_nids.to_msgpack(out)
44
+ end
45
+
46
+ def from_msgpack(msg)
47
+ @fault_nids = msg
48
+ on_change
49
+ self
50
+ end
51
+
52
+ protected
53
+ def read
54
+ return unless @path
55
+
56
+ begin
57
+ fault_nids = []
58
+
59
+ tsv_read do |row|
60
+ fault_nids << row[0].to_i
61
+ end
62
+
63
+ @fault_nids = fault_nids
64
+ rescue
65
+ $log.debug $!
66
+ end
67
+
68
+ update_hash
69
+
70
+ rescue
71
+ $log.debug $!
72
+ raise
73
+ end
74
+
75
+ def write
76
+ return unless @path
77
+
78
+ tsv_write do |writer|
79
+ @fault_nids.each {|nid|
80
+ writer << [nid.to_s]
81
+ }
82
+ end
83
+
84
+ rescue
85
+ $log.error $!
86
+ raise
87
+ end
88
+ end
89
+
90
+
91
+ class FaultDetector
92
+ class Term
93
+ def initialize(value)
94
+ @value = value
95
+ end
96
+ def expired?
97
+ @value <= 0
98
+ end
99
+ def forward_timer
100
+ if @value > 0
101
+ @value -= 1
102
+ if @value == 0
103
+ return true
104
+ end
105
+ return false
106
+ end
107
+ return nil
108
+ end
109
+ def reset(value)
110
+ @value = value
111
+ end
112
+ attr_accessor :value
113
+ end
114
+
115
+ def initialize
116
+ @period = 10
117
+ @detect = 5
118
+ @first_detect = 20 # FIXME
119
+ @map = {} # {nid => Term}
120
+ end
121
+
122
+ def set_init(all_nids, fault_nids)
123
+ map = {}
124
+ all_nids.each {|nid|
125
+ map[nid] = Term.new(@period + @first_detect)
126
+ }
127
+ fault_nids.each {|nid|
128
+ if all_nids.include?(nid)
129
+ map[nid] = Term.new(0)
130
+ end
131
+ }
132
+ @map = map
133
+ nil
134
+ end
135
+
136
+ def update(nid)
137
+ term = @map[nid]
138
+ if term && !term.expired?
139
+ term.reset(@period + @detect)
140
+ return @period
141
+ end
142
+ return nil
143
+ end
144
+
145
+ def reset(nid)
146
+ if term = @map[nid]
147
+ term.reset(@period + @detect)
148
+ on_change
149
+ return true
150
+ end
151
+ return nil
152
+ end
153
+
154
+ def add_nid(nid)
155
+ if term = @map[nid]
156
+ return nil
157
+ end
158
+ @map[nid] = Term.new(@period + @first_detect)
159
+ return true
160
+ end
161
+
162
+ def set_nid(nid)
163
+ if @map.has_key?(nid)
164
+ reset(nid)
165
+ else
166
+ add_nid(nid)
167
+ end
168
+ end
169
+
170
+ def delete_nid(nid)
171
+ if term = @map.delete(nid)
172
+ if term.expired?
173
+ on_change
174
+ end
175
+ return true
176
+ end
177
+ return nil
178
+ end
179
+
180
+ def get_fault_nids
181
+ fault_nids = []
182
+ @map.each_pair {|nid,term|
183
+ if term.expired?
184
+ fault_nids << nid
185
+ end
186
+ }
187
+ fault_nids
188
+ end
189
+
190
+ def forward_timer
191
+ fault_nids = []
192
+ @map.each_pair {|nid,term|
193
+ if term.forward_timer
194
+ fault_nids << nid
195
+ end
196
+ }
197
+
198
+ if !fault_nids.empty?
199
+ on_change
200
+ end
201
+
202
+ fault_nids
203
+ end
204
+
205
+ private
206
+ def on_change
207
+ # do nothing
208
+ end
209
+ end
210
+
211
+
212
+ end
@@ -0,0 +1,253 @@
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 NodeList < TSVData
22
+ def initialize
23
+ @path = nil
24
+ @map = {} # {nid => Node}
25
+ super()
26
+ end
27
+
28
+ def get(nid)
29
+ @map[nid]
30
+ end
31
+
32
+ def add(node)
33
+ @map[node.nid] = node
34
+ on_change
35
+ true
36
+ end
37
+
38
+ def delete(nid)
39
+ if node = @map.delete(nid)
40
+ on_change
41
+ node
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ def include?(nid)
48
+ @map.has_key?(nid)
49
+ end
50
+
51
+ def update(nid, address, name, rsids, location)
52
+ node = @map[nid]
53
+ if node
54
+ if address
55
+ node.address = address
56
+ end
57
+ if name
58
+ node.name = name
59
+ end
60
+ if rsids
61
+ node.rsids = rsids
62
+ end
63
+ if location
64
+ node.location = location
65
+ end
66
+ on_change
67
+ true
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ def each(&block)
74
+ @map.each_value(&block)
75
+ end
76
+
77
+ def get_all_nodes
78
+ @map.values
79
+ end
80
+
81
+ def get_all_nids
82
+ @map.map {|nid,node| nid }
83
+ end
84
+
85
+ def to_msgpack(out = '')
86
+ @map.values.sort_by {|node| node.nid }.to_msgpack(out)
87
+ end
88
+
89
+ def from_msgpack(obj)
90
+ map = {}
91
+ obj.each {|n|
92
+ node = Node.new.from_msgpack(n)
93
+ map[node.nid] = node
94
+ }
95
+ @map = map
96
+ on_change
97
+ self
98
+ end
99
+
100
+ protected
101
+ def read
102
+ return unless @path
103
+
104
+ begin
105
+ map = {}
106
+
107
+ tsv_read do |row|
108
+ nid = row[0].to_i
109
+
110
+ name = row[1]
111
+
112
+ addr = row[2].to_s
113
+ host, port = addr.split(':',2)
114
+ port ||= DS_DEFAULT_PORT
115
+ address = Address.new(host, port.to_i)
116
+
117
+ rsids = row[3].split(',').map {|id| id.to_i }
118
+
119
+ location = row[4]
120
+
121
+ map[nid] = Node.new(nid, address, name, rsids, location)
122
+ end
123
+
124
+ @map = map
125
+ rescue
126
+ $log.debug $!
127
+ end
128
+
129
+ update_hash
130
+
131
+ rescue
132
+ $log.debug $!
133
+ raise
134
+ end
135
+
136
+ def write
137
+ return unless @path
138
+
139
+ tsv_write do |writer|
140
+ @map.each_value {|node|
141
+ row = []
142
+ row[0] = node.nid.to_s
143
+ row[1] = node.name
144
+ row[2] = "#{node.address.host}:#{node.address.port}"
145
+ row[3] = node.rsids.join(',')
146
+ writer << row
147
+ }
148
+ end
149
+
150
+ rescue
151
+ $log.error $!
152
+ raise
153
+ end
154
+ end
155
+
156
+
157
+ class Membership
158
+ def initialize
159
+ @nodes = NodeList.new
160
+ @active_rsids = []
161
+ end
162
+
163
+ def open(path)
164
+ @nodes.open(path)
165
+ update_active_rsids
166
+ end
167
+
168
+ def close
169
+ @nodes.close
170
+ end
171
+
172
+ def add_node(nid, address, name, rsids, location)
173
+ node = Node.new(nid, address, name, rsids, location)
174
+ if @nodes.get(nid)
175
+ raise "nid already exist: #{nid}"
176
+ end
177
+ @nodes.add(node)
178
+ update_active_rsids
179
+ node
180
+ end
181
+
182
+ def remove_node(nid)
183
+ node = @nodes.delete(nid)
184
+ unless node
185
+ raise "nid not exist: #{nid}"
186
+ end
187
+ update_active_rsids
188
+ true
189
+ end
190
+
191
+ def update_node_info(nid, address, name, rsids, location)
192
+ node = get_node(nid)
193
+ if node.address == address && node.name == name &&
194
+ node.rsids == rsids && node.location == location
195
+ return false
196
+ end
197
+ @nodes.update(nid, address, name, rsids, location)
198
+ update_active_rsids
199
+ true
200
+ end
201
+
202
+ def get_node(nid)
203
+ node = @nodes.get(nid)
204
+ unless node
205
+ raise "no such node id: #{nid.inspect}"
206
+ end
207
+ node
208
+ end
209
+
210
+ def get_all_nodes
211
+ @nodes.get_all_nodes
212
+ end
213
+
214
+ def get_all_nids
215
+ @nodes.get_all_nids
216
+ end
217
+
218
+ def get_active_rsids
219
+ @active_rsids
220
+ end
221
+
222
+ def include?(nid)
223
+ @nodes.include?(nid)
224
+ end
225
+
226
+ def get_hash
227
+ @nodes.get_hash
228
+ end
229
+
230
+ def to_msgpack(out = '')
231
+ @nodes.to_msgpack(out)
232
+ end
233
+
234
+ def from_msgpack(obj)
235
+ @nodes.from_msgpack(obj)
236
+ update_active_rsids
237
+ self
238
+ end
239
+
240
+ private
241
+ def update_active_rsids
242
+ map = {}
243
+ @nodes.get_all_nodes.each {|node|
244
+ node.rsids.each {|rsid|
245
+ map[rsid] = nil
246
+ }
247
+ }
248
+ @active_rsids = map.keys.sort
249
+ end
250
+ end
251
+
252
+
253
+ end