bricolage 5.15.0 → 5.15.1

Sign up to get free protection for your applications and to get access to all the features.
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