bricolage-redis 5.26.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.
@@ -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: []