bricolage-streamingload 0.7.1 → 0.8.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.
- checksums.yaml +4 -4
- data/bin/bricolage-streaming-loader +2 -2
- data/lib/bricolage/sqsmock.rb +0 -1
- data/lib/bricolage/streamingload/dispatcher.rb +2 -1
- data/lib/bricolage/streamingload/job.rb +387 -0
- data/lib/bricolage/streamingload/{loaderparams.rb → jobparams.rb} +14 -39
- data/lib/bricolage/streamingload/manifest.rb +7 -1
- data/lib/bricolage/streamingload/objectbuffer.rb +0 -3
- data/lib/bricolage/streamingload/task.rb +5 -68
- data/lib/bricolage/streamingload/{loaderservice.rb → taskhandler.rb} +102 -61
- data/lib/bricolage/streamingload/version.rb +1 -1
- data/test/streamingload/test_dispatcher.rb +6 -6
- data/test/streamingload/test_job.rb +438 -0
- metadata +8 -9
- data/lib/bricolage/nulllogger.rb +0 -20
- data/lib/bricolage/snsdatasource.rb +0 -40
- data/lib/bricolage/streamingload/loader.rb +0 -158
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bricolage/context'
|
2
2
|
require 'bricolage/sqsdatasource'
|
3
3
|
require 'bricolage/streamingload/task'
|
4
|
-
require 'bricolage/streamingload/
|
4
|
+
require 'bricolage/streamingload/job'
|
5
5
|
require 'bricolage/streamingload/alertinglogger'
|
6
6
|
require 'bricolage/logger'
|
7
7
|
require 'bricolage/exception'
|
@@ -13,60 +13,60 @@ module Bricolage
|
|
13
13
|
|
14
14
|
module StreamingLoad
|
15
15
|
|
16
|
-
class
|
16
|
+
class TaskHandler < SQSDataSource::MessageHandler
|
17
17
|
|
18
|
-
def
|
19
|
-
opts =
|
18
|
+
def TaskHandler.main
|
19
|
+
opts = TaskHandlerOptions.new(ARGV)
|
20
20
|
opts.parse
|
21
|
-
unless opts.rest_arguments.size
|
21
|
+
unless opts.rest_arguments.size <= 1
|
22
22
|
$stderr.puts opts.usage
|
23
23
|
exit 1
|
24
24
|
end
|
25
|
-
config_path
|
25
|
+
config_path = opts.rest_arguments[0] || "#{opts.working_dir}/config/#{opts.environment}/streamingload.yml"
|
26
26
|
config = YAML.load(File.read(config_path))
|
27
|
+
|
27
28
|
logger = opts.log_file_path ? new_logger(opts.log_file_path, config) : nil
|
28
29
|
ctx = Context.for_application(opts.working_dir, environment: opts.environment, logger: logger)
|
29
|
-
|
30
|
+
|
31
|
+
ctl_ds = ctx.get_data_source('sql', config.fetch('ctl-postgres-ds', 'db_ctl'))
|
30
32
|
task_queue = ctx.get_data_source('sqs', config.fetch('task-queue-ds', 'sqs_task'))
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
)
|
38
|
-
end
|
33
|
+
service_logger =
|
34
|
+
if config.key?('alert-level')
|
35
|
+
new_alerting_logger(ctx, config)
|
36
|
+
else
|
37
|
+
ctx.logger
|
38
|
+
end
|
39
39
|
|
40
|
-
|
40
|
+
task_handler = new(
|
41
41
|
context: ctx,
|
42
|
-
|
43
|
-
data_source: redshift_ds,
|
42
|
+
ctl_ds: ctl_ds,
|
44
43
|
task_queue: task_queue,
|
45
44
|
working_dir: opts.working_dir,
|
46
|
-
logger:
|
45
|
+
logger: service_logger,
|
46
|
+
job_class: opts.noop? ? NoopJob : Job
|
47
47
|
)
|
48
48
|
|
49
49
|
if opts.task_id
|
50
50
|
# Single task mode
|
51
|
-
|
51
|
+
task_handler.execute_task_by_id opts.task_id, force: opts.force?
|
52
52
|
else
|
53
53
|
# Server mode
|
54
54
|
Process.daemon(true) if opts.daemon?
|
55
55
|
Dir.chdir '/'
|
56
56
|
create_pid_file opts.pid_file_path if opts.pid_file_path
|
57
57
|
begin
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
service_logger.info "*** bricolage-streaming-loader started: pid=#{$$}"
|
59
|
+
task_handler.event_loop
|
60
|
+
service_logger.info "*** bricolage-streaming-loader shutdown gracefully: pid=#{$$}"
|
61
61
|
rescue Exception => ex
|
62
|
-
|
63
|
-
|
62
|
+
service_logger.exception(ex)
|
63
|
+
service_logger.error "*** bricolage-streaming-loader abort: pid=#{$$}"
|
64
64
|
raise
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
def
|
69
|
+
def TaskHandler.new_logger(path, config)
|
70
70
|
Logger.new(
|
71
71
|
device: path,
|
72
72
|
rotation_period: config.fetch('log-rotation-period', 'daily'),
|
@@ -74,7 +74,15 @@ module Bricolage
|
|
74
74
|
)
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
77
|
+
def TaskHandler.new_alerting_logger(ctx, config)
|
78
|
+
AlertingLogger.new(
|
79
|
+
logger: ctx.logger,
|
80
|
+
sns_datasource: ctx.get_data_source('sns', config.fetch('sns-ds', 'sns')),
|
81
|
+
alert_level: config.fetch('alert-level', 'warn')
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def TaskHandler.create_pid_file(path)
|
78
86
|
File.open(path, 'w') {|f|
|
79
87
|
f.puts $$
|
80
88
|
}
|
@@ -82,76 +90,91 @@ module Bricolage
|
|
82
90
|
# ignore
|
83
91
|
end
|
84
92
|
|
85
|
-
def initialize(context:,
|
93
|
+
def initialize(context:, ctl_ds:, task_queue:, working_dir:, logger:, job_class: Job)
|
86
94
|
@ctx = context
|
87
|
-
@ctl_ds =
|
88
|
-
@ds = data_source
|
95
|
+
@ctl_ds = ctl_ds
|
89
96
|
@task_queue = task_queue
|
90
97
|
@working_dir = working_dir
|
91
98
|
@logger = logger
|
99
|
+
@job_class = job_class
|
92
100
|
end
|
93
101
|
|
94
102
|
attr_reader :logger
|
95
103
|
|
96
|
-
def
|
97
|
-
@
|
98
|
-
|
99
|
-
|
100
|
-
def execute_task_by_id(task_id)
|
101
|
-
execute_task load_task(task_id)
|
104
|
+
def execute_task_by_id(task_id, force: false)
|
105
|
+
job = @job_class.new(context: @ctx, ctl_ds: @ctl_ds, task_id: task_id, force: force, logger: @logger)
|
106
|
+
job.execute(fail_fast: true)
|
102
107
|
end
|
103
108
|
|
104
|
-
def
|
105
|
-
@
|
109
|
+
def event_loop
|
110
|
+
@task_queue.handle_messages(handler: self, message_class: Task)
|
106
111
|
end
|
107
112
|
|
108
113
|
# message handler
|
109
|
-
def handle_unknown(
|
110
|
-
@logger.warn "unknown task: #{
|
111
|
-
@task_queue.delete_message
|
114
|
+
def handle_unknown(t)
|
115
|
+
@logger.warn "unknown task: #{t.message_body}"
|
116
|
+
@task_queue.delete_message t
|
112
117
|
end
|
113
118
|
|
114
119
|
# message handler
|
115
|
-
def handle_streaming_load_v3(
|
120
|
+
def handle_streaming_load_v3(t)
|
116
121
|
Dir.chdir(@working_dir) {
|
117
|
-
|
118
|
-
if
|
119
|
-
|
120
|
-
@logger.info "skip disabled task: task_id=#{task.id}"
|
121
|
-
return
|
122
|
+
job = @job_class.new(context: @ctx, ctl_ds: @ctl_ds, task_id: t.task_id, force: t.force?, logger: @logger)
|
123
|
+
if job.execute
|
124
|
+
@task_queue.delete_message(t)
|
122
125
|
end
|
123
|
-
execute_task(loadtask)
|
124
|
-
# Do not use async delete
|
125
|
-
@task_queue.delete_message(task)
|
126
126
|
}
|
127
|
-
rescue => ex
|
127
|
+
rescue Exception => ex
|
128
128
|
@logger.exception ex
|
129
129
|
end
|
130
130
|
|
131
|
-
def
|
132
|
-
@
|
133
|
-
|
134
|
-
|
131
|
+
def job_class
|
132
|
+
@job_class ||= Job
|
133
|
+
end
|
134
|
+
|
135
|
+
attr_writer :job_class
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
class NoopJob
|
141
|
+
|
142
|
+
def initialize(context:, ctl_ds:, task_id:, force: false, logger:)
|
143
|
+
@ctx = context
|
144
|
+
@ctl_ds = ctl_ds
|
145
|
+
@task_id = task_id
|
146
|
+
@force = force
|
147
|
+
@logger = logger
|
148
|
+
end
|
149
|
+
|
150
|
+
def execute(fail_fast: false)
|
151
|
+
@logger.info "execute: fail_fast=#{fail_fast}"
|
152
|
+
execute_task
|
153
|
+
true
|
154
|
+
end
|
155
|
+
|
156
|
+
def execute_task
|
157
|
+
@logger.info "execute_task: task_id=#{@task_id} force=#{@force} ctx=#{@ctx.home_path} ctl_ds=#{@ctl_ds.name} dir=#{@working_dir}"
|
135
158
|
end
|
136
159
|
|
137
160
|
end
|
138
161
|
|
139
|
-
|
162
|
+
|
163
|
+
class TaskHandlerOptions
|
140
164
|
|
141
165
|
def initialize(argv)
|
142
166
|
@argv = argv
|
143
|
-
@task_id = nil
|
144
167
|
@environment = Context::DEFAULT_ENV
|
145
168
|
@daemon = false
|
146
169
|
@log_file_path = nil
|
147
170
|
@pid_file_path = nil
|
148
171
|
@working_dir = Dir.getwd
|
172
|
+
@task_id = nil
|
173
|
+
@force = false
|
174
|
+
@noop = false
|
149
175
|
@rest_arguments = nil
|
150
176
|
|
151
177
|
@opts = opts = OptionParser.new("Usage: #{$0} CONFIG_PATH")
|
152
|
-
opts.on('--task-id=ID', 'Execute oneshot load task (implicitly disables daemon mode).') {|task_id|
|
153
|
-
@task_id = task_id
|
154
|
-
}
|
155
178
|
opts.on('-e', '--environment=NAME', "Sets execution environment [default: #{@environment}]") {|env|
|
156
179
|
@environment = env
|
157
180
|
}
|
@@ -167,6 +190,15 @@ module Bricolage
|
|
167
190
|
opts.on('--working-dir=PATH', "Loader working directory. [default: #{@working_dir}]") {|path|
|
168
191
|
@working_dir = path
|
169
192
|
}
|
193
|
+
opts.on('--task-id=ID', 'Execute oneshot load task (implicitly disables daemon mode).') {|task_id|
|
194
|
+
@task_id = task_id
|
195
|
+
}
|
196
|
+
opts.on('--force', 'Disables loaded check.') {|path|
|
197
|
+
@force = true
|
198
|
+
}
|
199
|
+
opts.on('--noop', 'Does not execute tasks.') {
|
200
|
+
@noop = true
|
201
|
+
}
|
170
202
|
opts.on('--help', 'Prints this message and quit.') {
|
171
203
|
puts opts.help
|
172
204
|
exit 0
|
@@ -190,7 +222,6 @@ module Bricolage
|
|
190
222
|
|
191
223
|
attr_reader :rest_arguments
|
192
224
|
|
193
|
-
attr_reader :task_id
|
194
225
|
attr_reader :environment
|
195
226
|
|
196
227
|
def daemon?
|
@@ -201,6 +232,16 @@ module Bricolage
|
|
201
232
|
attr_reader :pid_file_path
|
202
233
|
attr_reader :working_dir
|
203
234
|
|
235
|
+
attr_reader :task_id
|
236
|
+
|
237
|
+
def force?
|
238
|
+
@force
|
239
|
+
end
|
240
|
+
|
241
|
+
def noop?
|
242
|
+
@noop
|
243
|
+
end
|
244
|
+
|
204
245
|
end
|
205
246
|
|
206
247
|
end
|
@@ -87,9 +87,9 @@ module Bricolage
|
|
87
87
|
|
88
88
|
# Object Buffer
|
89
89
|
assert_equal [], unassigned_objects(ctl_ds)
|
90
|
-
task =
|
91
|
-
assert_equal 'testschema', task.
|
92
|
-
assert_equal 'desttable', task.
|
90
|
+
task = Job::ControlConnection.open(ctl_ds) {|ctl| ctl.load_task(task_id) }
|
91
|
+
assert_equal 'testschema', task.schema_name
|
92
|
+
assert_equal 'desttable', task.table_name
|
93
93
|
assert_equal 10, task.object_urls.size
|
94
94
|
end
|
95
95
|
|
@@ -180,9 +180,9 @@ module Bricolage
|
|
180
180
|
|
181
181
|
# Object Buffer
|
182
182
|
assert_equal [], unassigned_table_objects(ctl_ds, 'testschema.bbb')
|
183
|
-
task =
|
184
|
-
assert_equal 'testschema', task.
|
185
|
-
assert_equal 'bbb', task.
|
183
|
+
task = Job::ControlConnection.open(ctl_ds) {|ctl| ctl.load_task(task_id) }
|
184
|
+
assert_equal 'testschema', task.schema_name
|
185
|
+
assert_equal 'bbb', task.table_name
|
186
186
|
assert_equal 2, task.object_urls.size
|
187
187
|
end
|
188
188
|
|