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.
- checksums.yaml +7 -0
- data/README.md +25 -0
- data/RELEASE.md +5 -0
- data/jobclass/redis-export.rb +42 -0
- data/lib/bricolage-redis.rb +5 -0
- data/lib/bricolage/redisdatasource.rb +153 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/RELEASE.md
ADDED
@@ -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,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: []
|