-
Notifications
You must be signed in to change notification settings - Fork 105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #672: Add health monitoring for backend servers. #877
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to merge after several clenups. Don't forget to close #683 since it isn't mentioned in the patch title.
tempesta_fw/apm.c
Outdated
}, | ||
{} | ||
{ 0 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed that APM configuration isn't documented on our Wiki at all, so please update https://github.com/tempesta-tech/tempesta/wiki/Configuration and https://github.com/tempesta-tech/tempesta/wiki/Performance-monitoring chapters. The last one also requires description on the logic from tasks #672 and #683, however probably the chapter for backend servers also must be updated.
Also please create a test issues for #672 and #683. The test must at least test following:
- all the new configuration options
- a server returning error response must not be scheduled, but only for configured error responses
- after some time, when server stops return error responses, it receives requests again
- procfs statistic must be verified for enabled and disabled server states
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@krizhanovsky Done. Test created. Wiki:
tempesta_fw/apm.c
Outdated
char *req; | ||
unsigned long reqsz; | ||
u32 crc32; | ||
unsigned short tmt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please place the members to minimize alignment holes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
atomic_t rearm; | ||
} TfwApmHMCtl; | ||
|
||
/* Entry for configureation of separate health monitors. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"configureation' -> "configuration"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected
tempesta_fw/http.c
Outdated
INIT_LIST_HEAD(&hm->msg.seq_list); | ||
INIT_LIST_HEAD(&((TfwHttpReq *)hm)->fwd_list); | ||
INIT_LIST_HEAD(&((TfwHttpReq *)hm)->nip_list); | ||
hm->destructor = tfw_http_req_destruct; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is partial copy & paste from tfw_http_msg_alloc()
. Moreover, it's body is generic enough, but it's named with "hwmonitor" suffix. Please unify it with tfw_http_msg_alloc()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
BUG_ON(!srv->apmref); | ||
hmctl = &((TfwApmData *)srv->apmref)->hmctl; | ||
list_for_each_entry(hm, &tfw_hm_list, list) { | ||
if(!strcasecmp(name, hm->name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please place a space between if
and opening brace. Also it's better to write such loops like
if (strcasecmp(name, hm->name))
continue;
// the if body
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
if (!req->conn) { | ||
tfw_http_msg_free((TfwHttpMsg *)req); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Later it'll be unclear what requests without a connection are, so please write a comment, or, better since there are a few such places, write a small function for the requests deletion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected. Replaced these pointer checks with TFW_HTTP_HMONITOR
request flag verification, according to #877 (comment).
tempesta_fw/str.c
Outdated
|
||
return 0; | ||
} | ||
EXPORT_SYMBOL(tfw_str_crc32_calc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add an appropriate unit test to t/unit/test_tfw_str.c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this function. tfw_str_to_cstr()
used instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly the patch is good, but i don't really like new hmonitor
parameter in scheduling functions and frequent checks for requests without origin connections. This should be discussed in comments before merging
@@ -372,6 +373,103 @@ tfw_stats_update(TfwPcntRanges *rng, unsigned int r_time) | |||
return; | |||
} | |||
|
|||
/* Time granularity for HTTP codes accounting during health monitoring. */ | |||
#define HM_FREQ 10 | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra empty line is not needed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
|
||
BUG_ON(srv->apmref); | ||
|
||
if (!(data = tfw_apm_create())) | ||
if (!(data = tfw_apm_create(hm))) | ||
return -ENOMEM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a real issue, just a notice. tfw_apm_create()
may fail due to improper input values (hm
, tfw_hm_codes_cnt
) in addition to lack of free memory. You can pass actual error code in pointer by using ERR_PTR()
macro and check it using IS_ERR()
macro. It's widely used across kernel and TempestaFW code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed hm
argument from tfw_apm_create()
, due to reconfiguration changes.
tempesta_fw/apm.c
Outdated
BUG_ON(!hmstats); | ||
for (i = 0; i < tfw_hm_codes_cnt; ++i) { | ||
if (hmstats[i].hmcfg->code == status || | ||
hmstats[i].hmcfg->code == (int)(status / 100)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear for the first sight why we must take into account the first digit in status (status / 100
), comment is required here. Also documentation of code
field of the TfwApmHMCfg
should be extended to cover all possible values: 4*
and 5*
masks in addition to exact status codes.
Explicit casting to int
is not requited since status
is already int
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
|
||
if (tfw_http_parse_status(ce->vals[0], &code)) { | ||
TFW_ERR_NL("Unable to parse http code value: '%s'\n", | ||
ce->vals[0] ? ce->vals[0] : "No value specified"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ce->vals[0|1|2]
is non NULL
if tfw_cfg_check_val_n(ce, 3)
conditions has met.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
@@ -1179,6 +1235,13 @@ tfw_http_conn_resched(struct list_head *sch_queue, struct list_head *equeue) | |||
" an available back end server"); | |||
continue; | |||
} | |||
|
|||
if (!req->conn && sch_conn->peer != prev_conn->peer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking for req->conn
here and in many other places seems a little bit hacky. Isn't it better to introduce some flag for TfwHttpReq
to indicate that the request was issued by TempestaFW it self and not by the end user.
With that flag you can also remove newly added hmonitor
operand in sched_srv_conn()
functions.
TempestaFW will generate more requests form it's own in the future, e.g. conditional requests to backend servers, so it's likely that we will need some way to distinguish requests from Tempesta and end clients.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected. TFW_HTTP_HMONITOR
request flag introduced.
tempesta_fw/http.c
Outdated
@@ -1179,6 +1235,13 @@ tfw_http_conn_resched(struct list_head *sch_queue, struct list_head *equeue) | |||
" an available back end server"); | |||
continue; | |||
} | |||
|
|||
if (!req->conn && sch_conn->peer != prev_conn->peer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's better to find out first is the request from client or from hmonitor and then chose the right scheduling function: generic scheduling (can be slow) or scheduling to the same server (the fastest way).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/str.c
Outdated
unsigned long len = 0; | ||
|
||
BUG_ON(str->len && !str->ptr); | ||
data = p = kmalloc(str->len, GFP_ATOMIC); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use tfw_str_to_cstr()
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, missed that handy function. Thanks.
Corrected.
tempesta_fw/apm.c
Outdated
BUG_ON(list_empty(&tfw_hm_list)); | ||
if (!tfw_hm_entry->codes && !tfw_hm_entry->crc32) { | ||
TFW_ERR_NL("Response codes and crc32 values are not" | ||
" configured for '%s'\n", cs->name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message is confusing and doesn't report the actual reason for the error. Both of these configuration options are marked as allow_none
which means that they can be skipped. So the actual error here, I believe, is that at least one of these (or both, for that matter) must be configured.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keshonok Thank you for review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
}, | ||
{ | ||
.name = "request_url", | ||
.deflt = NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe, that If the request
option has a default value, then it's logical that this option should have a default value as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
tfw_hm_entry->req = (char *)__get_free_pages(GFP_KERNEL, | ||
get_order(size)); | ||
if (!tfw_hm_entry->req) { | ||
TFW_ERR_NL("Can't allocate memory for helth" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
helth
-> health
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
|
||
if (tfw_cfg_check_val_n(ce, 1)) | ||
return -EINVAL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that these two checks are congenerical - about a very similar thing, simple, and logically in the same realm. Usually there's no need for an empty line between checks like these as they are not logically separated. There's lot of examples in the code for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected (and two cases below too).
tempesta_fw/apm.c
Outdated
|
||
if (tfw_cfg_check_val_n(ce, 3)) | ||
return -EINVAL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that these two checks are congenerical - about a very similar thing, simple, and logically in the same realm. Usually there's no need for an empty line between checks like these as they are not logically separated. There's lot of examples in the code for this.
tempesta_fw/apm.c
Outdated
|
||
if (tfw_cfg_check_single_val(ce)) | ||
return -EINVAL; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that these two checks are congenerical - about a very similar thing, simple, and logically in the same realm. Usually there's no need for an empty line between checks like these as they are not logically separated. There's lot of examples in the code for this.
} | ||
hm->h_tbl->size = __HHTBL_SZ(1); | ||
hm->h_tbl->off = TFW_HTTP_HDR_RAW; | ||
memset(hm->h_tbl->tbl, 0, __HHTBL_SZ(1) * sizeof(TfwStr)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes leave a strange feeling of being half-done. On one hand, the new argument allows for creating of special messages without the header table that is required for the parser. On the other hand, the parser is still initialized regardless of that in the code that follows. Then, there's tfw_http_msg_alloc_err_resp()
that basically does the same thing but it's still there and not removed.
Now the new argument is required for all allocations, and it looks somewhat ugly.
I believe this (and related) code should be refactored. The original top-level interface should stay the same in the form of tfw_http_msg_alloc(int type)
. A different interface should be introduced for messages that are not going through the parser AND are not modified - which are the messages that are fully prepared from scratch by Tempesta. Perhaps, internally, these two different sets of interfaces may use the same common code - either by calling some inline functions or with macros. tfw_http_msg_alloc_err_resp()
would be superseded with the new interface.
Perhaps, you could call it tfw_http_msg_alloc_light()
- nothing better comes to mind right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -206,6 +208,24 @@ tfw_srvstats_seq_show(struct seq_file *seq, void *off) | |||
atomic64_read(&srv->sess_n)); | |||
seq_printf(seq, "Total schedulable connections\t: %zd\n", | |||
srv->conn_n - rc); | |||
|
|||
seq_printf(seq, "HTTP health monitor is enabled\t: %d\n", hm); | |||
if (hm) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, hm_stats
should be declared within this block as it's not used elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/server.h
Outdated
@@ -64,9 +65,15 @@ typedef struct { | |||
atomic64_t refcnt; | |||
unsigned int weight; | |||
unsigned int flags; | |||
unsigned int hm_flags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I am wrong, but my understanding is that both atomic and non-atomic ops can be used on the same variable. There's only a handful of flags, and it's a 32-bit variable. So, perhaps, a separate hm_flags
is unnecessary?
Also, I am not sure I understand what's going on here. For atomic access you need an unsigned long
variable, but this is an unsigned int
. Everywhere in the code it's cast to unsigned long
, but I believe that it's incorrect as it would extend to memory occupied by other unrelated data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can not mix atomic and non-atomic access to the same data: if one operation doesn't lock the system bus, then it just writes back it's outdated view to the memory location. I agree with wrong type conversions. Also it seems the atomicity on the flags adjustment is broken, see comment for tfw_srv_mark_alive()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In current implementation of set_bit/clear_bit
functions for x86, in cases of constant bit index argument - per-byte instructions orb
and andb
are used. So if for bit index we use immediate value less then 32 - we will not go beyond the int
boarders. But yes, this is not very safe approach.
Corrected.
tempesta_fw/http.c
Outdated
} | ||
else | ||
tfw_http_req_error(srv_conn, req, equeue, 500, | ||
"request dropped: forwarding error"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curly braces are required here according to the coding standard (because the other branch has them).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
tfw_http_msg_free((TfwHttpMsg *)req); | ||
return; | ||
} | ||
|
||
if (reply) { | ||
TfwCliConn *cli_conn = (TfwCliConn *)req->conn; | ||
tfw_connection_unlink_msg(req->conn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not visible here, but curly braces are required here in the else
branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
tfw_connection_unlink_msg(resp->conn); | ||
tfw_apm_update(((TfwServer *)resp->conn->peer)->apmref, | ||
resp->jrxtstamp, | ||
resp->jrxtstamp - req->jtxtstamp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line fits perfectly in the preceding line of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
"request dropped: forwarding error"); | ||
if (req->flags & TFW_HTTP_HMONITOR) { | ||
tfw_http_req_delist(srv_conn, req); | ||
tfw_http_msg_free((TfwHttpMsg *)req); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, if we're unable to send a health monitoring request, then it's simply dropped without any trace or action upon that? Is that the intended behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, maybe having a warning here would be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning added.
tempesta_fw/sock_srv.c
Outdated
memcpy(health->name, hname, size); | ||
srv = orig_srv ? : new_srv; | ||
} else { | ||
srv = orig_srv; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's difficult to understand the variety of combinations of function's arguments without comments. orig_srv
may be NULL
. The code suggest it's definitely NOT NULL
when !hname
. Why is that? A proper comment would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code has been redesigned.
tempesta_fw/sock_srv.c
Outdated
static int | ||
tfw_cfg_srv_set_health(void) | ||
{ | ||
TfwSrvHealth *hth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There're some common shortcuts that are understood in a specific way. For instance, nth usually mean n-th consecutive number, or something like that. Of course you can name variables any way you like in a small function, but it's best if the name resemble an unambiguous meaning.
There's a number of "rules" for creating short names, one of which is removing all vowels: default
-> dflt
, etc. With that, it would be health
-> hlth
. Or just leave it health
, it's sufficiently short in this particular case. Or even just h
- as long as it can't be confused with h-th number or something. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code has been redesigned.
return false; | ||
} | ||
return true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps, this can be made a simple static const array to avoid unnecessary run-time branching?
tempesta_fw/http.c
Outdated
ce->vals[0] | ||
? ce->vals[0] | ||
: "No value specified"); | ||
ce->vals[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this message be made clearer? What's 'value'? Perhaps, 'HTTP status code'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/http.c
Outdated
return; | ||
} | ||
tfw_str_to_cstr(&resp->body, body_cstr, resp->body.len + 1); | ||
crc32 = crc32(0, body_cstr, resp->body.len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly, this looks like extra unnecessary work. The whole body of a response is copied to memory allocated from pool just to calculate CRC32 on it.
This is a response to a health monitoring request, so I guess it's relatively small, and because it comes from a server it probably comes in minimal number of chunks (i.e. it's not sent one byte at a time). As far as I can see crc32()
function is perfectly suited for calculating the checksum over data that is located in different chunks (the seed
argument), so it should be straightforward to write a function that goes over string's chunks and calculates CRC32.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, definitely: with #76 in mind we can have ~1M sites with enabled health monitoring and the copyings can be harmful. We also shouldn't make an assumption about size of health checking response - this is 100% depends on a user preferences. In general, we put a lot of effort for zero-copying - there are plenty of TfwStr and skb routines processing data chunks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still several issues. hm_flags
atomicity is still unclear. Also please write a wiki doc for the next review - it'd be good to review it as well.
tempesta_fw/str.c
Outdated
BUG_ON(len != str->len); | ||
return crc; | ||
} | ||
EXPORT_SYMBOL(tfw_str_crc32_calc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function must be tested in t/unit/test_tfw_str.c, at least that CRC32 calculated for the same data represented in single block and TfwStr's chunks have the same values. Also please calculate a checksum for some data using Linxu crc32 command (as a sysadmin is supposed to use it for calculating HTML file checksums) and use the checkusm in the test - our implementation and the utility must return the same values. Or, the better, to use the utility in the functional thest for HM.
Also please mention the Linux utility crc32 in the Wiki in an example for health monitoring settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/str.c
Outdated
BUG_ON(str->len && !str->ptr); | ||
TFW_STR_FOR_EACH_CHUNK(c, str, end) { | ||
crc = crc32(crc, c->ptr, c->len); | ||
len += c->len;//!!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no need to introduce the overhead in the function. There'd be too expensive to check the length consistency in all chunks traversing logic, so only strings creation functions should care about the consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, forgot to remove this debug code. Thanks!
Corrected.
'request "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";\n' | ||
'request_url "/page.html";\n' | ||
'resp_code 200;\n' | ||
'resp_crc32 3456;\n' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use more realistic HTML response and properly calcucalted, using crc32
Linux tool, CRC32 checksum. The test must verify that CRC32 is calculated by Tempesta FW correctly and that Tempesta FW catches bad responses with wrong CRC32 and marks the server as dead.
@ikoveshnikov and @vladtcvs should we enumerate somewhere all the test with stress options to run them in long running mode on the CI system?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. New functional test TestHealthMonitorCRCOnly
(for separate CRC32 only verification) is added.
@@ -2879,8 +3044,10 @@ tfw_http_req_key_calc(TfwHttpReq *req) | |||
|
|||
req->hash = tfw_hash_str(&req->uri_path); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no sense to fall down for !(req->flags & TFW_HTTP_HMONITOR)
and calculate CRC32 over empty data, so just return here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
|
||
BUG_ON(!srv->apmref); | ||
BUG_ON(!hm); | ||
BUG_ON(test_bit(TFW_SRV_B_HMONITOR, &srv->hm_flags)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems the check should be WARN_ON_ONCE()
since failing it desn't lead to the whole system crash, just to misbehaving health monitoring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/apm.c
Outdated
|
||
BUG_ON(!hm); | ||
if (hm->codes && test_bit(HTTP_CODE_BIT_NUM(status), hm->codes)) | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if a server return us a 'good' response code, then we consider it as alive and don't check the response body. However, missbehaving server can return us 200 responce code with messy response body. Only both the correct response code and the body checksum must be treated as success.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in case of default auto
monitor - it seems that we haven't any acceptable CRC32 value to examine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use crc32 of the first response, but in auto mode only.
However, for applications with dynamic content, it's good to be able to explicitly disable crc32 check. Probably, the easiest way to do this is just explicitly configure auto
HM without crc32, e.g.
health_check auto {
request "GET / HTTTP/1.0\r\n\r\n";
request_url "/";
resp_code 200;
timeout 10;
}
Other possibility is to explicitly set auto
value for crc32 field if a user want's auto calculated crc32, e.g.:
health_check auto {
request "GET / HTTTP/1.0\r\n\r\n";
request_url "/";
resp_code 200;
resp_crc32 auto;
timeout 10;
}
Please update the Wiki about explicit auto
configuration and automatic crc32 calculation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done (the last approach).
tempesta_fw/server.h
Outdated
tfw_srv_suspended(TfwServer *srv) | ||
{ | ||
return test_bit(TFW_SRV_B_HMONITOR, &srv->hm_flags) | ||
&& test_bit(TFW_SRV_B_SUSPEND, &srv->hm_flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TFW_SRV_B_SUSPEND
is set in tfw_http_hm_control()
after check for TFW_SRV_B_HMONITOR
, so TFW_SRV_B_SUSPEND
can be set for HM-enabled servers only. test_bit(TFW_SRV_B_HMONITOR, &srv->hm_flags)
isn't needed here and can be replaced by WARN_ON_ONCE()
.
...also see comment for tfw_http_hm_control()
. Should the function look like?
unsigned long tmp = READ_ONCE(srv->hm_flags) & (TFW_SRV_B_HMONITOR | TFW_SRV_B_SUSPEND);
return tmp == (TFW_SRV_B_HMONITOR | TFW_SRV_B_SUSPEND));
It this way we can read and check both the flags at onece.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected: #877 (comment).
tempesta_fw/server.h
Outdated
@@ -64,9 +65,15 @@ typedef struct { | |||
atomic64_t refcnt; | |||
unsigned int weight; | |||
unsigned int flags; | |||
unsigned long hm_flags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at flags
and it's needed for live reconfiguration only, so why not to treat the TFW_CFG_F_*
the same way through atomic bit operations? It's really confusing to see two different flags. There is nothing about performance or massive concurrent updates, the only difference is that hm_flags
can be ptentially updated concurrently.
Also TFW_SRV_B_HMONITOR
is cleared in tfw_server_destroy() -> tfw_apm_del_srv()
called from tfw_sock_srv_grace_shutdown_srv()
, which sets srv->flags |= TFW_CFG_F_DEL
, so probably there is a direct link between TFW_SRV_B_HMONITOR
and TFW_CFG_F_DEL
flags. @ikoveshnikov could you please also review this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done: flags have been merged.
tempesta_fw/http.c
Outdated
|
||
if (!test_bit(TFW_SRV_B_HMONITOR, &srv->hm_flags)) | ||
return; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...I still don't understand the function intention: we check TFW_SRV_B_HMONITOR
at the above and check it again in tfw_srv_suspended()
at the calls at the below - is it some kind of protection against concurrent clearing from tfw_apm_hm_disable_srv()
? If so then why tfw_apm_hm_disable_srv()
can't be called just between test_bit(TFW_SRV_B_HMONITOR, &srv->hm_flags)
and test_bit(TFW_SRV_B_SUSPEND, &srv->hm_flags)
? Also see comment for tfw_srv_suspended()
.
What's wrong if at this point of code the server is destroying due to reconfiguration? TFW_SRV_B_SUSPEND
seems much easier: it seems the worse can happen is just unnecessary calculation of response body calculation or wrong mark the server as dead (the last one will be quickly fixed by the next HM message). @ikoveshnikov also please review the point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functions tfw_http_hm_control()
and tfw_srv_mark_suspended()
have been redesigned in accordance with discussion in chat.
tempesta_fw/apm.c
Outdated
} | ||
|
||
bool | ||
tfw_apm_get_hm(const char *name, void **res_hm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not to return hm
? Why do we need the pointer and a boolean return value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor fixes are still required. Please if you have some objections about my comments - ping me in the chat.
@@ -681,7 +694,7 @@ tfw_sock_srv_grace_shutdown_srv(TfwSrvGroup *sg, TfwServer *srv) | |||
|
|||
tfw_server_get(srv); | |||
__tfw_sg_del_srv(sg, srv, false); | |||
srv->flags |= TFW_CFG_F_DEL; | |||
set_bit(TFW_CFG_B_DEL, &srv->flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several other places which are good to update for the code consistency:
./sock_srv.c:1993: if (!(srv->flags & TFW_CFG_M_ACTION)) {
./sock_srv.c:2000: else if (srv->flags & TFW_CFG_F_MOD)
./sock_srv.c:2012: if (!(srv->flags & TFW_CFG_F_ADD))
./sock_srv.c:2066: if (!(srv->flags & TFW_CFG_M_ACTION))
./http_sess.c:757: if (unlikely(srv->flags & TFW_CFG_F_DEL)) {
While technically it's OK to read the flags on x86-64 in this manner, it's better to have to code consistent in using the same API to access the flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected, but with TFW_CFG_M_ACTION
- left the same as didn't find acceptable interface for bit mask checking.
tempesta_fw/http.c
Outdated
flags | TFW_SRV_F_SUSPEND); | ||
if (likely(old_flags == flags)) { | ||
TFW_WARN_ADDR("server has been suspended: limit" | ||
" for bad responses is exceeded", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add resp->status
to the message to provide more information to a system administrator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/apm.c
Outdated
|
||
BUG_ON(!hm); | ||
if (hm->codes && test_bit(HTTP_CODE_BIT_NUM(status), hm->codes)) | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use crc32 of the first response, but in auto mode only.
However, for applications with dynamic content, it's good to be able to explicitly disable crc32 check. Probably, the easiest way to do this is just explicitly configure auto
HM without crc32, e.g.
health_check auto {
request "GET / HTTTP/1.0\r\n\r\n";
request_url "/";
resp_code 200;
timeout 10;
}
Other possibility is to explicitly set auto
value for crc32 field if a user want's auto calculated crc32, e.g.:
health_check auto {
request "GET / HTTTP/1.0\r\n\r\n";
request_url "/";
resp_code 200;
resp_crc32 auto;
timeout 10;
}
Please update the Wiki about explicit auto
configuration and automatic crc32 calculation.
tempesta_fw/apm.c
Outdated
if (hm->crc32 && body->len && | ||
tfw_str_crc32_calc(body) == hm->crc32) | ||
return true; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please print here a warning with expected crc32 (needed for auto mode), the response crc32 and response status code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the corrections! Good to merge.
tempesta_fw/sched/tfw_sched_hash.c
Outdated
{ | ||
return !tfw_srv_conn_restricted(conn) | ||
&& !tfw_srv_conn_queue_full(conn) | ||
&& tfw_srv_conn_get_if_live(conn); | ||
&& tfw_srv_conn_get_if_live(conn) | ||
&& (hmonitor || !tfw_srv_suspended((TfwServer *)conn->peer)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to make tfw_srv_suspended()
check before any others:
- That will reduce number of atomic operations.
- An extra reference might be taken in
tfw_srv_conn_get_if_live()
. The reference won't be released iftfw_srv_suspended
failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
* helth monitoring of backend server. | ||
*/ | ||
if (!(((TfwHttpReq *)msg)->flags & TFW_HTTP_HMONITOR) | ||
&& tfw_srv_suspended(srv)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check can be done before rcu operations: no need trying to schedule request if server is suspended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
tempesta_fw/server.h
Outdated
@@ -255,6 +272,24 @@ tfw_srv_conn_need_resched(TfwSrvConn *srv_conn) | |||
return ((ACCESS_ONCE(srv_conn->recns) >= sg->max_recns)); | |||
} | |||
|
|||
/* | |||
* Put server into alive or suspended (excluded from processing) state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is misleading: function can't put server into suspended state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
TfwApmHMStats *hmstats; | ||
atomic64_t rcount; | ||
unsigned long jtmstamp; | ||
struct timer_list timer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sending requests should be done in separate kernel not in timer function. Must be done as a part of #736
tempesta_fw/sock_srv.c
Outdated
if (!(srv->flags & TFW_CFG_M_ACTION)) | ||
continue; | ||
|
||
tfw_cfgop_update_srv_health(srv, sg_cfg->hm_name, hm); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was admitted that the main disadvantage of merged ik-51 branch (runtime backend server reconfiguration) was too many traversal across lists. In this patch you traverse the same sg->srv_list
twice: here and in tfw_cfgop_update_sg_srv_list()
. Can't we do the same job with only one traversal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
tempesta_fw/sock_srv.c
Outdated
TfwSrvGroup *sg = sg_cfg->parsed_sg; | ||
void *hm = NULL; | ||
|
||
if (sg_cfg->hm_name && !(hm = tfw_apm_get_hm(sg_cfg->hm_name))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nobody checks if user specified available health monitor in tfw_cfgop_health_monitor()
or later during cfgend()
. In that case we may get an error here, tfw_cfgop_start_sg_cfg()
will return -EINVAL
and so does tfw_sock_srv_start()
. This behaviour is not user-friendly since errors on start
are non-recoverable and TempestaFW will be stopped. Too hard penalty for possible misprints as for me.
Instead we must check if the health monitor named sg_cfg->hm_name
is available on cfgend
stage. Error will be found, a new configuration will be dropped, and the TempestaFW will continue working with old good configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
* @rearm - flag for gracefull stopping of @timer; | ||
*/ | ||
typedef struct { | ||
TfwApmHM *hm; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems at least hmstats
, hm
are server group-wide options and should be stored there. We can also use one timer per server group: send health check message to all server in group. What do you think? not the comment below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cannot make hmstats
group-wide - since this will break the essence of the task (server-wide HTTP health statistics and server-wide suspend/alive decisions).
As for hm
- yes, theoretically we can use it group-wide, but for now I'd prefer leave it server-wide for three reasons:
- Changing current implementation (originally grounded on a server-wide approach) will require significant code changes and re-testing.
- In current approach all health monitoring functionality is concentrated in APM module and the there is no need to spreading it between other modules (e.g
sock_srv
etc.) - Current solution is more flexible and give more granularity in HM management if it will be needed in future, and I do not see significant advantages in group-wide approach except of saving memory on
hm
pointers for each server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to merge! I've commented a couple of moments where performance optimisation is possible.
tempesta_fw/sock_srv.c
Outdated
* and still remain in new configuration too. | ||
*/ | ||
static void | ||
tfw_cfgop_update_sg_health(TfwCfgSrvGroup *sg_cfg, void *hm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more like a note to all places where tfw_cfgop_update_srv_health()
is called. All servers in the group has the same hmonitor. That means you can check that hmonitor has changed only once, like it done here for scheduler:
tempesta/tempesta_fw/sock_srv.c
Lines 1474 to 1475 in 9381703
if (tfw_cfgop_sched_changed(sg_cfg)) | |
sg_cfg->reconf_flags |= TFW_CFG_MDF_SG_SCHED; |
We can add a new flag TFW_CFG_MDF_SG_HMON
and set it in tfw_cfgop_setup_srv_group()
.
- Then when we enter
tfw_cfgop_update_sg_health()
function, we can return immediately if the flag is not set. - Then we update server list in
tfw_cfgop_update_sg_srv_list()
we need to calltfw_cfgop_update_srv_health()
only if the flag set. - the
tfw_apm_hm_srv_eq()
check intfw_cfgop_update_sg_health()
won't be needed.
This suggestion is only performance optimisation. The only effect is reducing of tfw_apm_hm_srv_eq()
calls: once per group instead of once per server.
tempesta_fw/sock_srv.c
Outdated
* later (in 'tfw_cfgop_update_sg_srv_list()') it | ||
* will be stopped and removed. | ||
*/ | ||
if (!(srv->flags & TFW_CFG_M_ACTION)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check is obsolete: the function is called now only if tfw_cfgop_update_sg_srv_list()
won't be called. Thus srv->flags & TFW_CFG_M_ACTION == TFW_CFG_F_KEEP
for all server in group.
tempesta_fw/sock_srv.c
Outdated
if (tfw_cfg_check_single_val(ce)) | ||
return -EINVAL; | ||
if (!tfw_cfgop_sg_set_hm_name(sg_cfg, ce->vals[0])) { | ||
TFW_ERR_NL("Unable to add group's health" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string can be fit in single line.
) | ||
return response | ||
|
||
def make_502_expected(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is not specific to this test, it's rather generic one. Please move the function to chains.py
. Almost the same was done in another PR: https://github.com/tempesta-tech/tempesta/pull/897/files#diff-2395c33b49b27bf69b94b134a3625992R19
) | ||
path = self.tester.get_server_path() | ||
stats, _ = self.tempesta.get_server_stats(path) | ||
s = r'HTTP availability\s+: (\d+)' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here: too generic steps in the function for the test. I'd create a new helper class in tempesta.py
, which can help to pass server statistics. Init it with pointer to Tempesta instance, server group and the server name and use like helpers.tempesta.Stats
class. No need to parse everything in server stats in this PR, just only needed stats.
@vladtcvs Do you have any suggestions here?
|
||
|
||
class TestHealthMonitor(functional.FunctionalTest): | ||
""" Test for health monitor functionality with stress option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't say it's 'regression' test. It's definitely test for a independent new feature - health monitoring. Please move the test to it's own directory. We'll need more tests about HTTP availability in future.
No description provided.