bricolage 5.15.0 → 5.15.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f55cd961f8a67911b9ceb8383202dbf5a4bc5eac
4
- data.tar.gz: 5ea072313fd1a0a7455a57e02ae8a38579c1d8ef
3
+ metadata.gz: 26898f98eb266df22a4940e128b1ae4cf44afd83
4
+ data.tar.gz: fd57e7f63e899b6f9f560a2694a4e1466efcb655
5
5
  SHA512:
6
- metadata.gz: 43b49955f3739925b6c92e84f72fde9457d2f72a10034316f2fec0a44cbde62cdfd9072ae29048023092a3662bcae16733339a72429061159898740db2f3e15f
7
- data.tar.gz: 397a51bc3f114ca552ee3441992ef7311e38c5cb081cde01acece6d2e0daa2db7cb8c35f7a3d7ab374c40dd6ab9123f05ffe389efc0b5d7a6cec281dc4188642
6
+ metadata.gz: 46996f94e09c5e9c2efd6814492ebaff39fbdfc1b765a906dbd71dc387b6fecc6bd446a6cd9d916be2479bc0f83588380c3f91afb985ee9ad29f0275c63ed779
7
+ data.tar.gz: fad034727fa9b0781eeb642bda3da779ff8183ea94e7f7eef9d5d5e049a41075c309c8585fcdc37784730fed6713eac26e7979b74f0a2532ce01da83fc96b989
@@ -14,6 +14,7 @@ JobClass.define('redis-export') {
14
14
  params.add StringParam.new('key-column', 'REDIS_KEY', 'Redis object key. default: id', optional: true)
15
15
  params.add StringParam.new('prefix', 'REDIS_PREFIX', 'Redis object key prefix', optional: true)
16
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)
17
18
  }
18
19
 
19
20
  script {|params, script|
@@ -24,7 +25,8 @@ JobClass.define('redis-export') {
24
25
  sql_statement(params),
25
26
  params['key-column'] || "id",
26
27
  params['prefix'],
27
- params['encode'] || "hash"
28
+ params['encode'] || "hash",
29
+ expire: params['expire'].to_i
28
30
  }
29
31
  }
30
32
 
@@ -1,4 +1,5 @@
1
1
  require 'bricolage/exception'
2
+ require 'securerandom'
2
3
  require 'pg'
3
4
 
4
5
  module Bricolage
@@ -10,6 +11,7 @@ module Bricolage
10
11
  @connection = connection
11
12
  @ds = ds
12
13
  @logger = logger
14
+ @cursor = nil
13
15
  end
14
16
 
15
17
  def source
@@ -33,6 +35,36 @@ module Bricolage
33
35
  exec(query, &block)
34
36
  end
35
37
 
38
+ def execute_query_with_cursor(query, fetch_size, cursor, &block)
39
+ raise "Begin transaction before invoking this method" unless in_transaction?
40
+ if @cursor.nil?
41
+ @cursor = cursor || (0...32).map { alphabets[rand(alphabets.length)] }.join
42
+ declare_cursor = "declare #{@cursor} cursor for #{query}"
43
+ @logger.info "[#{@ds.name}] #{declare_cursor}"
44
+ @connection.exec(declare_cursor)
45
+ elsif !@cursor.nil? && cursor.nil?
46
+ raise "Cursor in use"
47
+ elsif @cursor != cursor
48
+ raise "Invalid cursor"
49
+ end
50
+ fetch = "fetch #{fetch_size} in #{@cursor}"
51
+ @logger.info "[#{@ds.name}] #{fetch}" if cursor.nil?
52
+ yield @connection.exec(fetch)
53
+ return @cursor
54
+ end
55
+
56
+ def clear_cursor
57
+ @cursor = nil
58
+ end
59
+
60
+ def alphabets
61
+ @alphabets = @alphabets || [('a'...'z'), ('A'...'Z')].map { |i| i.to_a }.flatten
62
+ end
63
+
64
+ def in_transaction?
65
+ @connection.transaction_status == PG::Constants::PQTRANS_INTRANS
66
+ end
67
+
36
68
  alias update execute
37
69
 
38
70
  def drop_table(name)
@@ -88,5 +120,4 @@ module Bricolage
88
120
  end
89
121
  end
90
122
  end
91
-
92
123
  end
@@ -110,6 +110,28 @@ module Bricolage
110
110
  open {|conn| conn.execute_query(query, &block) }
111
111
  end
112
112
 
113
+ def cursor_transaction(&block)
114
+ raise "Cursor in use" if cursor_in_transaction?
115
+ conn = PG::Connection.open(host: @host, port: @port, dbname: @database, user: @user, password: password)
116
+ @cur_conn = PostgresConnection.new(conn, self, logger)
117
+ @cur_conn.execute("begin transaction")
118
+ yield
119
+ ensure
120
+ @cur_conn.execute("commit") if cursor_in_transaction?
121
+ @cur_conn.clear_cursor if @cur_conn
122
+ @cur_conn = nil
123
+ conn.close if conn
124
+ end
125
+
126
+ def cursor_execute_query(query, fetch_size: 10000, cursor: nil, &block)
127
+ raise "Begin transaction before invoking this method" unless cursor_in_transaction?
128
+ @cur_conn.execute_query_with_cursor(query, fetch_size, cursor, &block)
129
+ end
130
+
131
+ def cursor_in_transaction?
132
+ @cur_conn && @cur_conn.in_transaction?
133
+ end
134
+
113
135
  def drop_table(name)
