gizzmo 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +11 -16
- data/VERSION +1 -1
- data/bin/setup_shards +173 -0
- data/lib/gizzard.rb +4 -0
- data/lib/gizzard/commands.rb +286 -152
- data/lib/gizzard/migrator.rb +192 -0
- data/lib/gizzard/nameserver.rb +206 -0
- data/lib/gizzard/shard_template.rb +252 -0
- data/lib/gizzard/thrift.rb +187 -135
- data/lib/gizzard/transformation.rb +230 -0
- data/lib/gizzard/transformation_op.rb +181 -0
- data/lib/gizzard/transformation_scheduler.rb +220 -0
- data/lib/gizzmo.rb +87 -20
- data/test/gizzmo_spec.rb +499 -0
- data/test/nameserver_spec.rb +139 -0
- data/test/scheduler_spec.rb +59 -0
- data/test/shard_template_spec.rb +103 -0
- data/test/spec.opts +7 -0
- data/test/spec_helper.rb +139 -0
- data/test/test_server/.gitignore +13 -0
- data/test/test_server/project/build.properties +8 -0
- data/test/test_server/project/build/Project.scala +13 -0
- data/test/test_server/project/plugins/Plugins.scala +6 -0
- data/test/test_server/src/main/scala/Main.scala +18 -0
- data/test/test_server/src/main/scala/TestServer.scala +247 -0
- data/test/test_server/src/main/thrift/TestServer.thrift +12 -0
- data/test/transformation_spec.rb +181 -0
- metadata +32 -5
- data/gizzmo.gemspec +0 -75
data/lib/gizzard/thrift.rb
CHANGED
@@ -1,177 +1,229 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
require 'vendor/thrift_client/simple'
|
3
2
|
|
4
3
|
module Gizzard
|
5
|
-
|
6
|
-
T = ThriftClient::Simple
|
4
|
+
T = ThriftClient::Simple
|
7
5
|
|
8
|
-
|
9
|
-
|
6
|
+
def self.struct(*args)
|
7
|
+
T::StructType.new(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.list(*args)
|
11
|
+
T::ListType.new(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
GizzardException = T.make_exception(:GizzardException,
|
15
|
+
T::Field.new(:description, T::STRING, 1)
|
16
|
+
)
|
17
|
+
|
18
|
+
ShardId = T.make_struct(:ShardId,
|
19
|
+
T::Field.new(:hostname, T::STRING, 1),
|
20
|
+
T::Field.new(:table_prefix, T::STRING, 2)
|
21
|
+
)
|
22
|
+
|
23
|
+
class ShardId
|
24
|
+
def inspect
|
25
|
+
"#{hostname}/#{table_prefix}"
|
10
26
|
end
|
11
27
|
|
12
|
-
|
13
|
-
|
14
|
-
|
28
|
+
def <=>(o)
|
29
|
+
self.hostname <=> o.hostname
|
30
|
+
end
|
15
31
|
|
16
|
-
|
17
|
-
T::Field.new(:hostname, T::STRING, 1),
|
18
|
-
T::Field.new(:table_prefix, T::STRING, 2)
|
19
|
-
)
|
32
|
+
alias_method :to_unix, :inspect
|
20
33
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
def self.parse(string)
|
35
|
+
new(*string.match("(.*)/(.*)").values_at(1, 2))
|
36
|
+
end
|
37
|
+
end
|
25
38
|
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
ShardInfo = T.make_struct(:ShardInfo,
|
40
|
+
T::Field.new(:id, struct(ShardId), 1),
|
41
|
+
T::Field.new(:class_name, T::STRING, 2),
|
42
|
+
T::Field.new(:source_type, T::STRING, 3),
|
43
|
+
T::Field.new(:destination_type, T::STRING, 4),
|
44
|
+
T::Field.new(:busy, T::I32, 5)
|
45
|
+
)
|
46
|
+
|
47
|
+
class ShardInfo
|
48
|
+
def busy?
|
49
|
+
busy && busy > 0
|
50
|
+
end
|
29
51
|
|
30
|
-
|
52
|
+
def inspect(short = false)
|
53
|
+
"#{id.inspect}" + (busy? ? " (BUSY)" : "")
|
54
|
+
end
|
31
55
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
56
|
+
def to_unix
|
57
|
+
[id.to_unix, class_name, busy? ? "busy" : "ok"].join("\t")
|
35
58
|
end
|
59
|
+
end
|
36
60
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
T::Field.new(:busy, T::I32, 5)
|
43
|
-
)
|
44
|
-
|
45
|
-
class ShardInfo
|
46
|
-
def busy?
|
47
|
-
busy && busy > 0
|
48
|
-
end
|
61
|
+
LinkInfo = T.make_struct(:LinkInfo,
|
62
|
+
T::Field.new(:up_id, struct(ShardId), 1),
|
63
|
+
T::Field.new(:down_id, struct(ShardId), 2),
|
64
|
+
T::Field.new(:weight, T::I32, 3)
|
65
|
+
)
|
49
66
|
|
50
|
-
|
51
|
-
|
52
|
-
|
67
|
+
class LinkInfo
|
68
|
+
def inspect
|
69
|
+
"#{up_id.inspect} -> #{down_id.inspect}" + (weight == 1 ? "" : " <#{weight}>")
|
70
|
+
end
|
53
71
|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
72
|
+
def to_unix
|
73
|
+
[up_id.to_unix, down_id.to_unix, weight].join("\t")
|
57
74
|
end
|
75
|
+
end
|
58
76
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
77
|
+
Forwarding = T.make_struct(:Forwarding,
|
78
|
+
T::Field.new(:table_id, T::I32, 1),
|
79
|
+
T::Field.new(:base_id, T::I64, 2),
|
80
|
+
T::Field.new(:shard_id, struct(ShardId), 3)
|
81
|
+
)
|
64
82
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
83
|
+
class Forwarding
|
84
|
+
def <=>(o)
|
85
|
+
[self.table_id, self.base_id] <=> [o.table_id, o.base_id]
|
86
|
+
end
|
69
87
|
|
70
|
-
|
71
|
-
|
72
|
-
end
|
88
|
+
def inspect
|
89
|
+
"[#{table_id}] #{base_id.to_s(16)} = #{shard_id.inspect}"
|
73
90
|
end
|
91
|
+
end
|
74
92
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
93
|
+
NameServerState = T.make_struct(:NameserverState,
|
94
|
+
T::Field.new(:shards, list(struct(ShardInfo)), 1),
|
95
|
+
T::Field.new(:links, list(struct(LinkInfo)), 2),
|
96
|
+
T::Field.new(:forwardings, list(struct(Forwarding)), 3),
|
97
|
+
T::Field.new(:table_id, T::I32, 4)
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
module HostStatus
|
102
|
+
Normal = 0
|
103
|
+
Offline = 1
|
104
|
+
Blocked = 2
|
105
|
+
end
|
106
|
+
|
107
|
+
Host = T.make_struct(:Host,
|
108
|
+
T::Field.new(:hostname, T::STRING, 1),
|
109
|
+
T::Field.new(:port, T::I32, 2),
|
110
|
+
T::Field.new(:cluster, T::STRING, 3),
|
111
|
+
T::Field.new(:status, T::I32, 4)
|
112
|
+
)
|
113
|
+
|
114
|
+
class Host
|
115
|
+
def inspect
|
116
|
+
"(#{hostname}:#{port} - #{cluster} (#{status})"
|
91
117
|
end
|
118
|
+
end
|
92
119
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
120
|
+
class GizzmoService < T::ThriftService
|
121
|
+
def initialize(host, port, log_path, framed, dry_run = false)
|
122
|
+
super(host, port, framed)
|
123
|
+
@dry = dry_run
|
124
|
+
begin
|
125
|
+
@log = File.open(log_path, "a")
|
126
|
+
rescue
|
127
|
+
STDERR.puts "Error opening log file at #{log_path}. Continuing..."
|
102
128
|
end
|
129
|
+
end
|
103
130
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
rescue ThriftClient::Simple::ThriftException
|
118
|
-
if @dry
|
119
|
-
puts "Skipped reading: #{printable(method_name, args)}"
|
120
|
-
else
|
121
|
-
raise
|
122
|
-
end
|
131
|
+
def _proxy(method_name, *args)
|
132
|
+
cls = self.class.ancestors.find { |cls| cls.respond_to?(:_arg_structs) and cls._arg_structs[method_name.to_sym] }
|
133
|
+
arg_class, rv_class = cls._arg_structs[method_name.to_sym]
|
134
|
+
|
135
|
+
# Writing methods return void. Methods should never both read and write. If this assumption
|
136
|
+
# is violated in the future, dry-run will fail!!
|
137
|
+
is_writing_method = rv_class._fields.first.type == ThriftClient::Simple::VOID
|
138
|
+
if @dry && is_writing_method
|
139
|
+
puts "Skipped writing: #{printable(method_name, args)}"
|
140
|
+
else
|
141
|
+
@log.puts printable(method_name, args, true)
|
142
|
+
super(method_name, *args)
|
123
143
|
end
|
144
|
+
end
|
124
145
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
146
|
+
def printable(method_name, args, timestamp = false)
|
147
|
+
ts = timestamp ? "#{Time.now}\t" : ""
|
148
|
+
"#{ts}#{method_name}(#{args.map{|a| a.inspect}.join(', ')})"
|
129
149
|
end
|
150
|
+
end
|
130
151
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
thrift_method :get_shard, struct(ShardInfo), field(:id, struct(ShardId), 1), :throws => exception(ShardException)
|
152
|
+
class Manager < GizzmoService
|
153
|
+
thrift_method :reload_config, void, :throws => exception(GizzardException)
|
154
|
+
thrift_method :rebuild_schema, void, :throws => exception(GizzardException)
|
135
155
|
|
136
|
-
|
137
|
-
thrift_method :remove_link, void, field(:up_id, struct(ShardId), 1), field(:down_id, struct(ShardId), 2), :throws => exception(ShardException)
|
156
|
+
thrift_method :find_current_forwarding, struct(ShardInfo), field(:table_id, i32, 1), field(:id, i64, 2), :throws => exception(GizzardException)
|
138
157
|
|
139
|
-
thrift_method :list_upward_links, list(struct(LinkInfo)), field(:id, struct(ShardId), 1), :throws => exception(ShardException)
|
140
|
-
thrift_method :list_downward_links, list(struct(LinkInfo)), field(:id, struct(ShardId), 1), :throws => exception(ShardException)
|
141
158
|
|
142
|
-
|
159
|
+
# Shard Tree Management
|
143
160
|
|
144
|
-
|
145
|
-
|
161
|
+
thrift_method :create_shard, void, field(:shard, struct(ShardInfo), 1), :throws => exception(GizzardException)
|
162
|
+
thrift_method :delete_shard, void, field(:id, struct(ShardId), 1), :throws => exception(GizzardException)
|
146
163
|
|
147
|
-
|
148
|
-
|
149
|
-
thrift_method :remove_forwarding, void, field(:forwarding, struct(Forwarding), 1), :throws => exception(ShardException)
|
164
|
+
thrift_method :add_link, void, field(:up_id, struct(ShardId), 1), field(:down_id, struct(ShardId), 2), field(:weight, i32, 3), :throws => exception(GizzardException)
|
165
|
+
thrift_method :remove_link, void, field(:up_id, struct(ShardId), 1), field(:down_id, struct(ShardId), 2), :throws => exception(GizzardException)
|
150
166
|
|
151
|
-
|
152
|
-
|
167
|
+
thrift_method :set_forwarding, void, field(:forwarding, struct(Forwarding), 1), :throws => exception(GizzardException)
|
168
|
+
thrift_method :replace_forwarding, void, field(:old_id, struct(ShardId), 1), field(:new_id, struct(ShardId), 2), :throws => exception(GizzardException)
|
169
|
+
thrift_method :remove_forwarding, void, field(:forwarding, struct(Forwarding), 1), :throws => exception(GizzardException)
|
153
170
|
|
154
|
-
|
155
|
-
|
171
|
+
thrift_method :get_shard, struct(ShardInfo), field(:id, struct(ShardId), 1), :throws => exception(GizzardException)
|
172
|
+
thrift_method :shards_for_hostname, list(struct(ShardInfo)), field(:hostname, string, 1), :throws => exception(GizzardException)
|
173
|
+
thrift_method :get_busy_shards, list(struct(ShardInfo)), :throws => exception(GizzardException)
|
156
174
|
|
157
|
-
|
175
|
+
thrift_method :list_upward_links, list(struct(LinkInfo)), field(:id, struct(ShardId), 1), :throws => exception(GizzardException)
|
176
|
+
thrift_method :list_downward_links, list(struct(LinkInfo)), field(:id, struct(ShardId), 1), :throws => exception(GizzardException)
|
177
|
+
thrift_method :get_forwarding, struct(Forwarding), field(:table_id, i32, 1), field(:base_id, i64, 2), :throws => exception(GizzardException)
|
178
|
+
thrift_method :get_forwarding_for_shard, struct(Forwarding), field(:shard_id, struct(ShardId), 1), :throws => exception(GizzardException)
|
179
|
+
thrift_method :get_forwardings, list(struct(Forwarding)), :throws => exception(GizzardException)
|
158
180
|
|
159
|
-
|
160
|
-
thrift_method :get_busy_shards, list(struct(ShardInfo)), :throws => exception(ShardException)
|
161
|
-
thrift_method :list_hostnames, list(string), :throws => exception(ShardException)
|
181
|
+
thrift_method :list_hostnames, list(string), :throws => exception(GizzardException)
|
162
182
|
|
163
|
-
|
164
|
-
|
183
|
+
thrift_method :mark_shard_busy, void, field(:id, struct(ShardId), 1), field(:busy, i32, 2), :throws => exception(GizzardException)
|
184
|
+
thrift_method :copy_shard, void, field(:source_id, struct(ShardId), 1), field(:destination_id, struct(ShardId), 2), :throws => exception(GizzardException)
|
165
185
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
186
|
+
thrift_method :dump_nameserver, struct(NameServerState), field(:table_id, i32, 1), :throws => exception(GizzardException)
|
187
|
+
|
188
|
+
|
189
|
+
# Job Scheduler Management
|
190
|
+
|
191
|
+
thrift_method :retry_errors, void
|
192
|
+
thrift_method :stop_writes, void
|
193
|
+
thrift_method :resume_writes, void
|
194
|
+
|
195
|
+
thrift_method :retry_errors_for, void, field(:priority, i32, 1)
|
196
|
+
thrift_method :stop_writes_for, void, field(:priority, i32, 1)
|
197
|
+
thrift_method :resume_writes_for, void, field(:priority, i32, 1)
|
198
|
+
|
199
|
+
thrift_method :is_writing, bool, field(:priority, i32, 1)
|
200
|
+
|
201
|
+
|
202
|
+
# Remote Host Cluster Management
|
203
|
+
|
204
|
+
thrift_method :add_remote_host, void, field(:host, struct(Host), 1)#, :throws => exception(GizzardException)
|
205
|
+
thrift_method :remove_remote_host, void, field(:hostname, string, 1), field(:port, i32, 2), :throws => exception(GizzardException)
|
206
|
+
thrift_method :set_remote_host_status, void, field(:hostname, string, 1), field(:port, i32, 2), field(:status, i32, 3), :throws => exception(GizzardException)
|
207
|
+
thrift_method :set_remote_cluster_status, void, field(:cluster, string, 1), field(:status, i32, 2), :throws => exception(GizzardException)
|
208
|
+
|
209
|
+
thrift_method :get_remote_host, struct(Host), field(:hostname, string, 1), field(:port, i32, 2), :throws => exception(GizzardException)
|
210
|
+
thrift_method :list_remote_clusters, list(string), :throws => exception(GizzardException)
|
211
|
+
thrift_method :list_remote_hosts, list(struct(Host)), :throws => exception(GizzardException)
|
212
|
+
thrift_method :list_remote_hosts_in_cluster, list(struct(Host)), field(:cluster, string, 1), :throws => exception(GizzardException)
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
|
217
|
+
Job = T.make_struct(:Job,
|
218
|
+
T::Field.new(:priority, T::I32, 1),
|
219
|
+
T::Field.new(:contents, T::STRING, 2)
|
220
|
+
)
|
221
|
+
|
222
|
+
JobException = T.make_exception(:JobException,
|
223
|
+
T::Field.new(:description, T::STRING, 1)
|
224
|
+
)
|
225
|
+
|
226
|
+
class JobInjector < GizzmoService
|
227
|
+
thrift_method :inject_jobs, void, field(:priority, list(struct(Job)), 1), :throws => exception(JobException)
|
176
228
|
end
|
177
229
|
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
module Gizzard
|
2
|
+
class Transformation
|
3
|
+
require 'gizzard/transformation_op'
|
4
|
+
require 'gizzard/transformation_scheduler'
|
5
|
+
|
6
|
+
OP_NAMES = {
|
7
|
+
Op::RemoveForwarding => "remove_forwarding",
|
8
|
+
Op::RemoveLink => "remove_link",
|
9
|
+
Op::DeleteShard => "delete_shard",
|
10
|
+
Op::CreateShard => "create_shard",
|
11
|
+
Op::AddLink => "add_link",
|
12
|
+
Op::SetForwarding => "set_forwarding",
|
13
|
+
Op::CopyShard => "copy_shard"
|
14
|
+
}
|
15
|
+
|
16
|
+
OP_INVERSES = {
|
17
|
+
Op::AddLink => Op::RemoveLink,
|
18
|
+
Op::CreateShard => Op::DeleteShard,
|
19
|
+
Op::SetForwarding => Op::RemoveForwarding
|
20
|
+
}
|
21
|
+
|
22
|
+
OP_INVERSES.keys.each {|k| v = OP_INVERSES[k]; OP_INVERSES[v] = k }
|
23
|
+
|
24
|
+
OP_PRIORITIES = {
|
25
|
+
Op::CreateShard => 1,
|
26
|
+
Op::AddLink => 2,
|
27
|
+
Op::SetForwarding => 3,
|
28
|
+
Op::RemoveForwarding => 4,
|
29
|
+
Op::RemoveLink => 5,
|
30
|
+
Op::DeleteShard => 6,
|
31
|
+
Op::CopyShard => 7
|
32
|
+
}
|
33
|
+
|
34
|
+
DEFAULT_DEST_WRAPPER = 'WriteOnlyShard'
|
35
|
+
|
36
|
+
attr_reader :from, :to
|
37
|
+
|
38
|
+
def initialize(from_template, to_template, copy_dest_wrapper = nil)
|
39
|
+
copy_dest_wrapper ||= DEFAULT_DEST_WRAPPER
|
40
|
+
|
41
|
+
unless Shard::VIRTUAL_SHARD_TYPES.include? copy_dest_wrapper
|
42
|
+
raise ArgumentError, "#{copy_dest_wrapper} is not a valid virtual shard type."
|
43
|
+
end
|
44
|
+
|
45
|
+
@from = from_template
|
46
|
+
@to = to_template
|
47
|
+
@copy_dest_wrapper = copy_dest_wrapper
|
48
|
+
|
49
|
+
if copies_required? && copy_source.nil?
|
50
|
+
raise ArgumentError, "copy required without a valid copy source"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def bind(base_name, forwardings_to_shards)
|
55
|
+
raise ArgumentError unless forwardings_to_shards.is_a? Hash
|
56
|
+
|
57
|
+
forwardings_to_shards.map do |forwarding, shard|
|
58
|
+
BoundTransformation.new(self, base_name, forwarding, shard)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
op_inspect = operations.inject({}) do |h, (phase, ops)|
|
64
|
+
h.update phase => ops.map {|job| " #{job.inspect}" }.join("\n")
|
65
|
+
end
|
66
|
+
|
67
|
+
prepare_inspect = op_inspect[:prepare].empty? ? "" : " PREPARE\n#{op_inspect[:prepare]}\n"
|
68
|
+
copy_inspect = op_inspect[:copy].empty? ? "" : " COPY\n#{op_inspect[:copy]}\n"
|
69
|
+
cleanup_inspect = op_inspect[:cleanup].empty? ? "" : " CLEANUP\n#{op_inspect[:cleanup]}\n"
|
70
|
+
|
71
|
+
op_inspect = [prepare_inspect, copy_inspect, cleanup_inspect].join
|
72
|
+
|
73
|
+
"#{from.inspect} => #{to.inspect} :\n#{op_inspect}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def operations
|
77
|
+
return @operations if @operations
|
78
|
+
|
79
|
+
log = []
|
80
|
+
log.concat destroy_tree(from) if from
|
81
|
+
log.concat create_tree(to) if to
|
82
|
+
|
83
|
+
# compact
|
84
|
+
log = collapse_jobs(log)
|
85
|
+
|
86
|
+
@operations = expand_jobs(log)
|
87
|
+
|
88
|
+
@operations.each do |(phase, jobs)|
|
89
|
+
jobs.sort!
|
90
|
+
end
|
91
|
+
|
92
|
+
@operations
|
93
|
+
end
|
94
|
+
|
95
|
+
def collapse_jobs(jobs)
|
96
|
+
jobs.reject do |job1|
|
97
|
+
jobs.find do |job2|
|
98
|
+
job1.inverse? job2
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def expand_jobs(jobs)
|
104
|
+
expanded = jobs.inject({:prepare => [], :copy => [], :cleanup => []}) do |ops, job|
|
105
|
+
job_ops = job.expand(self.copy_source, involved_in_copy?(job.template), @copy_dest_wrapper)
|
106
|
+
ops.update(job_ops) {|k,a,b| a + b }
|
107
|
+
end
|
108
|
+
|
109
|
+
# if there are no copies that need to take place, we can do all
|
110
|
+
# nameserver changes in one step
|
111
|
+
if expanded[:copy].empty?
|
112
|
+
expanded[:prepare].concat expanded[:cleanup]
|
113
|
+
expanded[:cleanup] = []
|
114
|
+
end
|
115
|
+
|
116
|
+
expanded
|
117
|
+
end
|
118
|
+
|
119
|
+
def copies_required?
|
120
|
+
return @copies_required unless @copies_required.nil?
|
121
|
+
@copies_required = !from.nil? &&
|
122
|
+
to.concrete_descendants.reject {|d| from.shared_host? d }.length > 0
|
123
|
+
end
|
124
|
+
|
125
|
+
def involved_in_copy?(template)
|
126
|
+
copy_source?(template) || copy_destination?(template)
|
127
|
+
end
|
128
|
+
|
129
|
+
def copy_destination?(template)
|
130
|
+
copies_required? && template.concrete? && !from.shared_host?(template)
|
131
|
+
end
|
132
|
+
|
133
|
+
def copy_source?(template)
|
134
|
+
copies_required? && !!from.copy_sources.find {|s| s.shard_eql? template }
|
135
|
+
end
|
136
|
+
|
137
|
+
def copy_source
|
138
|
+
from.copy_sources.first if copies_required?
|
139
|
+
end
|
140
|
+
|
141
|
+
def create_tree(root)
|
142
|
+
jobs = visit_collect(root) do |parent, child|
|
143
|
+
[Op::CreateShard.new(child), Op::AddLink.new(parent, child)]
|
144
|
+
end
|
145
|
+
[Op::CreateShard.new(root)].concat jobs << Op::SetForwarding.new(root)
|
146
|
+
end
|
147
|
+
|
148
|
+
def destroy_tree(root)
|
149
|
+
jobs = visit_collect(root) do |parent, child|
|
150
|
+
[Op::RemoveLink.new(parent, child), Op::DeleteShard.new(child)]
|
151
|
+
end
|
152
|
+
[Op::RemoveForwarding.new(root)].concat jobs << Op::DeleteShard.new(root)
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def visit_collect(parent, &block)
|
158
|
+
parent.children.inject([]) do |acc, child|
|
159
|
+
visit_collect(child, &block).concat(acc.concat(block.call(parent, child)))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
class BoundTransformation
|
166
|
+
attr_reader :transformation, :base_name, :forwarding, :shard
|
167
|
+
|
168
|
+
def from; transformation.from end
|
169
|
+
def to; transformation.to end
|
170
|
+
|
171
|
+
def initialize(transformation, base_name, forwarding, shard)
|
172
|
+
@transformation = transformation
|
173
|
+
@base_name = base_name
|
174
|
+
@forwarding = forwarding
|
175
|
+
@shard = shard
|
176
|
+
|
177
|
+
@table_id = forwarding.table_id
|
178
|
+
@base_id = forwarding.base_id
|
179
|
+
@enum = shard.enumeration
|
180
|
+
@table_prefix = Shard.canonical_table_prefix(@enum, @table_id, base_name)
|
181
|
+
@translations = shard.canonical_shard_id_map(base_name, @table_id, @enum)
|
182
|
+
end
|
183
|
+
|
184
|
+
def prepare!(nameserver)
|
185
|
+
apply_ops(nameserver, transformation.operations[:prepare])
|
186
|
+
end
|
187
|
+
|
188
|
+
def copy_required?
|
189
|
+
!transformation.operations[:copy].empty?
|
190
|
+
end
|
191
|
+
|
192
|
+
def copy!(nameserver)
|
193
|
+
apply_ops(nameserver, transformation.operations[:copy])
|
194
|
+
end
|
195
|
+
|
196
|
+
def cleanup!(nameserver)
|
197
|
+
apply_ops(nameserver, transformation.operations[:cleanup])
|
198
|
+
end
|
199
|
+
|
200
|
+
def involved_shards(phase = :copy)
|
201
|
+
transformation.operations[phase].map do |op|
|
202
|
+
op.involved_shards(@table_prefix, @translations)
|
203
|
+
end.flatten.compact.uniq
|
204
|
+
end
|
205
|
+
|
206
|
+
def involved_hosts(phase = :copy)
|
207
|
+
involved_shards(phase).map {|s| s.hostname }.uniq
|
208
|
+
end
|
209
|
+
|
210
|
+
def inspect
|
211
|
+
"#{@forwarding.inspect}: #{from.inspect} => #{to.inspect}"
|
212
|
+
end
|
213
|
+
|
214
|
+
def copy_descs
|
215
|
+
transformation.operations[:copy].map do |copy|
|
216
|
+
from_id = copy.from.to_shard_id(@table_prefix, @translations)
|
217
|
+
to_id = copy.to.to_shard_id(@table_prefix, @translations)
|
218
|
+
"#{from_id.inspect} -> #{to_id.inspect}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def apply_ops(nameserver, ops)
|
225
|
+
ops.each do |op|
|
226
|
+
op.apply(nameserver, @table_id, @base_id, @table_prefix, @translations)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|