static ya_result xfr_query_callback(void* data) { xfr_query_schedule_param *xqsp = (xfr_query_schedule_param*)data; ya_result return_value; ya_result scheduler_status; u32 refreshed_time = 0; u32 retried_time = time(NULL); /* * Zone refresh will be enabled (again) here. * * xfr_query_mark_zone_loaded * */ if(ISOK(xqsp->return_value)) { /* success but type == 0 => transfer done */ if(xqsp->type != 0) { log_info("slave: %{dnstype}: proceeding with transfer of %{dnsname}", &xqsp->type, xqsp->origin); } } else { log_err("slave: %{dnstype}: transfer of %{dnsname} failed: %r", &xqsp->type, xqsp->origin, xqsp->return_value); /* * Here (?) put the invalid zone placeholder if it is not there yet */ } switch(xqsp->type) { case TYPE_AXFR: { /* * Load the axfr */ if(ISOK(xqsp->return_value)) { axfr_query_axfr_load_param *aqalp; MALLOC_OR_DIE(axfr_query_axfr_load_param*, aqalp, sizeof(axfr_query_axfr_load_param), GENERIC_TAG); log_info("slave: opening AXFR for %{dnsname} %d", xqsp->origin, xqsp->loaded_serial); if(ISOK(return_value = zone_axfr_reader_open_with_serial(g_config->xfr_path, xqsp->origin, xqsp->loaded_serial, &aqalp->zr))) { /* * The system is ready to load an AXFR * A zone is already in place (by design), there may be an old zone that is now irrelevant. * At this point ... * * If the serial are not the same * MT the old zone must be destroyed (if it exist) * then * MT The new zone has to be created (loaded from the AXFR file) * Else * ST The new zone is actually the old zone, and the old zone is non-existent * EndIf * * then * * ST The new zone and the placeholder zone have to be swapped, then the placeholder has to be destroyed * */ zdb_zone *zone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN); if(zone != NULL) { zdb_zone_truncate_invalidate(zone); } /** * schedule the axfr load */ aqalp->old_zone = NULL; /* old zone */ aqalp->new_zone = NULL; aqalp->db = xqsp->db; aqalp->origin = dnsname_dup(xqsp->origin); scheduler_status = SCHEDULER_TASK_PROGRESS; thread_pool_schedule_job(xfr_query_axfr_load_thread, aqalp, NULL, "axfr load"); break; } } /** * @todo If the "old_zone" exists, then ... (destroy ? swap back ? expired ?) * */ /* * An issue occurred (AXFR transfer, load) */ log_err("slave: unable to load the axfr (retry set in %d seconds)", g_config->axfr_retry_delay); alarm_event_node *event = alarm_event_alloc(); event->epoch = time(NULL) + g_config->axfr_retry_delay; if(g_config->axfr_retry_jitter > 0) { random_ctx rndctx = thread_pool_get_random_ctx(); u32 jitter = random_next(rndctx) % g_config->axfr_retry_jitter; event->epoch += jitter; } event->function = scheduler_axfr_query_alarm; event->args = xqsp; event->key = ALARM_KEY_ZONE_AXFR_QUERY; event->flags = ALARM_DUP_NOP; event->text = "scheduler_axfr_query_alarm"; zdb *db = (zdb*)xqsp->db; alarm_set(db->alarm_handle, event); /* DO NOT FREE xqsp, SO DO NOT BREAK : return now */ return SCHEDULER_TASK_FINISHED; } case TYPE_IXFR: { /** * Load the ixfr (single thread, zone locked) */ scheduler_status = SCHEDULER_TASK_FINISHED; if(ISOK(xqsp->return_value)) { zdb_zone *dbzone = zdb_zone_find_from_dnsname((zdb*)xqsp->db, xqsp->origin, CLASS_IN); if(dbzone == NULL) { log_err("slave: zone %{dnsname} journal has vanished", xqsp->origin); break; } log_info("slave: opening journal for '%{dnsname}'", xqsp->origin); zdb_zone_lock(dbzone, ZDB_ZONE_MUTEX_XFR); if(ISOK(return_value = zdb_icmtl_replay(dbzone, g_config->xfr_path, xqsp->serial_start_offset, xqsp->loaded_serial, ZDB_ICMTL_REPLAY_SERIAL_OFFSET|ZDB_ICMTL_REPLAY_SERIAL_LIMIT))) { log_info("slave: replayed %d records changes", return_value); dbzone->apex->flags &= ~ZDB_RR_LABEL_INVALID_ZONE; refreshed_time = time(NULL); } else { log_err("slave: replay error: %r this is bad: invalidate the zone and ask for an AXFR", return_value); /** @todo INVALIDATE AND AXFR */ } /** @todo invalitate if retried_time > expired_time */ zdb_zone_unlock(dbzone, ZDB_ZONE_MUTEX_XFR); } log_info("slave: journal transfer done"); xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time); scheduler_status = SCHEDULER_TASK_FINISHED; break; } default: { refreshed_time = time(NULL); log_info("slave: transfer done"); xfr_query_mark_zone_loaded((zdb*)g_config->database, xqsp->origin, refreshed_time, retried_time); scheduler_status = SCHEDULER_TASK_FINISHED; break; } } /* switch return_value */
ya_result scheduler_do_next_job() { /** @todo use the pool counters * _ Readers * _ Writers * _ Current Task : I can have a task that works bit by bit and continues until the last bit is done (RRSIG) */ /* Dequeue a job */ if(threaded_queue_size(&scheduler_queue) == 0) { return SCHEDULER_TASK_NOTHING; } scheduler_task *task = threaded_queue_dequeue(&scheduler_queue); /* Do (part) of the job */ scheduler_task_callback *callback = task->task; void* args = task->args; free(task); ya_result return_code; switch(return_code = callback(args)) { default: /* An error occurred */ log_err("scheduler: unexpected callback return code %r", return_code); case SCHEDULER_TASK_FINISHED: #ifndef NDEBUG log_debug("scheduler: task finished"); #endif /** * Retrieve and start the next thread * * @todo EXCEPT IF A SHUTDOWN HAS BEEN REQUESTED ? */ case SCHEDULER_TASK_DEQUEUE_DELAYED: if(dnscore_shuttingdown()) { log_info("scheduler: shutdown in progress : ignoring next job"); logger_flush(); return STOPPED_BY_APPLICATION_SHUTDOWN; } pthread_mutex_lock(&scheduler_delayed_queue_mutex); scheduler_thread_running = FALSE; if(!scheduler_thread_queue_isempty(&scheduler_delayed_queue)) { scheduler_thread* thread = scheduler_thread_queue_dequeue(&scheduler_delayed_queue); #ifndef NDEBUG log_debug("scheduler: dequeued thread %s::(%p, %p, %p, %p@%u)", thread->categoryname, thread->task_init, thread->task, thread->args, thread, thread->debug_count); log_debug("scheduler: starting thread %u", thread->debug_count); log_debug("scheduler: next thread init"); #endif if(thread->task_init != NULL) { thread->task_init(thread->args); } #ifndef NDEBUG log_debug("scheduler: next thread ready"); #endif if(thread->task != NULL) { #ifndef NDEBUG log_debug("scheduler: next thread start"); #endif scheduler_thread_running = TRUE; thread_pool_schedule_job(thread->task, thread->args, &scheduler_threads_counter, thread->categoryname); } free(thread); } pthread_mutex_unlock(&scheduler_delayed_queue_mutex); break; case SCHEDULER_TASK_PROGRESS: /* NOP */ #ifndef NDEBUG log_debug("scheduler: task progress"); #endif break; } return return_code; }