diff --git a/doc/reference.rst b/doc/reference.rst index 12440c5c6cc1856b6e8ec62f5ede152b49583b6c..7d7d601212336600d5cc2d6cf7921fc1cdf65ca4 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -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 diff --git a/man/knot.conf.5.in b/man/knot.conf.5.in index c1bb55bf2d33364e0007831d04b6399a68a3e763..19aa7f246aafac8bcf6ff4fe8ec1e67d7087092b 100644 --- a/man/knot.conf.5.in +++ b/man/knot.conf.5.in @@ -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; diff --git a/samples/knot.full.conf b/samples/knot.full.conf index 91b521eafae1bd155f00d7023bbcb35ca5b04192..e2366412e6bb0c88ba69890447d0e3145dcff15d 100644 --- a/samples/knot.full.conf +++ b/samples/knot.full.conf @@ -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; diff --git a/src/knot/conf/cf-lex.l b/src/knot/conf/cf-lex.l index 555c9aa1f4a5ac1305b554fffa26d0946a6623af..d813c818d119914d9c5354e29678f6b456ff1f40 100644 --- a/src/knot/conf/cf-lex.l +++ b/src/knot/conf/cf-lex.l @@ -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; } diff --git a/src/knot/conf/cf-parse.y b/src/knot/conf/cf-parse.y index 33797a391cdf4e37cae7c1a7e2d8710c9e7465d4..fd70df4cfaff9fb889fc9f2ba3067573a12eafeb 100644 --- a/src/knot/conf/cf-parse.y +++ b/src/knot/conf/cf-parse.y @@ -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 diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index 54140321d76c9f714f5fbcd0c095a0dcf4fab24a..e3358d08134260342079a811e52f19bb8f4f9630 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -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. */ diff --git a/src/knot/main.c b/src/knot/main.c index 56351f7d843bb5055d019d6d6caeafa0b93be583..9f3f4b279cdc82972a9fdb605a509510fda2e07d 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -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)); diff --git a/src/knot/server/server.c b/src/knot/server/server.c index e1620e5b1b2011a0c9cd755c98946bb43932717f..005914b368c4bea8083c76555a866f05d98ac87f 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -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; } diff --git a/src/knot/server/server.h b/src/knot/server/server.h index f5a920d9e879d4a999332956f2e62bd2395cce6b..66bb7bcd83a176a1e9c64add92cb213181c629d8 100644 --- a/src/knot/server/server.h +++ b/src/knot/server/server.h @@ -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. diff --git a/src/knot/zone/events.c b/src/knot/zone/events.c index c0b04a974d3bbc3d57feb0c0595e7ab2d52df95c..7315e1c5b95633cc7c1840cf1848ca186f14bbb7 100644 --- a/src/knot/zone/events.c +++ b/src/knot/zone/events.c @@ -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; diff --git a/src/knot/zone/events.h b/src/knot/zone/events.h index 965c929d4bc05eddb1076f13f5967c2aa4f20e65..88c22acf92500cd23cd782f0c0dd0e7430e43a51 100644 --- a/src/knot/zone/events.h +++ b/src/knot/zone/events.h @@ -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. * diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c index bd481d290c536dfcea8ed331de4e78e57bf100b4..21ba839150bfe6f59ba435ad313d5c38b67bb055 100644 --- a/src/knot/zone/zonedb-load.c +++ b/src/knot/zone/zonedb-load.c @@ -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;