bricolage 5.25.1 → 5.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bricolage/version.rb +1 -1
- data/test/home/Gemfile.lock +18 -4
- data/test/home/config/development/database.yml +5 -18
- metadata +1 -41
- data/jobclass/my-export.rb +0 -40
- data/jobclass/my-import-delta.rb +0 -66
- data/jobclass/my-import.rb +0 -84
- data/jobclass/my-migrate.rb +0 -116
- data/jobclass/redis-export.rb +0 -42
- data/lib/bricolage/mysqldatasource.rb +0 -363
- data/lib/bricolage/redisdatasource.rb +0 -153
- data/libexec/mys3dump.jar +0 -0
- data/libexec/sqldump +0 -9
- data/libexec/sqldump.Darwin +0 -0
- data/libexec/sqldump.Linux +0 -0
- data/test/home/config/streamingload.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7331a52e0d7ce7f6e37a033455c4bbd0cd20ec98
|
4
|
+
data.tar.gz: 8c794e3df428209e2467e8edd7dca371d109eb17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f58e1983098afd0dace568e5f32e415e12e50e39be6a04502ef9cab9347c1325370c4304de4859d481d2b1ad5246aeaf816bc7971ca7f5adb0747f1af47ad19
|
7
|
+
data.tar.gz: c9831a9e1c8f5694654e69ed695f8886db98b3fe46e9a8ef7384a9f120bd8890e2557a14f90318665f0ad713cb087b6ce1ae103e9943c55aef98eb71ac684ff8
|
data/lib/bricolage/version.rb
CHANGED
data/test/home/Gemfile.lock
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../../../bricolage-mysql
|
3
|
+
specs:
|
4
|
+
bricolage-mysql (5.26.0)
|
5
|
+
bricolage (>= 5.26.0)
|
6
|
+
mysql2
|
7
|
+
|
8
|
+
PATH
|
9
|
+
remote: ../../../bricolage-redis
|
10
|
+
specs:
|
11
|
+
bricolage-redis (5.26.0)
|
12
|
+
bricolage (>= 5.26.0)
|
13
|
+
redis (~> 3)
|
14
|
+
|
1
15
|
PATH
|
2
16
|
remote: ../..
|
3
17
|
specs:
|
4
|
-
bricolage (5.
|
18
|
+
bricolage (5.26.0)
|
5
19
|
aws-sdk-s3 (~> 1)
|
6
20
|
aws-sdk-sns (~> 1)
|
7
|
-
mysql2
|
8
21
|
pg (~> 0.18.0)
|
9
|
-
redis (>= 3.0.0)
|
10
22
|
|
11
23
|
GEM
|
12
24
|
remote: https://rubygems.org/
|
@@ -35,13 +47,15 @@ GEM
|
|
35
47
|
pry (0.11.3)
|
36
48
|
coderay (~> 1.1.0)
|
37
49
|
method_source (~> 0.9.0)
|
38
|
-
redis (
|
50
|
+
redis (3.3.3)
|
39
51
|
|
40
52
|
PLATFORMS
|
41
53
|
ruby
|
42
54
|
|
43
55
|
DEPENDENCIES
|
44
56
|
bricolage!
|
57
|
+
bricolage-mysql!
|
58
|
+
bricolage-redis!
|
45
59
|
pry
|
46
60
|
|
47
61
|
BUNDLED WITH
|
@@ -79,29 +79,16 @@ mysql_summary:
|
|
79
79
|
password: <%= password 'mysql_shared_work_readonly' %>
|
80
80
|
encoding: utf8
|
81
81
|
|
82
|
-
sqs_preproc:
|
83
|
-
type: sqs
|
84
|
-
url: "https://sqs.ap-northeast-1.amazonaws.com/789035092620/bricolage-preproc-dev"
|
85
|
-
max_number_of_messages: 10
|
86
|
-
visibility_timeout: 60
|
87
|
-
|
88
|
-
sqs_dispatch:
|
89
|
-
type: sqs
|
90
|
-
url: "https://sqs.ap-northeast-1.amazonaws.com/789035092620/log-stream-dev"
|
91
|
-
max_number_of_messages: 10
|
92
|
-
visibility_timeout: 60
|
93
|
-
|
94
|
-
sqs_task:
|
95
|
-
type: sqs
|
96
|
-
url: "https://sqs.ap-northeast-1.amazonaws.com/789035092620/bricolage-load-tasks-dev"
|
97
|
-
max_number_of_messages: 1
|
98
|
-
visibility_timeout: 180
|
99
|
-
|
100
82
|
sns:
|
101
83
|
type: sns
|
102
84
|
region: "ap-northeast-1"
|
103
85
|
topic_arn: "arn:aws:sns:ap-northeast-1:789035092620:dwh-service-notification"
|
104
86
|
|
87
|
+
redis:
|
88
|
+
type: redis
|
89
|
+
host: localhost
|
90
|
+
port: 6379
|
91
|
+
|
105
92
|
td:
|
106
93
|
type: td
|
107
94
|
database: logs
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bricolage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Minero Aoki
|
@@ -52,34 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: mysql2
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: redis
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 3.0.0
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 3.0.0
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: test-unit
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,14 +126,9 @@ files:
|
|
154
126
|
- jobclass/insert-delta.rb
|
155
127
|
- jobclass/insert.rb
|
156
128
|
- jobclass/load.rb
|
157
|
-
- jobclass/my-export.rb
|
158
|
-
- jobclass/my-import-delta.rb
|
159
|
-
- jobclass/my-import.rb
|
160
|
-
- jobclass/my-migrate.rb
|
161
129
|
- jobclass/noop.rb
|
162
130
|
- jobclass/rebuild-drop.rb
|
163
131
|
- jobclass/rebuild-rename.rb
|
164
|
-
- jobclass/redis-export.rb
|
165
132
|
- jobclass/s3-put.rb
|
166
133
|
- jobclass/sql.rb
|
167
134
|
- jobclass/streaming_load.rb
|
@@ -190,11 +157,9 @@ files:
|
|
190
157
|
- lib/bricolage/logger.rb
|
191
158
|
- lib/bricolage/loglocator.rb
|
192
159
|
- lib/bricolage/loglocatorbuilder.rb
|
193
|
-
- lib/bricolage/mysqldatasource.rb
|
194
160
|
- lib/bricolage/parameters.rb
|
195
161
|
- lib/bricolage/postgresconnection.rb
|
196
162
|
- lib/bricolage/psqldatasource.rb
|
197
|
-
- lib/bricolage/redisdatasource.rb
|
198
163
|
- lib/bricolage/resource.rb
|
199
164
|
- lib/bricolage/rubyjobclass.rb
|
200
165
|
- lib/bricolage/s3datasource.rb
|
@@ -208,10 +173,6 @@ files:
|
|
208
173
|
- lib/bricolage/variables.rb
|
209
174
|
- lib/bricolage/version.rb
|
210
175
|
- libexec/create-lockfile
|
211
|
-
- libexec/mys3dump.jar
|
212
|
-
- libexec/sqldump
|
213
|
-
- libexec/sqldump.Darwin
|
214
|
-
- libexec/sqldump.Linux
|
215
176
|
- test/all.rb
|
216
177
|
- test/home/Gemfile
|
217
178
|
- test/home/Gemfile.lock
|
@@ -219,7 +180,6 @@ files:
|
|
219
180
|
- test/home/config/development/database.yml
|
220
181
|
- test/home/config/development/password.yml
|
221
182
|
- test/home/config/development/variable.yml
|
222
|
-
- test/home/config/streamingload.yml
|
223
183
|
- test/home/data/20141002-1355_00.txt
|
224
184
|
- test/home/data/20141002-1355_01.txt
|
225
185
|
- test/home/data/20141002-1355_02.txt
|
data/jobclass/my-export.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
JobClass.define('my-export') {
|
2
|
-
parameters {|params|
|
3
|
-
params.add SQLFileParam.new(optional: true)
|
4
|
-
params.add DestFileParam.new
|
5
|
-
params.add SrcTableParam.new
|
6
|
-
params.add EnumParam.new('format', %w(json tsv csv), 'Target file format.', default: 'json')
|
7
|
-
params.add OptionalBoolParam.new('gzip', 'If true, compresses target file by gzip.')
|
8
|
-
params.add OptionalBoolParam.new('override', 'If true, clears target file. Otherwise causes error.')
|
9
|
-
params.add OptionalBoolParam.new('sqldump', 'If true, clears use sqldump command to dump, only wheen usable.')
|
10
|
-
params.add DataSourceParam.new('mysql')
|
11
|
-
}
|
12
|
-
|
13
|
-
declarations {|params|
|
14
|
-
sql_statement(params).declarations
|
15
|
-
}
|
16
|
-
|
17
|
-
script {|params, script|
|
18
|
-
script.task(params['data-source']) {|task|
|
19
|
-
task.export sql_statement(params),
|
20
|
-
path: params['dest-file'],
|
21
|
-
format: params['format'],
|
22
|
-
override: params['override'],
|
23
|
-
gzip: params['gzip'],
|
24
|
-
sqldump: params['sqldump']
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
def sql_statement(params)
|
29
|
-
if sql = params['sql-file']
|
30
|
-
sql
|
31
|
-
else
|
32
|
-
srcs = params['src-tables']
|
33
|
-
raise ParameterError, "src-tables must be singleton when no sql-file is given" unless srcs.size == 1
|
34
|
-
src_table_var = srcs.keys.first
|
35
|
-
stmt = SQLStatement.for_string("select * from $#{src_table_var};")
|
36
|
-
stmt.declarations = Declarations.new({src_table_var => src_table_var})
|
37
|
-
stmt
|
38
|
-
end
|
39
|
-
end
|
40
|
-
}
|
data/jobclass/my-import-delta.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'bricolage/psqldatasource'
|
2
|
-
require 'bricolage/mysqldatasource'
|
3
|
-
|
4
|
-
JobClass.define('my-import-delta') {
|
5
|
-
parameters {|params|
|
6
|
-
# S3Export
|
7
|
-
params.add SrcTableParam.new(optional: false)
|
8
|
-
params.add DataSourceParam.new('mysql', 'src-ds', 'Source data source.')
|
9
|
-
params.add SQLFileParam.new(optional: true)
|
10
|
-
params.add DataSourceParam.new('s3', 's3-ds', 'Temporary file storage.')
|
11
|
-
params.add DestFileParam.new('s3-prefix', 'PREFIX', 'Temporary S3 prefix.')
|
12
|
-
params.add KeyValuePairsParam.new('dump-options', 'KEY:VALUE', 'dump options.', optional: true)
|
13
|
-
|
14
|
-
# Delete, Load
|
15
|
-
params.add DataSourceParam.new('sql', 'dest-ds', 'Destination data source.')
|
16
|
-
params.add StringParam.new('delete-cond', 'SQL_EXPR', 'DELETE condition.')
|
17
|
-
params.add DestTableParam.new(optional: false)
|
18
|
-
params.add KeyValuePairsParam.new('options', 'OPTIONS', 'Loader options.',
|
19
|
-
optional: true, default: PSQLLoadOptions.new,
|
20
|
-
value_handler: lambda {|value, ctx, vars| PSQLLoadOptions.parse(value) })
|
21
|
-
|
22
|
-
# Misc
|
23
|
-
params.add OptionalBoolParam.new('analyze', 'ANALYZE table after SQL is executed.', default: true)
|
24
|
-
params.add OptionalBoolParam.new('vacuum', 'VACUUM table after SQL is executed.')
|
25
|
-
params.add OptionalBoolParam.new('vacuum-sort', 'VACUUM SORT table after SQL is executed.')
|
26
|
-
|
27
|
-
# All
|
28
|
-
params.add OptionalBoolParam.new('export', 'Runs EXPORT task.')
|
29
|
-
params.add OptionalBoolParam.new('load', 'Runs LOAD task.')
|
30
|
-
params.add OptionalBoolParam.new('gzip', 'Compress Temporary files.')
|
31
|
-
}
|
32
|
-
|
33
|
-
script {|params, script|
|
34
|
-
run_all = !params['export'] && !params['load']
|
35
|
-
|
36
|
-
# S3Export
|
37
|
-
if params['export'] || run_all
|
38
|
-
script.task(params['src-ds']) {|task|
|
39
|
-
task.s3export params['src-tables'].values.first.to_s,
|
40
|
-
params['sql-file'],
|
41
|
-
params['s3-ds'],
|
42
|
-
params['s3-prefix'],
|
43
|
-
params['gzip'],
|
44
|
-
dump_options: params['dump-options']
|
45
|
-
}
|
46
|
-
end
|
47
|
-
|
48
|
-
# Load
|
49
|
-
if params['load'] || run_all
|
50
|
-
script.task(params['dest-ds']) {|task|
|
51
|
-
task.transaction {
|
52
|
-
# DELETE
|
53
|
-
task.exec SQLStatement.delete_where(params['delete-cond']) if params['delete-cond']
|
54
|
-
|
55
|
-
# COPY
|
56
|
-
task.load params['s3-ds'], params['s3-prefix'], params['dest-table'],
|
57
|
-
'json', nil, params['options'].merge('gzip' => params['gzip'])
|
58
|
-
}
|
59
|
-
|
60
|
-
# VACUUM, ANALYZE
|
61
|
-
task.vacuum_if params['vacuum'], params['vacuum-sort'], params['dest-table']
|
62
|
-
task.analyze_if params['analyze'], params['dest-table']
|
63
|
-
}
|
64
|
-
end
|
65
|
-
}
|
66
|
-
}
|
data/jobclass/my-import.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'bricolage/psqldatasource'
|
2
|
-
require 'bricolage/mysqldatasource'
|
3
|
-
|
4
|
-
JobClass.define('my-import') {
|
5
|
-
parameters {|params|
|
6
|
-
# S3Export
|
7
|
-
params.add SrcTableParam.new(optional: false)
|
8
|
-
params.add DataSourceParam.new('mysql', 'src-ds', 'Source data source.')
|
9
|
-
params.add SQLFileParam.new(optional: true)
|
10
|
-
params.add DataSourceParam.new('s3', 's3-ds', 'Temporary file storage.')
|
11
|
-
params.add DestFileParam.new('s3-prefix', 'PREFIX', 'Temporary S3 prefix.')
|
12
|
-
params.add KeyValuePairsParam.new('dump-options', 'KEY:VALUE', 'dump options.', optional: true)
|
13
|
-
|
14
|
-
# Load
|
15
|
-
params.add DestTableParam.new(optional: false)
|
16
|
-
params.add DataSourceParam.new('sql', 'dest-ds', 'Destination data source.')
|
17
|
-
params.add KeyValuePairsParam.new('options', 'OPTIONS', 'Loader options.',
|
18
|
-
optional: true, default: PSQLLoadOptions.new,
|
19
|
-
value_handler: lambda {|value, ctx, vars| PSQLLoadOptions.parse(value) })
|
20
|
-
params.add SQLFileParam.new('table-def', 'PATH', 'Create table file.')
|
21
|
-
params.add OptionalBoolParam.new('no-backup', 'Do not backup current table with suffix "_old".', default: false)
|
22
|
-
|
23
|
-
# Misc
|
24
|
-
params.add OptionalBoolParam.new('analyze', 'ANALYZE table after SQL is executed.', default: true)
|
25
|
-
params.add OptionalBoolParam.new('vacuum', 'VACUUM table after SQL is executed.')
|
26
|
-
params.add OptionalBoolParam.new('vacuum-sort', 'VACUUM SORT table after SQL is executed.')
|
27
|
-
params.add KeyValuePairsParam.new('grant', 'KEY:VALUE', 'GRANT table after SQL is executed. (required keys: privilege, to)')
|
28
|
-
|
29
|
-
# All
|
30
|
-
params.add OptionalBoolParam.new('export', 'Runs EXPORT task.')
|
31
|
-
params.add OptionalBoolParam.new('put', 'Runs PUT task.')
|
32
|
-
params.add OptionalBoolParam.new('load', 'Runs LOAD task.')
|
33
|
-
params.add OptionalBoolParam.new('gzip', 'Compress Temporary files.')
|
34
|
-
}
|
35
|
-
|
36
|
-
script {|params, script|
|
37
|
-
run_all = !params['export'] && !params['put'] && !params['load']
|
38
|
-
|
39
|
-
# S3Export
|
40
|
-
if params['export'] || run_all
|
41
|
-
script.task(params['src-ds']) {|task|
|
42
|
-
task.s3export params['src-tables'].keys.first,
|
43
|
-
params['sql-file'],
|
44
|
-
params['s3-ds'],
|
45
|
-
params['s3-prefix'],
|
46
|
-
params['gzip'],
|
47
|
-
dump_options: params['dump-options']
|
48
|
-
}
|
49
|
-
end
|
50
|
-
|
51
|
-
# Load
|
52
|
-
if params['load'] || run_all
|
53
|
-
script.task(params['dest-ds']) {|task|
|
54
|
-
prev_table = '${dest_table}_old'
|
55
|
-
work_table = '${dest_table}_wk'
|
56
|
-
|
57
|
-
task.transaction {
|
58
|
-
# CREATE
|
59
|
-
task.drop_force prev_table
|
60
|
-
task.drop_force work_table
|
61
|
-
task.exec params['table-def'].replace(/\$\{?dest_table\}?\b/, work_table)
|
62
|
-
|
63
|
-
# COPY
|
64
|
-
task.load params['s3-ds'], params['s3-prefix'], work_table,
|
65
|
-
'json', nil, params['options'].merge('gzip' => params['gzip'])
|
66
|
-
|
67
|
-
# GRANT, ANALYZE
|
68
|
-
task.grant_if params['grant'], work_table
|
69
|
-
task.analyze_if params['analyze'], work_table
|
70
|
-
|
71
|
-
# RENAME
|
72
|
-
task.create_dummy_table '${dest_table}'
|
73
|
-
task.rename_table params['dest-table'].to_s, "#{params['dest-table'].name}_old"
|
74
|
-
task.rename_table work_table, params['dest-table'].name
|
75
|
-
}
|
76
|
-
|
77
|
-
task.drop_force prev_table if params['no-backup']
|
78
|
-
|
79
|
-
# VACUUM: vacuum is needless for newly created table, applying vacuum after exposure is not a problem.
|
80
|
-
task.vacuum_if params['vacuum'], params['vacuum-sort'], params['dest-table'].to_s
|
81
|
-
}
|
82
|
-
end
|
83
|
-
}
|
84
|
-
}
|
data/jobclass/my-migrate.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
require 'bricolage/psqldatasource'
|
2
|
-
|
3
|
-
JobClass.define('my-migrate') {
|
4
|
-
parameters {|params|
|
5
|
-
# Export
|
6
|
-
params.add SrcTableParam.new(optional: false)
|
7
|
-
params.add DataSourceParam.new('mysql', 'src-ds', 'Source data source.')
|
8
|
-
params.add DestFileParam.new('tmp-file', 'PATH', 'Temporary local file path.')
|
9
|
-
params.add OptionalBoolParam.new('sqldump', 'If true, use sqldump command to dump, only on available.', default: true)
|
10
|
-
params.add SQLFileParam.new(optional: true)
|
11
|
-
|
12
|
-
# Put
|
13
|
-
params.add DestFileParam.new('s3-file', 'PATH', 'Temporary S3 file path.')
|
14
|
-
params.add DataSourceParam.new('s3', 's3-ds', 'Temporary file storage.')
|
15
|
-
params.add OptionalBoolParam.new('override', 'If true, overwrite s3 target file. Otherwise causes error.')
|
16
|
-
params.add OptionalBoolParam.new('remove-tmp', 'Removes temporary local files after S3-PUT is succeeded.')
|
17
|
-
|
18
|
-
# Load
|
19
|
-
params.add DestTableParam.new(optional: false)
|
20
|
-
params.add DataSourceParam.new('sql', 'dest-ds', 'Destination data source.')
|
21
|
-
params.add KeyValuePairsParam.new('options', 'OPTIONS', 'Loader options.',
|
22
|
-
optional: true, default: PSQLLoadOptions.new,
|
23
|
-
value_handler: lambda {|value, ctx, vars| PSQLLoadOptions.parse(value) })
|
24
|
-
params.add SQLFileParam.new('table-def', 'PATH', 'Create table file.')
|
25
|
-
params.add OptionalBoolParam.new('no-backup', 'Do not backup current table with suffix "_old".', default: false)
|
26
|
-
|
27
|
-
# Misc
|
28
|
-
params.add OptionalBoolParam.new('analyze', 'ANALYZE table after SQL is executed.', default: true)
|
29
|
-
params.add OptionalBoolParam.new('vacuum', 'VACUUM table after SQL is executed.')
|
30
|
-
params.add OptionalBoolParam.new('vacuum-sort', 'VACUUM SORT table after SQL is executed.')
|
31
|
-
params.add KeyValuePairsParam.new('grant', 'KEY:VALUE', 'GRANT table after SQL is executed. (required keys: privilege, to)')
|
32
|
-
|
33
|
-
# All
|
34
|
-
params.add OptionalBoolParam.new('export', 'Runs EXPORT task.')
|
35
|
-
params.add OptionalBoolParam.new('put', 'Runs PUT task.')
|
36
|
-
params.add OptionalBoolParam.new('load', 'Runs LOAD task.')
|
37
|
-
params.add OptionalBoolParam.new('gzip', 'If true, compresses target file by gzip.', default: true)
|
38
|
-
}
|
39
|
-
|
40
|
-
declarations {|params|
|
41
|
-
decls = sql_statement(params).declarations
|
42
|
-
decls.declare 'dest-table', nil
|
43
|
-
decls
|
44
|
-
}
|
45
|
-
|
46
|
-
script {|params, script|
|
47
|
-
run_all = !params['export'] && !params['put'] && !params['load']
|
48
|
-
|
49
|
-
# Export
|
50
|
-
if params['export'] || run_all
|
51
|
-
script.task(params['src-ds']) {|task|
|
52
|
-
task.export sql_statement(params),
|
53
|
-
path: params['tmp-file'],
|
54
|
-
format: 'json',
|
55
|
-
override: true,
|
56
|
-
gzip: params['gzip'],
|
57
|
-
sqldump: params['sqldump']
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
# Put
|
62
|
-
if params['put'] || run_all
|
63
|
-
script.task(params['s3-ds']) {|task|
|
64
|
-
task.put params['tmp-file'], params['s3-file'], check_args: false
|
65
|
-
}
|
66
|
-
if params['remove-tmp']
|
67
|
-
script.task(params.file_ds) {|task|
|
68
|
-
task.remove params['tmp-file']
|
69
|
-
}
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Load
|
74
|
-
if params['load'] || run_all
|
75
|
-
script.task(params['dest-ds']) {|task|
|
76
|
-
prev_table = '${dest_table}_old'
|
77
|
-
work_table = '${dest_table}_wk'
|
78
|
-
|
79
|
-
task.transaction {
|
80
|
-
# CREATE
|
81
|
-
task.drop_force prev_table
|
82
|
-
task.drop_force work_table
|
83
|
-
task.exec params['table-def'].replace(/\$\{?dest_table\}?\b/, work_table)
|
84
|
-
|
85
|
-
# COPY
|
86
|
-
task.load params['s3-ds'], params['s3-file'], work_table,
|
87
|
-
'json', nil, params['options'].merge('gzip' => params['gzip'])
|
88
|
-
|
89
|
-
# GRANT, ANALYZE
|
90
|
-
task.grant_if params['grant'], work_table
|
91
|
-
task.analyze_if params['analyze'], work_table
|
92
|
-
|
93
|
-
# RENAME
|
94
|
-
task.create_dummy_table '${dest_table}'
|
95
|
-
task.rename_table params['dest-table'].to_s, "#{params['dest-table'].name}_old"
|
96
|
-
task.rename_table work_table, params['dest-table'].name
|
97
|
-
}
|
98
|
-
|
99
|
-
task.drop_force prev_table if params['no-backup']
|
100
|
-
|
101
|
-
# VACUUM: vacuum is needless for newly created table, applying vacuum after exposure is not a problem.
|
102
|
-
task.vacuum_if params['vacuum'], params['vacuum-sort'], params['dest-table'].to_s
|
103
|
-
}
|
104
|
-
end
|
105
|
-
}
|
106
|
-
|
107
|
-
def sql_statement(params)
|
108
|
-
return params['sql-file'] if params['sql-file']
|
109
|
-
srcs = params['src-tables']
|
110
|
-
raise ParameterError, "src-tables must be singleton when no sql-file is given" unless srcs.size == 1
|
111
|
-
src_table_var = srcs.keys.first
|
112
|
-
stmt = SQLStatement.for_string("select * from $#{src_table_var};")
|
113
|
-
stmt.declarations = Declarations.new({src_table_var => src_table_var})
|
114
|
-
stmt
|
115
|
-
end
|
116
|
-
}
|
data/jobclass/redis-export.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'bricolage/psqldatasource'
|
2
|
-
require 'bricolage/redisdatasource'
|
3
|
-
require 'redis'
|
4
|
-
|
5
|
-
JobClass.define('redis-export') {
|
6
|
-
parameters {|params|
|
7
|
-
# Export
|
8
|
-
params.add DataSourceParam.new('psql', 'src-ds', 'Source data source.')
|
9
|
-
params.add SrcTableParam.new(optional: false)
|
10
|
-
params.add SQLFileParam.new(optional: true)
|
11
|
-
|
12
|
-
# Redis import
|
13
|
-
params.add DataSourceParam.new('redis', 'dest-ds', 'Redis cluster')
|
14
|
-
params.add StringParam.new('key-column', 'REDIS_KEY', 'Redis object key. default: id', optional: true)
|
15
|
-
params.add StringParam.new('prefix', 'REDIS_PREFIX', 'Redis object key prefix', optional: true)
|
16
|
-
params.add StringParam.new('encode', 'REDIS_ENCODE', 'Redis object encoding. default: hash', optional: true)
|
17
|
-
params.add StringParam.new('expire', 'REDIS_TTL', 'Redis object TTL. default: none', optional: true)
|
18
|
-
}
|
19
|
-
|
20
|
-
script {|params, script|
|
21
|
-
# Export
|
22
|
-
script.task(params['dest-ds']) {|task|
|
23
|
-
task.import params['src-ds'],
|
24
|
-
params['src-tables'].first,
|
25
|
-
sql_statement(params),
|
26
|
-
params['key-column'] || "id",
|
27
|
-
params['prefix'],
|
28
|
-
params['encode'] || "hash",
|
29
|
-
expire: params['expire'].to_i
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
def sql_statement(params)
|
34
|
-
return params['sql-file'] if params['sql-file']
|
35
|
-
srcs = params['src-tables']
|
36
|
-
raise ParameterError, "src-tables must be singleton when no sql-file is given" unless srcs.size == 1
|
37
|
-
src_table_var = srcs.keys.first
|
38
|
-
stmt = SQLStatement.for_string("select * from $#{src_table_var};")
|
39
|
-
stmt.declarations = Declarations.new({src_table_var => src_table_var})
|
40
|
-
stmt
|
41
|
-
end
|
42
|
-
}
|
@@ -1,363 +0,0 @@
|
|
1
|
-
require 'bricolage/datasource'
|
2
|
-
require 'mysql2'
|
3
|
-
require 'json'
|
4
|
-
require 'csv'
|
5
|
-
require 'stringio'
|
6
|
-
require 'open3'
|
7
|
-
|
8
|
-
module Bricolage
|
9
|
-
|
10
|
-
class MySQLDataSource < DataSource
|
11
|
-
declare_type 'mysql'
|
12
|
-
|
13
|
-
def initialize(**mysql_options)
|
14
|
-
@mysql_options = mysql_options
|
15
|
-
@client = nil
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_reader :mysql_options
|
19
|
-
|
20
|
-
def host
|
21
|
-
@mysql_options[:host]
|
22
|
-
end
|
23
|
-
|
24
|
-
def port
|
25
|
-
@mysql_options[:port]
|
26
|
-
end
|
27
|
-
|
28
|
-
def username
|
29
|
-
@mysql_options[:username]
|
30
|
-
end
|
31
|
-
|
32
|
-
def password
|
33
|
-
@mysql_options[:password]
|
34
|
-
end
|
35
|
-
|
36
|
-
def database
|
37
|
-
@mysql_options[:database]
|
38
|
-
end
|
39
|
-
|
40
|
-
def new_task
|
41
|
-
MySQLTask.new(self)
|
42
|
-
end
|
43
|
-
|
44
|
-
def open
|
45
|
-
@client = Mysql2::Client.new(**@mysql_options)
|
46
|
-
begin
|
47
|
-
yield self
|
48
|
-
ensure
|
49
|
-
c = @client
|
50
|
-
@client = nil
|
51
|
-
c.close
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def query(sql, **opts)
|
56
|
-
logger.info "[SQL] #{sql}"
|
57
|
-
connection_check
|
58
|
-
@client.query(sql, **opts)
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def connection_check
|
64
|
-
unless @client
|
65
|
-
raise FatalError, "#{self.class} used outside of \#open block"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class MySQLTask < DataSourceTask
|
71
|
-
def export(stmt, path: nil, format: nil, override: false, gzip: false, sqldump: false)
|
72
|
-
add Export.new(stmt, path: path, format: format, override: override, gzip: gzip, sqldump: sqldump)
|
73
|
-
end
|
74
|
-
|
75
|
-
class Export < Action
|
76
|
-
def initialize(stmt, path: nil, format: nil, override: false, gzip: false, sqldump: false)
|
77
|
-
@statement = stmt
|
78
|
-
@path = path
|
79
|
-
@format = format
|
80
|
-
@override = override
|
81
|
-
@gzip = gzip
|
82
|
-
@sqldump = sqldump
|
83
|
-
end
|
84
|
-
|
85
|
-
def bind(*args)
|
86
|
-
@statement.bind(*args)
|
87
|
-
end
|
88
|
-
|
89
|
-
def source
|
90
|
-
@statement.stripped_source
|
91
|
-
end
|
92
|
-
|
93
|
-
def run
|
94
|
-
if @sqldump and sqldump_available? and sqldump_usable?
|
95
|
-
export_by_sqldump
|
96
|
-
else
|
97
|
-
export_by_ruby
|
98
|
-
end
|
99
|
-
JobResult.success
|
100
|
-
end
|
101
|
-
|
102
|
-
def export_by_sqldump
|
103
|
-
cmds = [[{"SQLDUMP_PASSWORD" => ds.password}, sqldump_path.to_s, "--#{@format}", ds.host, ds.port.to_s, ds.username, ds.database, @statement.stripped_source]]
|
104
|
-
cmds.push [GZIP_COMMAND] if @gzip
|
105
|
-
cmds.last.push({out: @path.to_s})
|
106
|
-
ds.logger.info '[CMD] ' + format_pipeline(cmds)
|
107
|
-
statuses = Open3.pipeline(*cmds)
|
108
|
-
statuses.each_with_index do |st, idx|
|
109
|
-
unless st.success?
|
110
|
-
cmd = cmds[idx].first
|
111
|
-
raise JobFailure, "sqldump failed (status #{st.to_i})"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def format_pipeline(cmds)
|
117
|
-
cmds = cmds.map {|args| args[0].kind_of?(Hash) ? args[1..-1] : args.dup } # do not show env
|
118
|
-
cmds.map {|args| %Q("#{args.join('" "')}") }.join(' | ')
|
119
|
-
end
|
120
|
-
|
121
|
-
def sqldump_available?
|
122
|
-
sqldump_real_path.executable?
|
123
|
-
end
|
124
|
-
|
125
|
-
def sqldump_path
|
126
|
-
Pathname(__dir__).parent.parent + "libexec/sqldump"
|
127
|
-
end
|
128
|
-
|
129
|
-
def sqldump_real_path
|
130
|
-
Pathname("#{sqldump_path}.#{platform_name}")
|
131
|
-
end
|
132
|
-
|
133
|
-
def platform_name
|
134
|
-
@platform_name ||= `uname -s`.strip
|
135
|
-
end
|
136
|
-
|
137
|
-
def sqldump_usable?
|
138
|
-
%w[json tsv].include?(@format)
|
139
|
-
end
|
140
|
-
|
141
|
-
def export_by_ruby
|
142
|
-
ds.logger.info "exporting table into #{@path} ..."
|
143
|
-
count = 0
|
144
|
-
open_target_file(@path) {|f|
|
145
|
-
writer_class = WRITER_CLASSES[@format] or raise ArgumentError, "unknown export format: #{@format.inspect}"
|
146
|
-
writer = writer_class.new(f)
|
147
|
-
rs = ds.query(@statement.stripped_source, as: writer_class.record_format, stream: true, cache_rows: false)
|
148
|
-
ds.logger.info "got result set, writing..."
|
149
|
-
rs.each do |values|
|
150
|
-
writer.write_record values
|
151
|
-
count += 1
|
152
|
-
ds.logger.info "#{count} records exported..." if count % 10_0000 == 0
|
153
|
-
end
|
154
|
-
}
|
155
|
-
ds.logger.info "#{count} records exported; export finished"
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
# FIXME: parameterize
|
161
|
-
GZIP_COMMAND = 'gzip'
|
162
|
-
|
163
|
-
def open_target_file(path, &block)
|
164
|
-
unless @override
|
165
|
-
raise JobFailure, "destination file already exists: #{path}" if File.exist?(path)
|
166
|
-
end
|
167
|
-
if @gzip
|
168
|
-
ds.logger.info "enable compression: gzip"
|
169
|
-
IO.popen(%Q(#{GZIP_COMMAND} > "#{path}"), 'w', &block)
|
170
|
-
else
|
171
|
-
File.open(path, 'w', &block)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def s3export(table, stmt, s3ds, prefix, gzip, dump_options)
|
177
|
-
options = dump_options.nil? ? {} : dump_options[:dump_options]
|
178
|
-
add S3Export.new(table, stmt, s3ds, prefix, gzip: gzip,
|
179
|
-
format: options['format'],
|
180
|
-
partition_column: options['partition_column'],
|
181
|
-
partition_number: options['partition_number'],
|
182
|
-
write_concurrency: options['write_concurrency'],
|
183
|
-
rotation_size: options['rotation_size'],
|
184
|
-
delete_objects: options['delete_objects'],
|
185
|
-
object_key_delimiter: options['object_key_delimiter'],
|
186
|
-
src_zone_offset: options['src_zone_offset'],
|
187
|
-
dst_zone_offset: options['dst_zone_offset'])
|
188
|
-
end
|
189
|
-
|
190
|
-
class S3Export < Action
|
191
|
-
|
192
|
-
def initialize(table, stmt, s3ds, prefix, gzip: true,
|
193
|
-
format: "json",
|
194
|
-
partition_column: nil,
|
195
|
-
partition_number: 4,
|
196
|
-
write_concurrency: 4,
|
197
|
-
rotation_size: nil,
|
198
|
-
delete_objects: false,
|
199
|
-
object_key_delimiter: nil,
|
200
|
-
src_zone_offset: nil,
|
201
|
-
dst_zone_offset: nil)
|
202
|
-
@table = table
|
203
|
-
@statement = stmt
|
204
|
-
@s3ds = s3ds
|
205
|
-
@prefix = build_prefix @s3ds.prefix, prefix
|
206
|
-
@format = format
|
207
|
-
@gzip = gzip
|
208
|
-
@partition_column = partition_column
|
209
|
-
@partition_number = partition_number
|
210
|
-
@write_concurrency = write_concurrency
|
211
|
-
@rotation_size = rotation_size
|
212
|
-
@delete_objects = delete_objects
|
213
|
-
@object_key_delimiter = object_key_delimiter
|
214
|
-
@src_zone_offset = src_zone_offset
|
215
|
-
@dst_zone_offset = dst_zone_offset
|
216
|
-
end
|
217
|
-
|
218
|
-
def run
|
219
|
-
s3export
|
220
|
-
JobResult.success
|
221
|
-
end
|
222
|
-
|
223
|
-
def bind(*args)
|
224
|
-
@statement.bind(*args) if @statement
|
225
|
-
end
|
226
|
-
|
227
|
-
def source
|
228
|
-
"-- myexport #{@table} -> #{@s3ds.bucket_name}/#{@prefix}" +
|
229
|
-
(@statement ? "\n#{@statement.stripped_source}" : "")
|
230
|
-
end
|
231
|
-
|
232
|
-
def s3export
|
233
|
-
cmd = build_cmd(command_parameters)
|
234
|
-
ds.logger.info "[CMD] #{cmd}"
|
235
|
-
out, st = Open3.capture2e(environment_variables, cmd)
|
236
|
-
ds.logger.info "[CMDOUT] #{out}"
|
237
|
-
unless st.success?
|
238
|
-
msg = extract_exception_message(out)
|
239
|
-
raise JobFailure, "mys3dump failed (status: #{st.to_i}): #{msg}"
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
def environment_variables
|
244
|
-
{
|
245
|
-
'AWS_ACCESS_KEY_ID' => @s3ds.access_key,
|
246
|
-
'AWS_SECRET_ACCESS_KEY' => @s3ds.secret_key,
|
247
|
-
'MYS3DUMP_PASSWORD' => ds.password
|
248
|
-
}
|
249
|
-
end
|
250
|
-
|
251
|
-
def command_parameters
|
252
|
-
params = {
|
253
|
-
jar: mys3dump_path.to_s,
|
254
|
-
h: ds.host,
|
255
|
-
P: ds.port.to_s,
|
256
|
-
D: ds.database,
|
257
|
-
u: ds.username,
|
258
|
-
#p: ds.password,
|
259
|
-
o: connection_property,
|
260
|
-
t: @table,
|
261
|
-
b: @s3ds.bucket.name,
|
262
|
-
x: @prefix
|
263
|
-
}
|
264
|
-
params[:q] = @statement.stripped_source.chomp(';') if @statement
|
265
|
-
params[:f] = @format if @format
|
266
|
-
params[:C] = nil if @gzip
|
267
|
-
params[:c] = @partition_column if @partition_column
|
268
|
-
params[:n] = @partition_number if @partition_number
|
269
|
-
params[:w] = @write_concurrency if @write_concurrency
|
270
|
-
params[:r] = @rotation_size if @rotation_size
|
271
|
-
params[:d] = nil if @delete_objects
|
272
|
-
params[:k] = @object_key_delimiter if @object_key_delimiter
|
273
|
-
if src_zone_offset = @src_zone_offset || ds.mysql_options[:src_zone_offset]
|
274
|
-
params[:S] = src_zone_offset
|
275
|
-
end
|
276
|
-
if dst_zone_offset = @dst_zone_offset || ds.mysql_options[:dst_zone_offset]
|
277
|
-
params[:T] = dst_zone_offset
|
278
|
-
end
|
279
|
-
params
|
280
|
-
end
|
281
|
-
|
282
|
-
OPTION_MAP = {
|
283
|
-
encoding: 'useUnicode=true&characterEncoding',
|
284
|
-
read_timeout: 'netTimeoutForStreamingResults',
|
285
|
-
connect_timeout: 'connectTimeout',
|
286
|
-
reconnect: 'autoReconnect',
|
287
|
-
collation: 'connectionCollation'
|
288
|
-
}
|
289
|
-
|
290
|
-
def connection_property
|
291
|
-
ds.mysql_options.map {|k, v| opt = OPTION_MAP[k] ; opt ? "#{opt}=#{v}" : nil }.compact.join('&')
|
292
|
-
end
|
293
|
-
|
294
|
-
def build_prefix(ds_prefix, pm_prefix)
|
295
|
-
((ds_prefix || "") + "//" + (pm_prefix.to_s || "")).gsub(%r<\A/>, '').gsub(%r<//>, '/')
|
296
|
-
end
|
297
|
-
|
298
|
-
def mys3dump_path
|
299
|
-
Pathname(__dir__).parent.parent + "libexec/mys3dump.jar"
|
300
|
-
end
|
301
|
-
|
302
|
-
def build_cmd(options)
|
303
|
-
(['java'] + options.flat_map {|k, v| v ? ["-#{k}", v.to_s] : ["-#{k}"] }.map {|o| %Q("#{o}") }).join(" ")
|
304
|
-
end
|
305
|
-
|
306
|
-
def extract_exception_message(out)
|
307
|
-
out.lines do |line|
|
308
|
-
if /^.*Exception: (?<msg>.*)$/ =~ line
|
309
|
-
return msg
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
WRITER_CLASSES = {}
|
316
|
-
|
317
|
-
class JSONWriter
|
318
|
-
def JSONWriter.record_format
|
319
|
-
:hash
|
320
|
-
end
|
321
|
-
|
322
|
-
def initialize(f)
|
323
|
-
@f = f
|
324
|
-
end
|
325
|
-
|
326
|
-
def write_record(values)
|
327
|
-
@f.puts JSON.dump(values)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
WRITER_CLASSES['json'] = JSONWriter
|
331
|
-
|
332
|
-
class TSVWriter
|
333
|
-
def TSVWriter.record_format
|
334
|
-
:array
|
335
|
-
end
|
336
|
-
|
337
|
-
def initialize(f)
|
338
|
-
@f = f
|
339
|
-
end
|
340
|
-
|
341
|
-
def write_record(values)
|
342
|
-
@f.puts values.join("\t")
|
343
|
-
end
|
344
|
-
end
|
345
|
-
WRITER_CLASSES['tsv'] = TSVWriter
|
346
|
-
|
347
|
-
class CSVWriter
|
348
|
-
def CSVWriter.record_format
|
349
|
-
:array
|
350
|
-
end
|
351
|
-
|
352
|
-
def initialize(f)
|
353
|
-
@csv = CSV.new(f)
|
354
|
-
end
|
355
|
-
|
356
|
-
def write_record(values)
|
357
|
-
@csv.add_row values
|
358
|
-
end
|
359
|
-
end
|
360
|
-
WRITER_CLASSES['csv'] = CSVWriter
|
361
|
-
end
|
362
|
-
|
363
|
-
end
|
@@ -1,153 +0,0 @@
|
|
1
|
-
require 'bricolage/datasource'
|
2
|
-
require 'bricolage/commandutils'
|
3
|
-
require 'redis'
|
4
|
-
require 'json'
|
5
|
-
|
6
|
-
module Bricolage
|
7
|
-
|
8
|
-
class RedisDataSource < DataSource
|
9
|
-
declare_type 'redis'
|
10
|
-
|
11
|
-
def initialize(host: 'localhost', port: 6380, **options)
|
12
|
-
@host = host
|
13
|
-
@port = port
|
14
|
-
@options = options
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :host
|
18
|
-
attr_reader :port
|
19
|
-
attr_reader :options
|
20
|
-
|
21
|
-
def new_task
|
22
|
-
RedisTask.new(self)
|
23
|
-
end
|
24
|
-
|
25
|
-
def open
|
26
|
-
client = Redis.new(host: @host, port: @port, **@options)
|
27
|
-
yield client
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class RedisTask < DataSourceTask
|
32
|
-
def import(src, table, query, key_column, prefix, encode, expire: nil)
|
33
|
-
add Import.new(src, table, query, key_column, prefix, encode, expire)
|
34
|
-
end
|
35
|
-
|
36
|
-
class Import < Action
|
37
|
-
def initialize(src, table, query, key_column, prefix, encode, expire)
|
38
|
-
@src = src
|
39
|
-
@table = table
|
40
|
-
@query = query
|
41
|
-
@key_columns = key_column.split(',').map(&:strip)
|
42
|
-
@prefix = prefix || "#{@table.last.schema}_#{@table.last.name}_"
|
43
|
-
@encode = encode
|
44
|
-
@expire = expire
|
45
|
-
end
|
46
|
-
|
47
|
-
def bind(*args)
|
48
|
-
@query.bind(*args)
|
49
|
-
end
|
50
|
-
|
51
|
-
def source
|
52
|
-
@query.stripped_source
|
53
|
-
end
|
54
|
-
|
55
|
-
def run
|
56
|
-
logger = ds.logger
|
57
|
-
begin
|
58
|
-
logger.info "Key Pattern: #{@prefix}<#{@key_columns.join('_')}>"
|
59
|
-
logger.info "Encode: #{@encode}"
|
60
|
-
logger.info "Expire: #{@expire}"
|
61
|
-
ds.open {|client|
|
62
|
-
writer = RedisRowWriter.for_encode(@encode).new(client, @prefix, @key_columns)
|
63
|
-
import writer
|
64
|
-
}
|
65
|
-
rescue => ex
|
66
|
-
logger.exception ex
|
67
|
-
raise JobFailure, ex.message
|
68
|
-
end
|
69
|
-
JobResult.success
|
70
|
-
end
|
71
|
-
|
72
|
-
BATCH_SIZE = 5000
|
73
|
-
|
74
|
-
def import(writer)
|
75
|
-
count = 0
|
76
|
-
@src.query_batch(source, BATCH_SIZE) do |rs|
|
77
|
-
writer.pipelined {
|
78
|
-
rs.each do |row|
|
79
|
-
writer.write(row)
|
80
|
-
count += 1
|
81
|
-
ds.logger.info "transfered: #{count} rows" if count % 100_0000 == 0
|
82
|
-
end
|
83
|
-
}
|
84
|
-
end
|
85
|
-
ds.logger.info "all rows written: #{count} rows"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
class RedisRowWriter
|
91
|
-
def RedisRowWriter.for_encode(encode)
|
92
|
-
case encode
|
93
|
-
when 'hash' then RedisHashRowWriter
|
94
|
-
when 'json' then RedisJSONRowWriter
|
95
|
-
else
|
96
|
-
raise ParameterError, "unsupported Redis encode: #{encode.inspect}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def initialize(client, prefix, key_columns)
|
101
|
-
@client = client
|
102
|
-
@prefix = prefix
|
103
|
-
@key_columns = key_columns
|
104
|
-
end
|
105
|
-
|
106
|
-
attr_reader :prefix
|
107
|
-
attr_reader :write_count
|
108
|
-
|
109
|
-
def key(row)
|
110
|
-
@prefix + @key_columns.map {|k| row[k] }.join('_')
|
111
|
-
end
|
112
|
-
|
113
|
-
def value_columns(row)
|
114
|
-
r = row.dup
|
115
|
-
@key_columns.each do |key|
|
116
|
-
r.delete(key)
|
117
|
-
end
|
118
|
-
r.empty? ? {1 => 1} : r
|
119
|
-
end
|
120
|
-
|
121
|
-
def pipelined(&block)
|
122
|
-
@client.pipelined(&block)
|
123
|
-
end
|
124
|
-
|
125
|
-
def write(row)
|
126
|
-
key = key(row)
|
127
|
-
futures = do_write(key, value_columns(row))
|
128
|
-
futures.push @client.expire(key, @expire) if @expire
|
129
|
-
futures
|
130
|
-
end
|
131
|
-
|
132
|
-
def expire
|
133
|
-
@client.expire(key, @expire)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
class RedisHashRowWriter < RedisRowWriter
|
138
|
-
def do_write(key, values)
|
139
|
-
# set a value for each key:field pair
|
140
|
-
values.map {|field, value|
|
141
|
-
@client.hset(key, field, value)
|
142
|
-
}
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
class RedisJSONRowWriter < RedisRowWriter
|
147
|
-
def do_write(key, values)
|
148
|
-
future = @client.set(key, JSON.generate(values))
|
149
|
-
[future]
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
end
|
data/libexec/mys3dump.jar
DELETED
Binary file
|
data/libexec/sqldump
DELETED
data/libexec/sqldump.Darwin
DELETED
Binary file
|
data/libexec/sqldump.Linux
DELETED
Binary file
|