Commit cae3c1fb authored by Marek Vavruša's avatar Marek Vavruša

server: asynchronous/synchronous startup support

Two configurable options:
- wait for zones to be loaded before starting (default)
- start server and load zones in the background (asynchronous-start on)
parent e60c8585
......@@ -29,6 +29,7 @@ else.
[ rundir "string"; ]
[ pidfile "string"; ]
[ workers integer; ]
[ asynchronous-start ( on | off ); ]
[ user string[.string]; ]
[ max-conn-idle ( integer | integer(s | m | h | d); ) ]
[ max-conn-handshake ( integer | integer(s | m | h | d); ) ]
......@@ -149,6 +150,32 @@ online CPUs)
workers 16;
}
.. _asynchronous-start:
asynchronous-start
^^^^^^^^^^^^^^^^^^
When asynchronous startup is enabled, server doesn't wait for the zones to be loaded, and
starts responding immediately lame answers until the zone loads. This may be useful in
some scenarios, but it is disabled by default.
Default: disabled (wait for zones to be loaded before answering)
::
system {
asynchronous-start off;
}
.. _user:
user
^^^^
System ``user`` or ``user``.``group`` under which the Knot DNS is run
after starting and binding to interfaces. Linux capabilities
(:ref:`Required libraries`) are employed if supported and this
.. _user:
user
......
......@@ -54,6 +54,13 @@ serves as an example of the configuration for knotc(8) and knotd(8).
# Default: unset (auto-estimates optimal value from the number of online CPUs)
# background-workers 4;
# Start server asynchronously
# When asynchronous startup is enabled, server doesn't wait for the zones to be loaded, and
# starts responding immediately lame answers until the zone loads. This may be useful in
# some scenarios, but it is disabled by default.
# Default: disabled (wait for zones to be loaded before answering)
asynchronous-start off;
# User for running server
# May also specify user.group (e.g. knot.users)
# user knot.users;
......
......@@ -47,6 +47,14 @@ system {
# operations (e.g., zone loading, zone signing, XFR zone updates, ...)
# Default: unset (auto-estimates optimal value from the number of online CPUs)
# background-workers 4;
# Start server asynchronously
# When asynchronous startup is enabled, server doesn't wait for the zones to be loaded, and
# starts responding immediately lame answers until the zone loads. This may be useful in
# some scenarios, but it is disabled by default.
# Default: disabled (wait for zones to be loaded before answering)
asynchronous-start off;
# User for running server
# May also specify user.group (e.g. knot.users)
# user knot.users;
......
......@@ -118,6 +118,7 @@ notify-in { lval.t = yytext; return NOTIFY_IN; }
notify-out { lval.t = yytext; return NOTIFY_OUT; }
workers { lval.t = yytext; return WORKERS; }
background-workers { lval.t = yytext; return BACKGROUND_WORKERS; }
asynchronous-start { lval.t = yytext; return ASYNC_START; }
user { lval.t = yytext; return USER; }
pidfile { lval.t = yytext; return PIDFILE; }
rundir { lval.t = yytext; return RUNDIR; }
......
......@@ -468,6 +468,7 @@ static void ident_auto(int tok, conf_t *conf, bool val)
%token <tok> TSIG_ALGO_NAME
%token <tok> WORKERS
%token <tok> BACKGROUND_WORKERS
%token <tok> ASYNC_START
%token <tok> USER
%token <tok> RUNDIR
%token <tok> PIDFILE
......@@ -600,6 +601,9 @@ system:
| system BACKGROUND_WORKERS NUM ';' {
SET_NUM(new_config->bg_workers, $3.i, 1, 255, "background-workers");
}
| system ASYNC_START BOOL ';' {
new_config->async_start = $3.i;
}
| system USER TEXT ';' {
new_config->uid = new_config->gid = -1; // Invalidate
char* dpos = strchr($3.t, '.'); // Find uid.gid format
......
......@@ -214,6 +214,7 @@ typedef struct conf_t {
size_t max_udp_payload; /*!< Maximal UDP payload size. */
int workers; /*!< Number of workers per interface. */
int bg_workers; /*!< Number of background workers. */
bool async_start; /*!< Asynchronous startup. */
int uid; /*!< Specified user id. */
int gid; /*!< Specified group id. */
int max_conn_idle; /*!< TCP idle timeout. */
......
......@@ -349,7 +349,7 @@ int main(int argc, char **argv)
/* Start it up. */
log_server_info("Starting server...\n");
res = server_start(&server);
res = server_start(&server, config->async_start);
if (res != KNOT_EOK) {
log_server_fatal("Failed to start server: %s.\n",
knot_strerror(res));
......
......@@ -381,21 +381,24 @@ static void server_free_handler(iohandler_t *h)
memset(h, 0, sizeof(iohandler_t));
}
int server_start(server_t *s)
int server_start(server_t *s, bool async)
{
// Check server
dbg_server("%s(%p, %d)\n", __func__, s, async);
if (s == 0) {
return KNOT_EINVAL;
}
dbg_server("server: starting server instance\n");
/* Start workers. */
worker_pool_start(s->workers);
/* Wait for enqueued events if not asynchronous. */
if (!async) {
worker_pool_wait(s->workers);
}
/* Start evsched handler. */
dt_start(s->iosched);
/* Start workers. */
worker_pool_start(s->workers);
/* Start I/O handlers. */
int ret = KNOT_EOK;
s->state |= ServerRunning;
......@@ -405,8 +408,6 @@ int server_start(server_t *s)
}
}
dbg_server("server: server started\n");
return ret;
}
......
......@@ -144,12 +144,13 @@ void server_deinit(server_t *server);
* \brief Starts the server.
*
* \param server Server structure to be used for operation.
* \param async Don't wait for zones to load if true.
*
* \retval KNOT_EOK on success.
* \retval KNOT_EINVAL on invalid parameters.
*
*/
int server_start(server_t *server);
int server_start(server_t *server, bool async);
/*!
* \brief Waits for the server to finish.
......
......@@ -38,6 +38,10 @@
#include "knot/nameserver/tsig_ctx.h"
#include "knot/nameserver/process_answer.h"
/* ------------------------- internal timers -------------------------------- */
#define ZONE_EVENT_IMMEDIATE 1 /* Fast-track to worker queue. */
/* ------------------------- bootstrap timer logic -------------------------- */
#define BOOTSTRAP_RETRY (30) /*!< Interval between AXFR bootstrap retries. */
......@@ -795,6 +799,31 @@ void zone_events_schedule_at(zone_t *zone, zone_event_type_t type, time_t time)
pthread_mutex_unlock(&events->mx);
}
void zone_events_enqueue(zone_t *zone, zone_event_type_t type)
{
if (!zone || !valid_event(type)) {
return;
}
zone_events_t *events = &zone->events;
pthread_mutex_lock(&events->mx);
/* Possible only if no event is running at the moment. */
if (!events->running && !events->frozen) {
events->running = true;
event_set_time(events, type, ZONE_EVENT_IMMEDIATE);
worker_pool_assign(events->pool, &events->task);
pthread_mutex_unlock(&events->mx);
return;
}
pthread_mutex_unlock(&events->mx);
/* Execute as soon as possible. */
zone_events_schedule(zone, type, ZONE_EVENT_NOW);
}
void zone_events_schedule(zone_t *zone, zone_event_type_t type, unsigned dt)
{
time_t abstime = time(NULL) + dt;
......
......@@ -87,6 +87,17 @@ int zone_events_setup(struct zone_t *zone, worker_pool_t *workers,
*/
void zone_events_deinit(struct zone_t *zone);
/*!
* \brief Enqueue event type for asynchronous execution.
*
* \note This is similar to the scheduling an event for NOW, but it can
* bypass the event scheduler if no event is running at the moment.
*
* \param zone Zone to schedule new event for.
* \param type Type of event.
*/
void zone_events_enqueue(struct zone_t *zone, zone_event_type_t type);
/*!
* \brief Schedule new zone event to absolute time.
*
......
......@@ -147,10 +147,18 @@ static zone_t *create_zone(conf_zone_t *conf, server_t *server, zone_t *old_zone
zone_status_t zstatus = zone_file_status(old_zone, conf);
int result = zone_events_setup(zone, server->workers, &server->sched);
if (result != KNOT_EOK) {
zone->conf = NULL;
zone_free(&zone);
return NULL;
}
switch (zstatus) {
case ZONE_STATUS_FOUND_NEW:
case ZONE_STATUS_FOUND_UPDATED:
zone_events_schedule(zone, ZONE_EVENT_RELOAD, ZONE_EVENT_NOW);
/* Enqueueing makes the first zone load waitable. */
zone_events_enqueue(zone, ZONE_EVENT_RELOAD);
break;
case ZONE_STATUS_BOOSTRAP:
zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW);
......@@ -167,13 +175,6 @@ static zone_t *create_zone(conf_zone_t *conf, server_t *server, zone_t *old_zone
assert(0);
}
int result = zone_events_setup(zone, server->workers, &server->sched);
if (result != KNOT_EOK) {
zone->conf = NULL;
zone_free(&zone);
return NULL;
}
log_zone_load_info(zone, conf->name, zstatus);
return zone;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment