blazer 1.6.2 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of blazer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +16 -16
- data/app/assets/javascripts/blazer/application.js +64 -1
- data/app/controllers/blazer/queries_controller.rb +41 -23
- data/app/mailers/blazer/check_mailer.rb +2 -1
- data/app/models/blazer/query.rb +2 -2
- data/app/views/blazer/queries/_form.html.erb +9 -0
- data/app/views/blazer/queries/home.html.erb +9 -2
- data/app/views/layouts/blazer/application.html.erb +1 -0
- data/config/routes.rb +1 -0
- data/lib/blazer/adapters/base_adapter.rb +4 -0
- data/lib/blazer/adapters/sql_adapter.rb +22 -6
- data/lib/blazer/data_source.rb +13 -5
- data/lib/blazer/detect_anomalies.R +2 -2
- data/lib/blazer/run_statement.rb +31 -29
- data/lib/blazer/run_statement_job.rb +1 -1
- data/lib/blazer/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 51a0d5ecca671fbd2647ea5fcfac3235da673fb1
|
4
|
+
data.tar.gz: 5b502ae948a5e7ecc1c28f7c847f9937274922a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 678d3473e2aa19a0e2ae5078f54a0d82661e69d60b4dc8f8b0ca4753d5fe7c36404aac1a1241a6bea12ff823464d918843a7e443884ef6e78ec43020f6c03490
|
7
|
+
data.tar.gz: 16d6ad910db22bd09ff36cfb6f70d158c8b267bd4f306bf42a6bb23e9233f99d323779b4d9b5556ad8bdbcd7adead7cd4f745d9e88d2908e742d5973031d480a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 1.7.0
|
2
|
+
|
3
|
+
- Added ability to cancel queries on backend for Postgres and Redshift
|
4
|
+
- Only run 3 queries at a time on dashboards
|
5
|
+
- Better anomaly detection
|
6
|
+
- Attempt to reconnect when connection issues
|
7
|
+
- Fixed issues with caching
|
8
|
+
|
1
9
|
## 1.6.2
|
2
10
|
|
3
11
|
- Added basic query permissions
|
data/README.md
CHANGED
@@ -4,11 +4,11 @@ Explore your data with SQL. Easily create charts and dashboards, and share them
|
|
4
4
|
|
5
5
|
[Try it out](https://blazerme.herokuapp.com)
|
6
6
|
|
7
|
-
[![Screenshot](https://blazerme.herokuapp.com/assets/screenshot-
|
7
|
+
[![Screenshot](https://blazerme.herokuapp.com/assets/screenshot-6ca3115a518b488026e48be83ba0d4c9.png)](https://blazerme.herokuapp.com)
|
8
8
|
|
9
|
-
:
|
9
|
+
:envelope: [Get notified of updates](http://eepurl.com/cbUwsD)
|
10
10
|
|
11
|
-
:
|
11
|
+
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
12
12
|
|
13
13
|
## Features
|
14
14
|
|
@@ -373,9 +373,9 @@ data_sources:
|
|
373
373
|
- IBM DB2 and Informix
|
374
374
|
- SQLite
|
375
375
|
- [Redshift](#redshift)
|
376
|
+
- [Presto](#presto)
|
376
377
|
- [MongoDB](#mongodb) [beta]
|
377
378
|
- [Elasticsearch](#elasticsearch) [beta]
|
378
|
-
- [Presto](#presto) [beta]
|
379
379
|
|
380
380
|
You can also create an adapter for any other data store.
|
381
381
|
|
@@ -397,40 +397,40 @@ data_sources:
|
|
397
397
|
url: redshift://user:password@hostname:5439/database
|
398
398
|
```
|
399
399
|
|
400
|
-
###
|
400
|
+
### Presto
|
401
401
|
|
402
|
-
Add [
|
402
|
+
Add [presto-client](https://github.com/treasure-data/presto-client-ruby) to your Gemfile and set:
|
403
403
|
|
404
404
|
```yml
|
405
405
|
data_sources:
|
406
406
|
my_source:
|
407
|
-
url:
|
407
|
+
url: presto://user@hostname:8080/catalog
|
408
408
|
```
|
409
409
|
|
410
|
-
###
|
410
|
+
### MongoDB
|
411
411
|
|
412
|
-
Add [
|
412
|
+
Add [mongo](https://github.com/mongodb/mongo-ruby-driver) to your Gemfile and set:
|
413
413
|
|
414
414
|
```yml
|
415
415
|
data_sources:
|
416
416
|
my_source:
|
417
|
-
|
418
|
-
url: http://user:password@hostname:9200/
|
417
|
+
url: mongodb://user:password@hostname:27017/database
|
419
418
|
```
|
420
419
|
|
421
|
-
###
|
420
|
+
### Elasticsearch
|
422
421
|
|
423
|
-
Add [
|
422
|
+
Add [elasticsearch](https://github.com/elastic/elasticsearch-ruby) to your Gemfile and set:
|
424
423
|
|
425
424
|
```yml
|
426
425
|
data_sources:
|
427
426
|
my_source:
|
428
|
-
|
427
|
+
adapter: elasticsearch
|
428
|
+
url: http://user:password@hostname:9200/
|
429
429
|
```
|
430
430
|
|
431
|
-
## Permissions
|
431
|
+
## Query Permissions
|
432
432
|
|
433
|
-
Blazer supports a
|
433
|
+
Blazer supports a basic permissions model.
|
434
434
|
|
435
435
|
1. Queries without a name are unlisted
|
436
436
|
2. Queries whose name starts with `#` are only listed to the creator
|
@@ -25,15 +25,76 @@ $( function () {
|
|
25
25
|
});
|
26
26
|
});
|
27
27
|
|
28
|
+
function uuid() {
|
29
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
30
|
+
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
31
|
+
return v.toString(16);
|
32
|
+
});
|
33
|
+
}
|
34
|
+
|
28
35
|
function cancelQuery(runningQuery) {
|
29
36
|
runningQuery.canceled = true;
|
30
37
|
var xhr = runningQuery.xhr;
|
31
38
|
if (xhr) {
|
32
39
|
xhr.abort();
|
33
40
|
}
|
41
|
+
remoteCancelQuery(runningQuery);
|
42
|
+
queryComplete();
|
43
|
+
}
|
44
|
+
|
45
|
+
function csrfProtect(payload) {
|
46
|
+
var param = $("meta[name=csrf-param]").attr("content");
|
47
|
+
var token = $("meta[name=csrf-token]").attr("content");
|
48
|
+
if (param && token) payload[param] = token;
|
49
|
+
return new Blob([JSON.stringify(payload)], {type : "application/json; charset=utf-8"});
|
50
|
+
}
|
51
|
+
|
52
|
+
function remoteCancelQuery(runningQuery) {
|
53
|
+
var path = window.cancelQueriesPath;
|
54
|
+
var data = {run_id: runningQuery.run_id, data_source: runningQuery.data_source};
|
55
|
+
if (navigator.sendBeacon) {
|
56
|
+
navigator.sendBeacon(path, csrfProtect(data));
|
57
|
+
} else {
|
58
|
+
// TODO make sync
|
59
|
+
$.post(path, data);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
var queriesQueue = [];
|
64
|
+
var runningQueries = 0;
|
65
|
+
var maxQueries = 3;
|
66
|
+
|
67
|
+
function queueQuery(callback) {
|
68
|
+
queriesQueue.push(callback);
|
69
|
+
runNext();
|
70
|
+
}
|
71
|
+
|
72
|
+
function runNext() {
|
73
|
+
if (runningQueries < maxQueries) {
|
74
|
+
var callback = queriesQueue.shift();
|
75
|
+
if (callback) {
|
76
|
+
runningQueries++;
|
77
|
+
callback();
|
78
|
+
runNext();
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
function queryComplete() {
|
84
|
+
runningQueries--;
|
85
|
+
runNext();
|
34
86
|
}
|
35
87
|
|
36
88
|
function runQuery(data, success, error, runningQuery) {
|
89
|
+
queueQuery( function () {
|
90
|
+
runningQuery = runningQuery || {};
|
91
|
+
runningQuery.run_id = data.run_id = uuid();
|
92
|
+
runningQuery.data_source = data.data_source;
|
93
|
+
return runQueryHelper(data, success, error, runningQuery);
|
94
|
+
});
|
95
|
+
}
|
96
|
+
|
97
|
+
function runQueryHelper(data, success, error, runningQuery) {
|
37
98
|
var xhr = $.ajax({
|
38
99
|
url: window.runQueriesPath,
|
39
100
|
method: "POST",
|
@@ -45,15 +106,17 @@ function runQuery(data, success, error, runningQuery) {
|
|
45
106
|
data.blazer = response;
|
46
107
|
setTimeout( function () {
|
47
108
|
if (!(runningQuery && runningQuery.canceled)) {
|
48
|
-
|
109
|
+
runQueryHelper(data, success, error, runningQuery);
|
49
110
|
}
|
50
111
|
}, 1000);
|
51
112
|
} else {
|
52
113
|
success(d);
|
114
|
+
queryComplete();
|
53
115
|
}
|
54
116
|
}).fail( function(jqXHR, textStatus, errorThrown) {
|
55
117
|
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message;
|
56
118
|
error(message);
|
119
|
+
queryComplete();
|
57
120
|
});
|
58
121
|
if (runningQuery) {
|
59
122
|
runningQuery.xhr = xhr;
|
@@ -5,11 +5,13 @@ module Blazer
|
|
5
5
|
def home
|
6
6
|
set_queries(1000)
|
7
7
|
|
8
|
-
|
9
|
-
@dashboards = @dashboards.includes(:creator) if Blazer.user_class
|
10
|
-
if params[:filter] == "mine"
|
8
|
+
if params[:filter]
|
11
9
|
@dashboards = [] # TODO show my dashboards
|
10
|
+
else
|
11
|
+
@dashboards = Blazer::Dashboard.order(:name)
|
12
|
+
@dashboards = @dashboards.includes(:creator) if Blazer.user_class
|
12
13
|
end
|
14
|
+
|
13
15
|
@dashboards =
|
14
16
|
@dashboards.map do |d|
|
15
17
|
{
|
@@ -91,9 +93,9 @@ module Blazer
|
|
91
93
|
@cached_at = nil
|
92
94
|
params[:data_source] = nil
|
93
95
|
render_run
|
94
|
-
elsif Time.now > Time.at(@timestamp + (@data_source.timeout ||
|
95
|
-
#
|
96
|
-
@error =
|
96
|
+
elsif Time.now > Time.at(@timestamp + (@data_source.timeout || 600).to_i + 5)
|
97
|
+
# query lost
|
98
|
+
@error = "We lost your query :("
|
97
99
|
@rows = []
|
98
100
|
@columns = []
|
99
101
|
render_run
|
@@ -101,9 +103,9 @@ module Blazer
|
|
101
103
|
continue_run
|
102
104
|
end
|
103
105
|
elsif @success
|
104
|
-
@run_id =
|
106
|
+
@run_id = blazer_run_id
|
105
107
|
|
106
|
-
options = {user: blazer_user, query: @query, refresh_cache: params[:check], run_id: @run_id}
|
108
|
+
options = {user: blazer_user, query: @query, refresh_cache: params[:check], run_id: @run_id, async: Blazer.async}
|
107
109
|
if Blazer.async && request.format.symbol != :csv
|
108
110
|
result = []
|
109
111
|
Blazer::RunStatementJob.perform_async(result, @data_source, @statement, options)
|
@@ -114,7 +116,7 @@ module Blazer
|
|
114
116
|
end
|
115
117
|
@result = result.first
|
116
118
|
else
|
117
|
-
@result = RunStatement.new.perform(@data_source, @statement, options)
|
119
|
+
@result = Blazer::RunStatement.new.perform(@data_source, @statement, options)
|
118
120
|
end
|
119
121
|
|
120
122
|
if @result
|
@@ -174,6 +176,11 @@ module Blazer
|
|
174
176
|
@schema = Blazer.data_sources[params[:data_source]].schema
|
175
177
|
end
|
176
178
|
|
179
|
+
def cancel
|
180
|
+
Blazer.data_sources[params[:data_source]].cancel(blazer_run_id)
|
181
|
+
render json: {}
|
182
|
+
end
|
183
|
+
|
177
184
|
private
|
178
185
|
|
179
186
|
def continue_run
|
@@ -191,7 +198,7 @@ module Blazer
|
|
191
198
|
case @first_row[i]
|
192
199
|
when Integer
|
193
200
|
"int"
|
194
|
-
when Float
|
201
|
+
when Float, BigDecimal
|
195
202
|
"float"
|
196
203
|
else
|
197
204
|
"string-ins"
|
@@ -237,24 +244,24 @@ module Blazer
|
|
237
244
|
|
238
245
|
def set_queries(limit = nil)
|
239
246
|
@my_queries =
|
240
|
-
if limit && blazer_user && !params[:filter]
|
241
|
-
|
242
|
-
queries = Blazer::Query.named.where(id: favorite_query_ids)
|
243
|
-
queries = queries.includes(:creator) if Blazer.user_class
|
244
|
-
queries = queries.index_by(&:id)
|
245
|
-
favorite_query_ids.map { |query_id| queries[query_id] }.compact
|
247
|
+
if limit && blazer_user && !params[:filter] && Blazer.audit
|
248
|
+
queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).where("created_at > ?", 30.days.ago).where("query_id IS NOT NULL").group(:query_id).order("count_all desc").count.keys)
|
246
249
|
else
|
247
250
|
[]
|
248
251
|
end
|
249
252
|
|
250
|
-
@queries = Blazer::Query.named
|
251
|
-
if params[:filter] == "mine"
|
252
|
-
@queries = @queries.where(creator_id: blazer_user.try(:id)).reorder(updated_at: :desc)
|
253
|
-
limit = nil
|
254
|
-
end
|
255
|
-
@queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
|
253
|
+
@queries = Blazer::Query.named
|
256
254
|
@queries = @queries.includes(:creator) if Blazer.user_class
|
257
|
-
|
255
|
+
|
256
|
+
if blazer_user && params[:filter] == "mine"
|
257
|
+
@queries = @queries.where(creator_id: blazer_user.id).reorder(updated_at: :desc)
|
258
|
+
elsif blazer_user && params[:filter] == "viewed" && Blazer.audit
|
259
|
+
@queries = queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).order(created_at: :desc).limit(500).pluck(:query_id).uniq)
|
260
|
+
else
|
261
|
+
@queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
|
262
|
+
@queries = @queries.limit(limit) if limit
|
263
|
+
@queries = @queries.order(:name)
|
264
|
+
end
|
258
265
|
@queries = @queries.to_a
|
259
266
|
|
260
267
|
@more = limit && @queries.size >= limit
|
@@ -273,6 +280,13 @@ module Blazer
|
|
273
280
|
end
|
274
281
|
end
|
275
282
|
|
283
|
+
def queries_by_ids(favorite_query_ids)
|
284
|
+
queries = Blazer::Query.named.where(id: favorite_query_ids)
|
285
|
+
queries = queries.includes(:creator) if Blazer.user_class
|
286
|
+
queries = queries.index_by(&:id)
|
287
|
+
favorite_query_ids.map { |query_id| queries[query_id] }.compact
|
288
|
+
end
|
289
|
+
|
276
290
|
def set_query
|
277
291
|
@query = Blazer::Query.find(params[:id].to_s.split("-").first)
|
278
292
|
end
|
@@ -298,5 +312,9 @@ module Blazer
|
|
298
312
|
data_source.local_time_suffix.any? { |s| k.ends_with?(s) } ? v.to_s.sub(" UTC", "") : v.in_time_zone(Blazer.time_zone)
|
299
313
|
end
|
300
314
|
helper_method :blazer_time_value
|
315
|
+
|
316
|
+
def blazer_run_id
|
317
|
+
params[:run_id].to_s.gsub(/[^a-z0-9\-]/i, "")
|
318
|
+
end
|
301
319
|
end
|
302
320
|
end
|
@@ -15,7 +15,8 @@ module Blazer
|
|
15
15
|
|
16
16
|
def failing_checks(email, checks)
|
17
17
|
@checks = checks
|
18
|
-
|
18
|
+
# add reply_to for mailing lists
|
19
|
+
mail to: email, reply_to: email, subject: "#{pluralize(checks.size, "Check")} Failing"
|
19
20
|
end
|
20
21
|
end
|
21
22
|
end
|
data/app/models/blazer/query.rb
CHANGED
@@ -8,14 +8,14 @@ module Blazer
|
|
8
8
|
|
9
9
|
validates :statement, presence: true
|
10
10
|
|
11
|
-
scope :named, -> { where("name <> ''") }
|
11
|
+
scope :named, -> { where("blazer_queries.name <> ''") }
|
12
12
|
|
13
13
|
def to_param
|
14
14
|
[id, name].compact.join("-").gsub("'", "").parameterize
|
15
15
|
end
|
16
16
|
|
17
17
|
def friendly_name
|
18
|
-
name.to_s.gsub(/\[.+\]/, "").strip
|
18
|
+
name.to_s.sub(/\A[#\*]/, "").gsub(/\[.+\]/, "").strip
|
19
19
|
end
|
20
20
|
|
21
21
|
def editable?(user)
|
@@ -88,6 +88,8 @@
|
|
88
88
|
},
|
89
89
|
readOnly: false // false if this command should not apply in readOnly mode
|
90
90
|
});
|
91
|
+
// fix command+L
|
92
|
+
editor.commands.removeCommands(["gotoline"]);
|
91
93
|
|
92
94
|
// http://stackoverflow.com/questions/11584061/
|
93
95
|
function adjustHeight() {
|
@@ -131,6 +133,7 @@
|
|
131
133
|
|
132
134
|
|
133
135
|
function queryDone() {
|
136
|
+
runningQuery = null
|
134
137
|
$("#run").removeClass("hide")
|
135
138
|
$("#cancel").addClass("hide")
|
136
139
|
}
|
@@ -144,6 +147,12 @@
|
|
144
147
|
$("#results").html("")
|
145
148
|
})
|
146
149
|
|
150
|
+
$(window).unload(function() {
|
151
|
+
if (runningQuery) {
|
152
|
+
remoteCancelQuery(runningQuery)
|
153
|
+
}
|
154
|
+
})
|
155
|
+
|
147
156
|
$("#run").click( function (e) {
|
148
157
|
e.preventDefault();
|
149
158
|
|
@@ -1,8 +1,15 @@
|
|
1
1
|
<div id="queries">
|
2
2
|
<div id="header" style="margin-bottom: 20px;">
|
3
3
|
<div class="pull-right">
|
4
|
-
|
5
|
-
|
4
|
+
<% if blazer_user %>
|
5
|
+
<%= link_to "All", root_path, class: !params[:filter] ? "active" : nil, style: "margin-right: 40px;" %>
|
6
|
+
|
7
|
+
<% if Blazer.audit %>
|
8
|
+
<%= link_to "Viewed", root_path(filter: "viewed"), class: params[:filter] == "viewed" ? "active" : nil, style: "margin-right: 40px;" %>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<%= link_to "Mine", root_path(filter: "mine"), class: params[:filter] == "mine" ? "active" : nil, style: "margin-right: 40px;" %>
|
12
|
+
<% end %>
|
6
13
|
<div class="btn-group">
|
7
14
|
<%= link_to "New Query", new_query_path, class: "btn btn-info" %>
|
8
15
|
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
@@ -9,6 +9,7 @@
|
|
9
9
|
<%= javascript_include_tag "blazer/application" %>
|
10
10
|
<script>
|
11
11
|
var runQueriesPath = <%= run_queries_path.to_json.html_safe %>;
|
12
|
+
var cancelQueriesPath = <%= cancel_queries_path.to_json.html_safe %>;
|
12
13
|
</script>
|
13
14
|
<% if blazer_maps? %>
|
14
15
|
<%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.css" %>
|
data/config/routes.rb
CHANGED
@@ -9,7 +9,7 @@ module Blazer
|
|
9
9
|
@connection_model =
|
10
10
|
Class.new(Blazer::Connection) do
|
11
11
|
def self.name
|
12
|
-
"Blazer::Connection
|
12
|
+
"Blazer::Connection::Adapter#{object_id}"
|
13
13
|
end
|
14
14
|
establish_connection(data_source.settings["url"]) if data_source.settings["url"]
|
15
15
|
end
|
@@ -24,16 +24,17 @@ module Blazer
|
|
24
24
|
in_transaction do
|
25
25
|
set_timeout(data_source.timeout) if data_source.timeout
|
26
26
|
|
27
|
-
result =
|
27
|
+
result = select_all("#{statement} /*#{comment}*/")
|
28
28
|
columns = result.columns
|
29
29
|
cast_method = Rails::VERSION::MAJOR < 5 ? :type_cast : :cast_value
|
30
30
|
result.rows.each do |untyped_row|
|
31
31
|
rows << (result.column_types.empty? ? untyped_row : columns.each_with_index.map { |c, i| untyped_row[i] ? result.column_types[c].send(cast_method, untyped_row[i]) : nil })
|
32
32
|
end
|
33
33
|
end
|
34
|
-
rescue
|
34
|
+
rescue => e
|
35
35
|
error = e.message.sub(/.+ERROR: /, "")
|
36
36
|
error = Blazer::TIMEOUT_MESSAGE if Blazer::TIMEOUT_ERRORS.any? { |e| error.include?(e) }
|
37
|
+
reconnect if error.include?("can't get socket descriptor")
|
37
38
|
end
|
38
39
|
|
39
40
|
[columns, rows, error]
|
@@ -65,14 +66,29 @@ module Blazer
|
|
65
66
|
|
66
67
|
def explain(statement)
|
67
68
|
if postgresql? || redshift?
|
68
|
-
|
69
|
+
select_all("EXPLAIN #{statement}").rows.first.first
|
69
70
|
end
|
70
71
|
rescue
|
71
72
|
nil
|
72
73
|
end
|
73
74
|
|
75
|
+
def cancel(run_id)
|
76
|
+
if postgresql?
|
77
|
+
select_all("SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND query LIKE '%,run_id:#{run_id}%'")
|
78
|
+
elsif redshift?
|
79
|
+
first_row = select_all("SELECT pid FROM stv_recents WHERE status = 'Running' AND query LIKE '%,run_id:#{run_id}%'").first
|
80
|
+
if first_row
|
81
|
+
select_all("CANCEL #{first_row["pid"].to_i}")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
74
86
|
protected
|
75
87
|
|
88
|
+
def select_all(statement)
|
89
|
+
connection_model.connection.select_all(statement)
|
90
|
+
end
|
91
|
+
|
76
92
|
def postgresql?
|
77
93
|
["PostgreSQL", "PostGIS"].include?(adapter_name)
|
78
94
|
end
|
@@ -96,9 +112,9 @@ module Blazer
|
|
96
112
|
|
97
113
|
def set_timeout(timeout)
|
98
114
|
if postgresql? || redshift?
|
99
|
-
|
115
|
+
select_all("SET statement_timeout = #{timeout.to_i * 1000}")
|
100
116
|
elsif mysql?
|
101
|
-
|
117
|
+
select_all("SET max_execution_time = #{timeout.to_i * 1000}")
|
102
118
|
else
|
103
119
|
raise Blazer::TimeoutNotSupported, "Timeout not supported for #{adapter_name} adapter"
|
104
120
|
end
|
data/lib/blazer/data_source.rb
CHANGED
@@ -6,7 +6,7 @@ module Blazer
|
|
6
6
|
|
7
7
|
attr_reader :id, :settings, :adapter, :adapter_instance
|
8
8
|
|
9
|
-
def_delegators :adapter_instance, :schema, :tables, :preview_statement, :reconnect, :cost, :explain
|
9
|
+
def_delegators :adapter_instance, :schema, :tables, :preview_statement, :reconnect, :cost, :explain, :cancel
|
10
10
|
|
11
11
|
def initialize(id, settings)
|
12
12
|
@id = id
|
@@ -109,9 +109,14 @@ module Blazer
|
|
109
109
|
|
110
110
|
def run_statement(statement, options = {})
|
111
111
|
run_id = options[:run_id]
|
112
|
+
async = options[:async]
|
112
113
|
result = nil
|
113
|
-
if cache_mode != "off"
|
114
|
-
|
114
|
+
if cache_mode != "off"
|
115
|
+
if options[:refresh_cache]
|
116
|
+
clear_cache(statement) # for checks
|
117
|
+
else
|
118
|
+
result = read_cache(statement_cache_key(statement))
|
119
|
+
end
|
115
120
|
end
|
116
121
|
|
117
122
|
unless result
|
@@ -129,7 +134,10 @@ module Blazer
|
|
129
134
|
if options[:check]
|
130
135
|
comment << ",check_id:#{options[:check].id},check_emails:#{options[:check].emails}"
|
131
136
|
end
|
132
|
-
|
137
|
+
if options[:run_id]
|
138
|
+
comment << ",run_id:#{options[:run_id]}"
|
139
|
+
end
|
140
|
+
result = run_statement_helper(statement, comment, async ? options[:run_id] : nil)
|
133
141
|
end
|
134
142
|
|
135
143
|
result
|
@@ -144,7 +152,7 @@ module Blazer
|
|
144
152
|
end
|
145
153
|
|
146
154
|
def statement_cache_key(statement)
|
147
|
-
cache_key(["statement", id, Digest::MD5.hexdigest(statement)])
|
155
|
+
cache_key(["statement", id, Digest::MD5.hexdigest(statement.to_s.gsub("\r\n", "\n"))])
|
148
156
|
end
|
149
157
|
|
150
158
|
def run_cache_key(run_id)
|
@@ -8,9 +8,9 @@ tryCatch({
|
|
8
8
|
data$timestamp <- as.POSIXct(data$timestamp)
|
9
9
|
|
10
10
|
if (identical(args[1], "ts")) {
|
11
|
-
res
|
11
|
+
res <- AnomalyDetectionTs(data, direction = "both", alpha = 0.05, max_anoms = 0.2)
|
12
12
|
} else {
|
13
|
-
res
|
13
|
+
res <- AnomalyDetectionVec(data$count, direction = "both", alpha = 0.05, max_anoms = 0.2, period = length(data$count) / 2 - 1)
|
14
14
|
}
|
15
15
|
|
16
16
|
write.csv(res$anoms)
|
data/lib/blazer/run_statement.rb
CHANGED
@@ -1,38 +1,40 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Blazer
|
2
|
+
class RunStatement
|
3
|
+
def perform(data_source, statement, options = {})
|
4
|
+
query = options[:query]
|
5
|
+
Blazer.transform_statement.call(data_source, statement) if Blazer.transform_statement
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
# audit
|
8
|
+
if Blazer.audit
|
9
|
+
audit = Blazer::Audit.new(statement: statement)
|
10
|
+
audit.query = query
|
11
|
+
audit.data_source = data_source.id
|
12
|
+
audit.user = options[:user]
|
13
|
+
audit.save!
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
start_time = Time.now
|
17
|
+
result = data_source.run_statement(statement, options)
|
18
|
+
duration = Time.now - start_time
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
if Blazer.audit
|
21
|
+
audit.duration = duration if audit.respond_to?(:duration=)
|
22
|
+
audit.error = result.error if audit.respond_to?(:error=)
|
23
|
+
audit.timed_out = result.timed_out? if audit.respond_to?(:timed_out=)
|
24
|
+
audit.cached = result.cached? if audit.respond_to?(:cached=)
|
25
|
+
if !result.cached? && duration >= 10
|
26
|
+
audit.cost = data_source.cost(statement) if audit.respond_to?(:cost=)
|
27
|
+
end
|
28
|
+
audit.save! if audit.changed?
|
26
29
|
end
|
27
|
-
audit.save! if audit.changed?
|
28
|
-
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
if query && !result.timed_out?
|
32
|
+
query.checks.each do |check|
|
33
|
+
check.update_state(result)
|
34
|
+
end
|
33
35
|
end
|
34
|
-
end
|
35
36
|
|
36
|
-
|
37
|
+
result
|
38
|
+
end
|
37
39
|
end
|
38
40
|
end
|
@@ -8,7 +8,7 @@ module Blazer
|
|
8
8
|
def perform(result, data_source, statement, options)
|
9
9
|
begin
|
10
10
|
ActiveRecord::Base.connection_pool.with_connection do
|
11
|
-
result << RunStatement.new.perform(data_source, statement, options)
|
11
|
+
result << Blazer::RunStatement.new.perform(data_source, statement, options)
|
12
12
|
end
|
13
13
|
rescue Exception => e
|
14
14
|
result.clear
|
data/lib/blazer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blazer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08
|
11
|
+
date: 2016-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -196,9 +196,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
196
|
version: '0'
|
197
197
|
requirements: []
|
198
198
|
rubyforge_project:
|
199
|
-
rubygems_version: 2.
|
199
|
+
rubygems_version: 2.5.1
|
200
200
|
signing_key:
|
201
201
|
specification_version: 4
|
202
202
|
summary: Share data effortlessly with your team
|
203
203
|
test_files: []
|
204
|
-
has_rdoc:
|