-
Notifications
You must be signed in to change notification settings - Fork 103
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
Live reconfiguration #851
Live reconfiguration #851
Conversation
A server group was added to tempesta's internal lists before it was fully filled with data and before the data was verified. The patch separates these steps. A server group is filled with data first, the data is verified, and only then the group is added to Tempesta.
'call_counter' served two functions. One was to mark specs of directives that were found in configuration file, and attempt to apply default values to directives whose specs were not marked. The other was to indicate specs of directives that required a cleanup as memory may had been allocated. However, `call_counter` was cleared in some cases (read: nested specs), which broke the function of a cleanup. Two different flags are created __called_now and __called_ever, each serving its specific function.
That makes it so much easier to navigate, analyze, and modify.
The patch makes it possible to "start" Tempesta when it's running already. With that, a new configuration file may be supplied, provided that Tempesta can handle a live reconfiguration.
Tempesta can now handle multiple consecutive starts. In case of errors it cleans up correctly. The actual live reconfiguration of any directive is not there yet.
The function is called after complete configuration file is parsed and pushed to all modules. It gives the opportunity to cross-check configuration data, or set up other data based on configuration data before the system is started.
@@ -35,7 +35,7 @@ tfw_mod=tempesta_fw | |||
tfw_sched_mod=tfw_sched_$sched |
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 update copyright year at the above
--reload) | ||
reload | ||
exit | ||
;; |
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 move Build
, Run & Stop
and Configuration
sections from README.md
to the Wiki (leaving reference to the Wiki section(s) in README.md) and add description of the new command.
return -EINVAL; | ||
} | ||
|
||
ret = spec_handle_entry(matching_spec, &ps->e); | ||
entry_reset(&ps->e); | ||
if (ret) | ||
return ret; |
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.
If we return here or at the above, who calls entry_reset(ps)
, i.e. free()'s all the entry items? I didn't get the sense of 71545e6 (how the change is linked with 'show errors'), but it seems the change introduces memory leak.
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.
entry_reset()
is called here:
Line 1513 in cb39ace
entry_reset(&ps.e); |
The 71545e6 commit is for showing erroneous line number and line content for nested directives in print_parse_error()
. E.g. for the config:
srv_group A {
server 127.0.0.1:8000 error_string;
}
Current master will produce the next error message:
[tempesta] ERROR: Invalid number of arguments: 2
[tempesta] ERROR: configuration parsing error
While the current branch gives more helpful error message:
[tempesta] ERROR: Invalid number of arguments: 2
[tempesta] ERROR: configuration parsing error:
2: server 127.0.0.1:8000 error_string;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tempesta_fw/server.c
Outdated
__tfw_sg_for_each(&sg_list_reconfig, list_reconfig); | ||
} | ||
else { | ||
__tfw_sg_for_each(&sg_list, list); |
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 all use cases tfw_sg_for_each_srv()
is called with true
or false
as reconfig
value, so actually there are two functions aggregated together with the boolean argument. It's better to write 2 normal functions with only one argument.
I'm not sure whether it has sense to move 2-lines loop to the macro, but the macro makes the function bit more complex: the first impression is that cb
is unused, moreover since the macro is global it seems like it's used also in some other place but actually it isn't. Please move the macro definition into the funcrion with undef
at the end of the function or just replace the macro calls by the loop - not a big deal to copy & paste 2 lines of code.
tempesta_fw/server.c
Outdated
*/ | ||
TfwSrvGroup * | ||
tfw_sg_lookup(const char *name) | ||
__tfw_sg_lookup(const char *name, bool reconfig) |
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 always called with explicit true
or false
as a value of reconfig
, so please split the function to two real functions.
@@ -79,10 +81,11 @@ typedef struct { | |||
*/ | |||
struct tfw_srv_group_t { | |||
struct list_head list; | |||
struct list_head list_reconfig; |
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 comment the item above. Need to answer why list
isn't enough, i.e. when a group can be referenced from sg_list
and be in sg_list_reconfig
at the same time.
tempesta_fw/sock_srv.c
Outdated
|
||
/* Some servers was removed, so scheduler update is required */ | ||
if (sg_cfg->orig_sg && | ||
(sg_cfg->orig_sg->srv_n != sg_cfg->parsed_sg->srv_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 place opening curly braces on next line for complex if
conditions
tempesta_fw/sock_srv.c
Outdated
list_for_each_entry(srv, &sg_cfg->parsed_sg->srv_list, list) { | ||
if (tfw_addr_eq(&srv->addr, &orig_srv->addr)) | ||
break; | ||
} |
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 need curly braces
tempesta_fw/sock_srv.c
Outdated
/* | ||
* TODO: shrink connections: disconnect connection and remove | ||
* it from server's conn_list | ||
*/ |
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 a bad thing to leave server connection open - it could lead to serious memory leakage. Why don't you use tfw_sock_srv_disconnect()
?
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 was called later in one loop for all removed servers. I've reworked this to call disconnect() as soon as possible.
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 didn't understand your comment: if you reworked the code, then why the condition and the comment are still here?
tempesta_fw/sock_srv.c
Outdated
int r; | ||
|
||
INIT_LIST_HEAD(&sg_cfg_list); | ||
if ((r = tfw_cfgop_new_sg_cfg_default())) |
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 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.
Actually no. Default server group is created to store various server group options as global defaults. Implicit default server group is not registered as server group available after reconfig until cfgend()
.
Sticky sessions may became invalid after live reconfiguration, since pinned server or group can be removed from the configuration.
Don't remove back reference from server to server group since it's required in server connection callbacks.
BUG_ON is not needed here. This was found by @keshonok, see #692 (comment) for details.
tempesta_fw/sock_srv.c
Outdated
* invalid. | ||
* | ||
* On configuration parsing stage binary representation of a server group is | ||
* saved into @parsed_sg, while @orig_sg poins to the existing server group |
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.
poins -> points
tempesta_fw/sched/tfw_sched_http.c
Outdated
tfw_http_match_list_free(tfw_rules_reconfig); | ||
tfw_rules_reconfig = NULL; | ||
|
||
if (tfw_runstate_is_reconfig()) |
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.
If I correctly understand - this code will be executed after failed reconfiguration (on parse stage). And in this case Tempesta should continue working with old configuration, but we release old tfw_rules
here. Maybe condition should be inverted...
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, you're absolutely right, i'll fix that
…ing of sg default opts
tempesta_fw/server.c
Outdated
{ | ||
TfwServer *srv; | ||
|
||
write_lock(&sg->lock); |
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 that read_lock/unlock
is not used anywhere with sg->lock
- maybe using spin_lock
with sg->lock
will be simpler.
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.
Hm, frankly speaking I don't see write operation here - this is just a list scan which is supposed to be a read operation. It seems read lock should be used here.
tempesta_fw/server.c
Outdated
} | ||
|
||
/** | ||
* Iterate over all server groups of given server grop @sg and call @cb for |
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.
server groups
-> servers
grop
- > group
tempesta_fw/server.c
Outdated
@@ -202,7 +395,26 @@ __tfw_sg_for_each_srv(TfwSrvGroup *sg, int (*cb)(TfwServer *srv)) | |||
} | |||
|
|||
/** | |||
* Iterate over all server groups and call @cb for each server. | |||
* Iterate over all the acive server groups and call @cb for each server group. |
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.
acive
-> active
tempesta_fw/sock_srv.c
Outdated
.name = "sticky_sessions", | ||
.deflt = TFW_CFG_DFLT_VAL, | ||
.handler = tfw_cfgop_in_sticky_sess, | ||
.cleanup = tfw_cfgop_cleanup_srv_groups, |
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.
As I understand cleanup
callbacks is not called for nested srv_group
options specs. Does it make sense to define these callbacks?
tempesta_fw/sock_srv.c
Outdated
* stage since the new configuration is provided by a user and may contain | ||
* errors. | ||
* - applying stage: tfw_sock_srv_start(). Errors are unrecoverable and mean | ||
* that only a part of changes was appied, so the resulting configuration is |
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.
appied
-> applied
return -EINVAL; | ||
tfw_cfg_out_slstsz++; | ||
} |
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.
If in config file a server outside any group is defined at first and then default group is defined explicitly - is this an acceptable case?
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.
No, it's not acceptable. Once a server is defined outside of any group, default
group will be registered as available after reconfiguration. If the explicit default
group is defined after that, tfw_cfgop_begin_srv_group()
will throw a Group '...' already exists
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.
Yes - got it. Thanks.
tempesta_fw/sock_srv.c
Outdated
setup_timer(&srv->gs_timer, tfw_sock_srv_grace_shutdown_cb, | ||
(unsigned long)srv); | ||
mod_timer(&srv->gs_timer, jiffies + tfw_cfg_grace_time * HZ); | ||
tfw_sock_srv_grace_list_add(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 seems safer to place tfw_sock_srv_grace_list_add(srv)
before mod_timer(&srv->gs_timer, ...)
.
tempesta_fw/server.h
Outdated
@@ -41,20 +41,30 @@ typedef struct tfw_scheduler_t TfwScheduler; | |||
* Server descriptor, a TfwPeer successor. | |||
* | |||
* @list - member pointer in the list of servers of a server group; | |||
* @gs_timmer - grace shutdown 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.
gs_timmer
-> gs_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.
Good to merge after small fixes and cleanups (Yahoo!)
|
||
tfw_http_conn_evict_timeout(srv_conn, &equeue); | ||
if (unlikely(resched)) | ||
tfw_http_conn_collect(srv_conn, &sch_queue, &equeue); |
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 isn't about this particular patch set, but is about the whole recent changes: just too many list traversals and many of them are under lock. Such these two functions both traverse forward list: one to evict timed out requests and the second one to collect the rest of them. Why not to do this in one shot? Let's leave it for #687 .
tempesta_fw/sock_srv.c
Outdated
else if (orig_srv->conn_n > srv->conn_n) { | ||
/* | ||
* TODO: shrink number of connections. Disconnects are performed | ||
* asynchronously, can't destroy connection here and 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.
I still didn't get why for example we can't schedule the connections for eviction, i.e. it's still unclear why we can't implement the logic now and have to leave with the TODO. Please add a requirement to #687 - let's put the TODOs from the PR there.
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.
When we need to shrink number of connections to a given server, we must do two things: disconnect and destroy some connections; and update scheduler data for that server. The scheduler data is normally common for the whole server group.
Disconnecting of connections is an asynchronous operation. So we have to keep connections in srv->conn_list
until disconnect is completed. In the same time right after scheduling for disconnects we use all connections from srv->conn_list
to build scheduler data. Dead connections poison scheduler, since number of scheduling attempts is limited.
UPD: Somehow i missed a possible solution: we can check TFW_CONN_B_DEL
flag when building scheduler data to skip connections scheduled to disconnect. Any way that can be done as a separate task.
* @tfw_cfg_sg_def since options of explicitly defined 'default' group must not | ||
* be changed when user updates default server group options. | ||
*/ | ||
static TfwCfgSrvGroup *tfw_cfg_sg_opts = 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'm wondering if there is a functional test for #748? The issue has clear configuration example, but I couldn't quickly find the example in the tests.
The problem with the current test system is that all the tested scenarios are expressed in Python and it's quite difficult to find a particular case. Meantime, e.g. MariaDB uses SQL-like (the language of the pruduct) templates to express the test cases, for example https://github.com/tempesta-tech/mariadb/blob/trunk/mysql-test/suite/versioning/t/select.test . If you just familiar with SQL you can quickly get an idea which case is covered by the test.
Can we do the same for out test system? It'd be great to have some configuration templates with some high level commands like start Nginx with some configuration, start N instances of ab and so on. @intelfx @vladtcvs opinions?
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.
test functional/reconf/test_stress_sched_http.py
covers #748 and reconfiguration of http scheduler in the same time.
tempesta_fw/server.c
Outdated
{ | ||
TfwServer *srv; | ||
|
||
write_lock(&sg->lock); |
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.
Hm, frankly speaking I don't see write operation here - this is just a list scan which is supposed to be a read operation. It seems read lock should be used here.
tempesta_fw/server.c
Outdated
tfw_sg_destroy(TfwSrvGroup *sg) | ||
{ | ||
TFW_DBG2("release group: '%s'\n", sg->name); | ||
BUG_ON(!list_empty(&sg->srv_list)); |
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.
Do we definitely crash on this condition? We saw many crashes due to such checks and some of them don't actually mean inevitable crashes. It's better to use WARN() to notify us (developers) or a user about some problem. Also note https://github.com/tempesta-tech/linux-4.9.35-tfw/blob/master/include/asm-generic/bug.h#L37 . @aleksostapenko also please note this.
tempesta_fw/sock_srv.c
Outdated
/* Server was not found in new configuration. */ | ||
if (!(srv->flags & TFW_CFG_M_ACTION)) { | ||
if ((r = tfw_sock_srv_grace_shutdown_srv(sg, srv))) | ||
return r; |
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 release sg->lock
tempesta_fw/sock_srv.c
Outdated
} | ||
else if (srv->flags & TFW_CFG_F_MOD) | ||
if ((r = tfw_cfgop_update_srv(srv, sg_cfg))) | ||
goto err; |
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 release sg->lock
tempesta_fw/sock_srv.c
Outdated
while (1) | ||
{ | ||
TfwServer *srv; | ||
int act; |
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 open curly brace on a next line only in case of complex multi-line conditions and place it on the same line in all other cases.
Do you really need act
variable? Doesn't
if (del_timer_sync(&srv->gs_timer))
tfw_sock_srv_grace_stop(srv);
look simpler?
Live reconfiguration, only core features are supported in reconfiguration:
Known limitations/issues:
To start live reconfiguration simply run
./scripts/tempesta.sh --reload
or write sysctl variablesysctl -w net.tempesta.state=start
while Tempesta FW is running.