Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions redshift_console/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ def get(self):
results = tables.get_schemas()
self.write_json({'results': results})

class QueriesHistoryHandler(BaseHandler):
def get(self):
self.write_json({'queries_history': queries.queries_history.values(),
'updated_at': queries.queries_history_updated_at})

class QueriesInflightHandler(BaseHandler):
def get(self):
Expand Down Expand Up @@ -94,6 +98,7 @@ def create_app(debug):

return tornado.web.Application([(r"/", MainHandler),
(r"/api/queries/inflight$", QueriesInflightHandler),
(r"/api/queries/history$", QueriesHistoryHandler),
(r"/api/queries/queue$", QueriesQueueHandler),
(r"/api/queries/cancel/(.*)$", QueriesCancelHandler),
(r"/api/schemas$", SchemasHandler),
Expand Down
10 changes: 10 additions & 0 deletions redshift_console/queries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ inflight_queries:
JOIN pg_user
ON svv_query_inflight.userid = pg_user.usesysid
ORDER BY id, SEQUENCE;
queries_history:
SELECT query AS id,
database as db,
querytxt as query,
endtime AS endtime
FROM stl_query
WHERE querytxt NOT LIKE '%stl_query%'
AND querytxt NOT LIKE '%stl_alert_event_log%'
AND querytxt NOT LIKE '%svv_query_inflight%'
ORDER BY starttime DESC LIMIT 20;
queries_queue:
SELECT position,
start_time as timestamp,
Expand Down
9 changes: 9 additions & 0 deletions redshift_console/redshift.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ def __init__(self, session, refresh_interval):
self.queries_queue = []
self.queries_queue_updated_at = None
self.alerts_updated_at = None
self.queries_history = {}
self.queries_history_updated_at = None

def _set_cancellation_in_progres(self, pid):
for q in chain(self.inflight_queries.values(), self.queries_queue):
Expand Down Expand Up @@ -168,6 +170,7 @@ def refresh(self):
yield self._fetch_inflight_queries()
yield self._fetch_query_alerts()
yield self._fetch_queries_queue()
yield self._fetch_queries_history()

@coroutine
def _fetch_inflight_queries(self):
Expand All @@ -184,6 +187,12 @@ def _fetch_inflight_queries(self):

self.inflight_queries_updated_at = datetime.datetime.utcnow()

@coroutine
def _fetch_queries_history(self):
queries_history = yield self.execute_query(sql_queries['queries_history'])
self.queries_history = {q['id']: q for q in _concat_query_text(queries_history)}
self.queries_history_updated_at = datetime.datetime.utcnow()

@coroutine
def _fetch_query_alerts(self):
if not self.inflight_queries:
Expand Down
1 change: 1 addition & 0 deletions redshift_console/static/app/scripts/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ var routes = (
<Route name="app" path="/" handler={App}>
<Route name="queries" handler={queries.QueriesPage}>
<Route name="inflight" handler={queries.InflightQueries} />
<Route name="queries_history" handler={queries.QueriesHistory} />
<Route name="queries_queue" path="queue" handler={queries.QueriesQueue} />
<Redirect from="/queries" to="/queries/inflight" />
</Route>
Expand Down
62 changes: 60 additions & 2 deletions redshift_console/static/app/scripts/components/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ var QueriesPage = React.createClass({
queriesQueue: {
queries: [],
updatedAt: null
},
queriesHistory: {
queries: [],
updatedAt: null
}
};
},
Expand All @@ -30,6 +34,15 @@ var QueriesPage = React.createClass({
}
});

fetch('/api/queries/history').then(function(response) {
return response.json();
}).then(function(json) {
if (component.isMounted()) {
component.setState({queriesHistory: { queries: _.sortBy(json.queries_history, 'endtime'), updatedAt: json.updated_at}});
setTimeout(component.refresh, 100000);
}
});

fetch('/api/queries/queue').then(function(response) {
return response.json();
}).then(function(json) {
Expand All @@ -47,6 +60,10 @@ var QueriesPage = React.createClass({
name: <span>In Flight <span className="badge">{this.state.inflightQueries.queries.length}</span></span>,
route: 'inflight'
},
{
name: <span>Queries History <span className="badge">{this.state.queriesHistory.queries.length}</span></span>,
route: 'queries_history'
},
{
name: <span>Queries Queue <span className="badge">{this.state.queriesQueue.queries.length}</span></span>,
route: 'queries_queue'
Expand All @@ -56,7 +73,7 @@ var QueriesPage = React.createClass({
<div>
<Sidebar links={links} />
<div className="col-md-10">
<RouteHandler inflightQueries={this.state.inflightQueries} queriesQueue={this.state.queriesQueue}/>
<RouteHandler inflightQueries={this.state.inflightQueries} queriesQueue={this.state.queriesQueue} queriesHistory={this.state.queriesHistory}/>
</div>
</div>
);
Expand Down Expand Up @@ -119,6 +136,30 @@ var QueriesQueue = React.createClass({
}
})

var QueriesHistory = React.createClass({
render: function() {
var createItem = function(query) {
return <QueryHistory key={query.query} query={query} />;
};
return (
<div>
<div className="pull-right badge">Updated: <TimeAgo timestamp={this.props.queriesHistory.updatedAt} interval={5000} /></div>
<table className="table table-stripped queries">
<thead>
<tr>
<th>ID</th>
<th>DB</th>
<th>Query</th>
</tr>
</thead>
<tbody>
{this.props.queriesHistory.queries.map(createItem)}
</tbody>
</table>
</div>
);
}
})

var QueryExecutionTime = React.createClass({
componentWillMount: function() {
Expand Down Expand Up @@ -222,8 +263,25 @@ var Query = React.createClass({
}
});

var QueryHistory = React.createClass({
getInitialState: function() {
return {className: 'query collapsed'};
},
render: function() {
return (
<tr>
<td>{this.props.query.id}</td>
<td>{this.props.query.db}</td>
<td>{this.props.query.query}</td>
</tr>
);
}
});


module.exports = {
'QueriesPage': QueriesPage,
'InflightQueries': InflightQueries,
'QueriesQueue': QueriesQueue
'QueriesQueue': QueriesQueue,
'QueriesHistory': QueriesHistory
}