114
136
  open {|conn| conn.drop_table(name) }
115
137
  end
@@ -28,19 +28,19 @@ module Bricolage
28
28
  end
29
29
 
30
30
  class RedisTask < DataSourceTask
31
- def import(src, table, query, key_column, prefix, encode)
32
- add Import.new(src, table, query, key_column, prefix, encode)
31
+ def import(src, table, query, key_column, prefix, encode, expire: nil)
32
+ add Import.new(src, table, query, key_column, prefix, encode, expire)
33
33
  end
34
34
 
35
35
  class Import < Action
36
- def initialize(src, table, query, key_column, prefix, encode)
36
+ def initialize(src, table, query, key_column, prefix, encode, expire)
37
37
  @src = src
38
38
  @table = table
39
39
  @query = query
40
- @key_column = key_column
41
- puts key_column
40
+ @key_columns = key_column.split(',').map(&:strip)
42
41
  @prefix = prefix
43
42
  @encode = encode
43
+ @expire = expire
44
44
  @read_count = 0
45
45
  @write_count = 0
46
46
  end
@@ -58,48 +58,76 @@ module Bricolage
58
58
  end
59
59
 
60
60
  def import
61
- read_row do |row|
62
- write_row(row)
63
- end
61
+ results = []
62
+ @src.cursor_transaction {
63
+ read_count = 0
64
+ loop do
65
+ futures = []
66
+ ds.client.pipelined do
67
+ read_count = read_row do |row|
68
+ futures.push write_row row
69
+ end
70
+ end
71
+ results.push futures.flatten.map {|f| f.value}.all?
72
+ break if read_count == 0
73
+ end
74
+ }
75
+ @cursor = nil
76
+ results.all?
64
77
  end
65
78
 
66
79
  def read_row
67
- @src.execute_query(source) do |rs|
80
+ rs_size = 0
81
+ @cursor = @src.cursor_execute_query(source, cursor: @cursor) do |rs|
82
+ rs_size = rs.values.size
83
+ break if rs_size == 0
68
84
  rs.each do |row|
69
85
  yield row
70
86
  @read_count += 1
71
87
  ds.logger.info "Rows read: #{@read_count}" if @read_count % 100000 == 0
72
88
  end
73
89
  end
90
+ rs_size
74
91
  end
75
92
 
76
- def write_row(row, &block)
93
+ def write_row(row)
77
94
  key = key(row)
95
+ data = delete_key_columns(row)
96
+ f = []
78
97
  case @encode
79
98
  when 'hash'
80
99
  # set a value for each key:field pair
81
- r = []
82
- row.each do |field,value|
83
- r.push ds.client.hset(key, field, value)
100
+ data.each do |field,value|
101
+ f.push ds.client.hset(key, field, value)
84
102
  end
85
103
  when 'json'
86
- r = ds.client.set(key, JSON.generate(row))
104
+ f.push ds.client.set(key, JSON.generate(data))
87
105
  else
88
106
  raise %Q("encode: #{type}" is not supported)
89
107
  end
90
- yield r if block
91
- ds.logger.info "Key sample: #{key}" if @write_count == 0
108
+ f.push ds.client.expire(key, @expire) if @expire
92
109
  @write_count += 1
110
+ return f
111
+ end
112
+
113
+ def delete_key_columns(row)
114
+ r = row.dup
115
+ @key_columns.each do |key|
116
+ r.delete(key)
117
+ end
118
+ r.empty? ? {1 => 1} : r
93
119
  end
94
120
 
95
121
  def key(row)
96
- key_columns = @key_column.split(',').map(&:strip).map {|k| row[k]}
97
- prefix + key_columns.join('_')
122
+ prefix + @key_columns.map {|k| row[k]}.join('_')
98
123
  end
99
124
 
100
125
  def run
101
126
  begin
102
- import
127
+ ds.logger.info "Key Pattern: #{prefix}<#{@key_columns.join('_')}>"
128
+ ds.logger.info "Encode: #{@encode}"
129
+ ds.logger.info "Expire: #{@expire}"
130
+ raise "Unexpected error. Please check data." unless import
103
131
  rescue => ex
104
132
  ds.logger.error ex.backtrace.join("\n")
105
133
  raise JobFailure, ex.message
@@ -1,4 +1,4 @@
1
1
  module Bricolage
2
2
  APPLICATION_NAME = 'Bricolage'
3
- VERSION = '5.15.0'
3
+ VERSION = '5.15.1'
4
4
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- bricolage (5.15.0)
4
+ bricolage (5.15.1)
5
5
  aws-sdk (~> 2)
6
6
  mysql2
7
7
  pg
@@ -1,6 +1,9 @@
1
1
  class: redis-export
2
2
  src-ds: sql
3
3
  src-tables:
4
- users: $test_schema.users
4
+ user_cook_recipes: $test_schema.user_cook_recipes
5
5
  dest-ds: redis
6
- key-column: id, user_name
6
+ prefix: ha_ucp_
7
+ key-column: user_id, recipe_id
8
+ encode: hash
9
+ expire: 604800
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bricolage
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.15.0
4
+ version: 5.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Minero Aoki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-15 00:00:00.000000000 Z
11
+ date: 2016-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg