Module: check_mk
Branch: master
Commit: d01f66d6a75f2f5479ba0dcafbb8df97a4497f63
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=d01f66d6a75f2f…
Author: Mathias Kettner <mk(a)mathias-kettner.de>
Date: Mon Dec 16 14:20:01 2013 +0100
New header for limiting the execution time of a query
The new Livestatus header Timelimit: sets an upper limit
for the execution time of a query. Specify the timeout as
a number of seconds. The following query will stop after
5000 lines of output. If that takes longer than 30 seconds,
then the query <b>fails with no output</b>:
F+:query.lql
GET log
Columns: message
Timelimit: 30
Limit: 5000
F-:
Please note, that <tt>Limit:</tt> and <tt>Timelimit:</tt> have
a different behaviour when the limit is reached:
<li class=list><tt>Limit:</tt> end the query, output all rows that
have been found so far.</li>
<li class=list><tt>Timelimit:</tt> abort the query with an error,
do not output any lines.</li>
---
.werks/337 | 29 +++++++++++++++++++++++++++++
ChangeLog | 1 +
livestatus/src/OutputBuffer.h | 1 +
livestatus/src/Query.cc | 33 +++++++++++++++++++++++++++++++++
livestatus/src/Query.h | 3 +++
5 files changed, 67 insertions(+)
diff --git a/.werks/337 b/.werks/337
new file mode 100644
index 0000000..90fe85e
--- /dev/null
+++ b/.werks/337
@@ -0,0 +1,29 @@
+Title: New header for limiting the execution time of a query
+Level: 1
+Component: livestatus
+Class: feature
+State: unknown
+Version: 1.2.5i1
+Date: 1387199727
+Targetversion: future
+
+The new Livestatus header Timelimit: sets an upper limit
+for the execution time of a query. Specify the timeout as
+a number of seconds. The following query will stop after
+5000 lines of output. If that takes longer than 30 seconds,
+then the query <b>fails with no output</b>:
+
+F+:query.lql
+GET log
+Columns: message
+Timelimit: 30
+Limit: 5000
+F-:
+
+Please note, that <tt>Limit:</tt> and <tt>Timelimit:</tt> have
+a different behaviour when the limit is reached:
+
+<li class=list><tt>Limit:</tt> end the query, output all rows that
+have been found so far.</li>
+<li class=list><tt>Timelimit:</tt> abort the query with an error,
+do not output any lines.</li>
diff --git a/ChangeLog b/ChangeLog
index 089e3cc..4d20cfe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -65,6 +65,7 @@
* 0089 FIX: CSV export of event console was broken...
Livestatus:
+ * 0337 New header for limiting the execution time of a query...
* 0335 FIX: Parse state of downtime notification log entries correctly...
* 0336 FIX: Limit the number of lines read from a single logfile...
diff --git a/livestatus/src/OutputBuffer.h b/livestatus/src/OutputBuffer.h
index b86a583..433435a 100644
--- a/livestatus/src/OutputBuffer.h
+++ b/livestatus/src/OutputBuffer.h
@@ -36,6 +36,7 @@ using namespace std;
#define RESPONSE_CODE_INVALID_HEADER 400
#define RESPONSE_CODE_UNAUTHORIZED 403
#define RESPONSE_CODE_NOT_FOUND 404
+#define RESPONSE_CODE_LIMIT_EXCEEDED 413
#define RESPONSE_CODE_INCOMPLETE_REQUEST 451
#define RESPONSE_CODE_INVALID_REQUEST 452
#define RESPONSE_CODE_UNKNOWN_COLUMN 450
diff --git a/livestatus/src/Query.cc b/livestatus/src/Query.cc
index 138ce94..2da047b 100644
--- a/livestatus/src/Query.cc
+++ b/livestatus/src/Query.cc
@@ -65,6 +65,8 @@ Query::Query(InputBuffer *input, OutputBuffer *output, Table *table) :
_need_ds_separator(false),
_output_format(OUTPUT_FORMAT_CSV),
_limit(-1),
+ _time_limit(-1),
+ _time_limit_timeout(0),
_current_line(0),
_timezone_offset(0)
{
@@ -111,6 +113,9 @@ Query::Query(InputBuffer *input, OutputBuffer *output, Table *table)
:
else if (!strncmp(buffer, "Limit:", 6))
parseLimitLine(lstrip(buffer + 6));
+ else if (!strncmp(buffer, "Timelimit:", 10))
+ parseTimelimitLine(lstrip(buffer + 10));
+
else if (!strncmp(buffer, "AuthUser:", 9))
parseAuthUserHeader(lstrip(buffer + 9));
@@ -617,6 +622,26 @@ void Query::parseLimitLine(char *line)
}
}
+
+void Query::parseTimelimitLine(char *line)
+{
+ char *value = next_field(&line);
+ if (!value) {
+ _output->setError(RESPONSE_CODE_INVALID_HEADER, "Header Timelimit:
missing value");
+ }
+ else {
+ int timelimit = atoi(value);
+ if (!isdigit(value[0]) || timelimit < 0)
+ _output->setError(RESPONSE_CODE_INVALID_HEADER,
+ "Invalid value for Timelimit: must be non-negative integer
(seconds)");
+ else {
+ _time_limit = timelimit;
+ _time_limit_timeout = time(0) + _time_limit;
+ }
+ }
+}
+
+
void Query::parseWaitTimeoutLine(char *line)
{
char *value = next_field(&line);
@@ -782,6 +807,14 @@ bool Query::processDataset(void *data)
if (_limit >= 0 && (int)_current_line > _limit)
return false;
+ // When we reach the time limit we let the query fail. Otherwise the user will
+ // not know that the answer is incomplete.
+ if (_time_limit >= 0 && time(0) >= _time_limit_timeout) {
+ logger(LG_INFO, "Maximum query time of %d seconds exceeded!",
_time_limit);
+ _output->setError(RESPONSE_CODE_LIMIT_EXCEEDED, "Maximum query time
of %d seconds exceeded!", _time_limit);
+ return false;
+ }
+
if (doStats())
{
Aggregator **aggr;
diff --git a/livestatus/src/Query.h b/livestatus/src/Query.h
index 82b864a..ed40902 100644
--- a/livestatus/src/Query.h
+++ b/livestatus/src/Query.h
@@ -74,6 +74,8 @@ class Query
bool _need_ds_separator;
int _output_format;
int _limit;
+ int _time_limit;
+ time_t _time_limit_timeout;
unsigned _current_line;
int _timezone_offset;
@@ -148,6 +150,7 @@ private:
void parseColumnsLine(char *line);
void parseColumnHeadersLine(char *line);
void parseLimitLine(char *line);
+ void parseTimelimitLine(char *line);
void parseSeparatorsLine(char *line);
void parseOutputFormatLine(char *line);
void parseKeepAliveLine(char *line);