flare-up 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +34 -11
- data/lib/flare_up/boot.rb +1 -1
- data/lib/flare_up/stl_load_error.rb +7 -2
- data/lib/flare_up/stl_load_error_fetcher.rb +1 -1
- data/lib/flare_up/version.rb +1 -1
- data/spec/lib/flare_up/stl_load_error_fetcher_spec.rb +5 -3
- data/spec/lib/flare_up/stl_load_error_spec.rb +5 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NmI5NGU5NTk4NjAzNDE1ODY4YTQ1ODRjNGVhZmZjYjFkNjlhYjEzNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzhiOTAyMDk5YTlmODg3NmJlM2M5OWI5ZWYyOTJjMzZjMDdhZmI5ZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDQ1MjFiZjkyMGU1MTUyYjU2ZTVlYjQyNzAyMGYwMmM0MGVhYmZkMWM3NzFj
|
10
|
+
NDI0NmRmOGYwNDdiYWYwZTNhODBiZmZkZDVhYmUxNzhlODlkMGNlYTAzY2Nk
|
11
|
+
NmQ1MzFjMWQwODY1YjliMzM3NTkxNjY1ODRjYTlhN2Y4YjllY2Q=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YjRlOGVmMGFjYzdiYTUyMWU3NTlhODcwNmM2NTc1NTJhNjU3MjBmYzZhY2Jj
|
14
|
+
ZTAwNmU4NzgwZGJkMmFjMDE3MTc5YTllOGFkOTVjMjkyZTc1MjEyMjkyZTA2
|
15
|
+
MjU2YjFhOGJhZTYzMGJhNTg2ODhiY2MwZTM4NGM1YmE5YmJmYTg=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,27 +3,50 @@
|
|
3
3
|
|
4
4
|
## Why?
|
5
5
|
|
6
|
-
Redshift prefers a bulk COPY operation over indidivual INSERTs which Redshift is not optimized for, and Amazon does not recommend it as a strategy for
|
6
|
+
Redshift prefers a bulk COPY operation over indidivual INSERTs which Redshift is not optimized for, and Amazon does not recommend it as a strategy for loading. COPY is a SQL command, not something issued via the AWS Redshift REST API, meaning you need a SQL connection to your Redshift instance to bulk load data.
|
7
7
|
|
8
8
|
The astute consumer of the AWS toolchain will note that [Data Pipeline](http://aws.amazon.com/datapipeline/) is one way this import may be completed however, we use Azkaban and the only thing worse one than one job flow control tool is two job flow control tools :)
|
9
9
|
|
10
|
-
Additionally, access to COPY errors is a bit cumbersome. On failure, Redshift populates the ```stl_load_errors``` table which inherently must be accessed via SQL. Flare-up will pretty print any errors that occur during import so that you may examine your logs
|
10
|
+
Additionally, access to COPY errors is a bit cumbersome. On failure, Redshift populates the ```stl_load_errors``` table which inherently must be accessed via SQL. Flare-up will pretty print any errors that occur during import so that you may examine your logs rather than establishing a connection to Redshift to understand what went wrong.
|
11
|
+
|
12
|
+
## Requirements and Installation
|
13
|
+
|
14
|
+
The `pg` gem is a dependency (required to issue SQL commands to Redshift) and will be pulled down with flare-up.
|
11
15
|
|
12
16
|
```
|
13
|
-
|
17
|
+
> gem install flare-up
|
14
18
|
```
|
15
19
|
|
16
|
-
##
|
20
|
+
## Syntax
|
21
|
+
|
22
|
+
Available via `flare-up help copy`.
|
17
23
|
|
18
|
-
|
24
|
+
While we'd prefer if everyone stored configuration variables (esp. credentials) as environment variables (re: [Twelve-Factor App](http://12factor.net/)), it can be a pain to export variables when you're testing a tool and as such, we support specifying all of these on the command-line.
|
25
|
+
|
26
|
+
```
|
27
|
+
Usage:
|
28
|
+
flare-up copy DATA_SOURCE REDSHIFT_ENDPOINT DATABASE TABLE
|
29
|
+
|
30
|
+
Options:
|
31
|
+
[--aws-access-key=AWS_ACCESS_KEY] # Required unless ENV['AWS_ACCESS_KEY_ID'] is set.
|
32
|
+
[--aws-secret-key=AWS_SECRET_KEY] # Required unless ENV['AWS_SECRET_ACCESS_KEY'] is set.
|
33
|
+
[--redshift-username=REDSHIFT_USERNAME] # Required unless ENV['REDSHIFT_USERNAME'] is set.
|
34
|
+
[--redshift-password=REDSHIFT_PASSWORD] # Required unless ENV['REDSHIFT_PASSWORD'] is set.
|
35
|
+
[--column-list=one two three] # A space-separated list of columns, should your DATA_SOURCE require it
|
36
|
+
[--copy-options=COPY_OPTIONS] # Appended to the end of the COPY command; enclose "IN QUOTES"
|
37
|
+
```
|
19
38
|
|
20
|
-
|
39
|
+
## Sample Usage
|
21
40
|
|
22
|
-
|
41
|
+
Note that this example assumes you have credentials set as environment variables.
|
23
42
|
|
24
43
|
```
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
44
|
+
> flare-up \
|
45
|
+
copy \
|
46
|
+
s3://slif-redshift/hearthstone_cards_short_list.csv \
|
47
|
+
flare-up-test.cskjnp4xvaje.us-west-2.redshift.amazonaws.com \
|
48
|
+
dev \
|
49
|
+
hearthstone_cards \
|
50
|
+
--column-list name cost attack health description \
|
51
|
+
--copy_options "REGION 'us-east-1' CSV"
|
29
52
|
```
|
data/lib/flare_up/boot.rb
CHANGED
@@ -40,7 +40,7 @@ module FlareUp
|
|
40
40
|
# TODO: How can we test this?
|
41
41
|
def self.handle_load_errors(stl_load_errors)
|
42
42
|
return if stl_load_errors.empty?
|
43
|
-
puts "\x1b[31mThere was an error processing the COPY command
|
43
|
+
puts "\x1b[31mThere was an error processing the COPY command. Displaying the last (#{stl_load_errors.length}) errors."
|
44
44
|
stl_load_errors.each do |e|
|
45
45
|
puts e.pretty_print
|
46
46
|
end
|
@@ -11,8 +11,9 @@ module FlareUp
|
|
11
11
|
attr_reader :filename
|
12
12
|
attr_reader :position
|
13
13
|
attr_reader :line_number
|
14
|
+
attr_reader :start_time
|
14
15
|
|
15
|
-
def initialize(err_reason, raw_field_value, raw_line, col_length, type, colname, filename, position, line_number)
|
16
|
+
def initialize(err_reason, raw_field_value, raw_line, col_length, type, colname, filename, position, line_number, start_time)
|
16
17
|
@err_reason = err_reason
|
17
18
|
@raw_field_value = raw_field_value
|
18
19
|
@raw_line = raw_line
|
@@ -22,6 +23,7 @@ module FlareUp
|
|
22
23
|
@filename = filename
|
23
24
|
@position = position
|
24
25
|
@line_number = line_number
|
26
|
+
@start_time = start_time
|
25
27
|
end
|
26
28
|
|
27
29
|
def ==(other_error)
|
@@ -34,11 +36,13 @@ module FlareUp
|
|
34
36
|
return false unless @filename == other_error.filename
|
35
37
|
return false unless @position == other_error.position
|
36
38
|
return false unless @line_number == other_error.line_number
|
39
|
+
return false unless @start_time == other_error.start_time
|
37
40
|
true
|
38
41
|
end
|
39
42
|
|
40
43
|
def pretty_print
|
41
44
|
output = ''
|
45
|
+
output += "\e[33mSTART : \e[37m#{@start_time} (#{@start_time - 7 * 60 * 60} PST)\n"
|
42
46
|
output += "\e[33mREASON: \e[37m#{@err_reason}\n"
|
43
47
|
output += "\e[33mLINE : \e[37m#{@line_number}\n"
|
44
48
|
output += "\e[33mPOS : \e[37m#{@position}\n"
|
@@ -58,7 +62,8 @@ module FlareUp
|
|
58
62
|
row['colname'].strip,
|
59
63
|
row['filename'].strip,
|
60
64
|
row['position'].strip.to_i,
|
61
|
-
row['line_number'].strip.to_i
|
65
|
+
row['line_number'].strip.to_i,
|
66
|
+
Time.parse("#{row['starttime'].strip} UTC'")
|
62
67
|
)
|
63
68
|
end
|
64
69
|
|
@@ -3,7 +3,7 @@ module FlareUp
|
|
3
3
|
class STLLoadErrorFetcher
|
4
4
|
|
5
5
|
def self.fetch_errors(connection)
|
6
|
-
query_result = connection.execute('SELECT * FROM stl_load_errors ORDER BY query DESC, line_number, position LIMIT
|
6
|
+
query_result = connection.execute('SELECT * FROM stl_load_errors ORDER BY query DESC, line_number, position LIMIT 3')
|
7
7
|
errors = []
|
8
8
|
query_result.each do |row|
|
9
9
|
errors << STLLoadError.from_pg_results_row(row)
|
data/lib/flare_up/version.rb
CHANGED
@@ -6,7 +6,7 @@ describe FlareUp::STLLoadErrorFetcher do
|
|
6
6
|
|
7
7
|
before do
|
8
8
|
expect(connection).to receive(:execute).
|
9
|
-
with('SELECT * FROM stl_load_errors ORDER BY query DESC, line_number, position LIMIT
|
9
|
+
with('SELECT * FROM stl_load_errors ORDER BY query DESC, line_number, position LIMIT 3').
|
10
10
|
and_return([
|
11
11
|
{
|
12
12
|
'err_reason' => 'TEST_REASON',
|
@@ -17,7 +17,8 @@ describe FlareUp::STLLoadErrorFetcher do
|
|
17
17
|
'colname' => 'TEST_COLNAME',
|
18
18
|
'filename' => 'TEST_FILENAME',
|
19
19
|
'position' => '2',
|
20
|
-
'line_number' => '3'
|
20
|
+
'line_number' => '3',
|
21
|
+
'starttime' => '2014-08-11 06:06:59'
|
21
22
|
}
|
22
23
|
])
|
23
24
|
end
|
@@ -34,7 +35,8 @@ describe FlareUp::STLLoadErrorFetcher do
|
|
34
35
|
'TEST_COLNAME',
|
35
36
|
'TEST_FILENAME',
|
36
37
|
2,
|
37
|
-
3
|
38
|
+
3,
|
39
|
+
Time.parse('2014-08-11 06:06:59 UTC')
|
38
40
|
)
|
39
41
|
]
|
40
42
|
)
|
@@ -10,7 +10,8 @@ describe FlareUp::STLLoadError do
|
|
10
10
|
'TEST_COLNAME',
|
11
11
|
'TEST_FILENAME',
|
12
12
|
2,
|
13
|
-
3
|
13
|
+
3,
|
14
|
+
Time.parse('2014-08-11 06:06:59 UTC')
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
@@ -35,7 +36,8 @@ describe FlareUp::STLLoadError do
|
|
35
36
|
'colname' => 'TEST_COLNAME ',
|
36
37
|
'filename' => 'TEST_FILENAME ',
|
37
38
|
'position' => '2 ',
|
38
|
-
'line_number' => '3 '
|
39
|
+
'line_number' => '3 ',
|
40
|
+
'starttime' => '2014-08-11 06:06:59 '
|
39
41
|
}
|
40
42
|
end
|
41
43
|
|
@@ -55,6 +57,7 @@ describe FlareUp::STLLoadError do
|
|
55
57
|
expect(load_error_from_hash.filename).to eq('TEST_FILENAME')
|
56
58
|
expect(load_error_from_hash.position).to eq(2)
|
57
59
|
expect(load_error_from_hash.line_number).to eq(3)
|
60
|
+
expect(load_error_from_hash.start_time).to eq(Time.parse('2014-08-11 06:06:59 UTC'))
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|