bricolage-redis 5.26.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8b0dd1f32e3cda4a5638cfcd08db7061a121223
4
+ data.tar.gz: 0c4bb9f8f7d1ebe9d44797362680ed064afa3646
5
+ SHA512:
6
+ metadata.gz: 87f635b937a9d1e00f4d9a187f3eb49b2e7938dc1f15f6bfba3585bd81891079ff55e986739bf2bf29abc564b4b2dfd3c084498d1abba5487b0184e41bb27fc9
7
+ data.tar.gz: 92f2ac32c7a911312356e7fb7fbaa6504cf4289250676ab889fe4e92ea8c23e6c104884c6b6f182c91da84cd4fdd49d8d2102d7de9649dcf3ff0edae0b337351
@@ -0,0 +1,25 @@
1
+ # bricolage-redis
2
+
3
+ Redis-related job classes for Bricolage batch framework.
4
+
5
+ This software is written in working time in Cookpad, Inc.
6
+
7
+ ## Home Page
8
+
9
+ https://github.com/bricolages/bricolage-redis
10
+
11
+ ## Usage
12
+
13
+ Add following line to your Gemfile:
14
+ ```
15
+ gem 'bricolage-redis', require: 'bricolage-redis'
16
+ ```
17
+
18
+ ## License
19
+
20
+ MIT license.
21
+ SEe LICENSES file for details.
22
+
23
+ ## Author
24
+
25
+ Minero Aoki
@@ -0,0 +1,5 @@
1
+ # bricolage-redis release note
2
+
3
+ ## version 5.26.0
4
+
5
+ - first release.
@@ -0,0 +1,42 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ require 'bricolage/jobclass'
2
+ require 'pathname'
3
+
4
+ jobclass_path = Pathname(__dir__).realpath.parent.cleanpath + 'jobclass'
5
+ Bricolage::JobClass.add_load_path jobclass_path
@@ -0,0 +1,153 @@
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
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bricolage-redis
3
+ version: !ruby/object:Gem::Version
4
+ version: 5.26.0
5
+ platform: ruby
6
+ authors:
7
+ - Minero Aoki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bricolage
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.26.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.26.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ description:
42
+ email: aamine@loveruby.net
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.md
48
+ - RELEASE.md
49
+ - jobclass/redis-export.rb
50
+ - lib/bricolage-redis.rb
51
+ - lib/bricolage/redisdatasource.rb
52
+ homepage: https://github.com/bricolages/bricolage-redis
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.2.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.6.11
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Redis-related job classes for Bricolage batch framework
76
+ test_files: []