datasrc_clients_mgr.h 23.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef DATASRC_CLIENTS_MGR_H
#define DATASRC_CLIENTS_MGR_H 1

18
#include <util/threads/thread.h>
19
#include <util/threads/sync.h>
20

21
22
23
#include <log/logger_support.h>
#include <log/log_dbglevels.h>

24
25
#include <dns/rrclass.h>

26
#include <cc/data.h>
27
28

#include <datasrc/data_source.h>
29
#include <datasrc/client_list.h>
30
#include <datasrc/memory/zone_writer.h>
31

32
#include <auth/auth_log.h>
33
#include <auth/datasrc_config.h>
34

35
#include <boost/array.hpp>
36
#include <boost/bind.hpp>
37
#include <boost/shared_ptr.hpp>
38
#include <boost/noncopyable.hpp>
39

40
#include <exception>
41
#include <cassert>
42
43
44
45
46
47
#include <list>
#include <utility>

namespace isc {
namespace auth {

48
namespace datasrc_clientmgr_internal {
49
50
51
52
53
54
// This namespace is essentially private for DataSrcClientsMgr(Base) and
// DataSrcClientsBuilder(Base).  This is exposed in the public header
// only because these classes are templated (for testing purposes) and
// class internal has to be defined here.

/// \brief ID of commands from the DataSrcClientsMgr to DataSrcClientsBuilder.
55
enum CommandID {
56
    NOOP,         ///< Do nothing.  Only useful for tests; no argument
Jelte Jansen's avatar
Jelte Jansen committed
57
58
59
    RECONFIGURE,  ///< Reconfigure the datasource client lists,
                  ///  the argument to the command is the full new
                  ///  datasources configuration.
60
61
62
    LOADZONE,     ///< Load a new version of zone into a memory,
                  ///  the argument to the command is a map containing 'class'
                  ///  and 'origin' elements, both should have been validated.
63
64
    SHUTDOWN,     ///< Shutdown the builder; no argument
    NUM_COMMANDS
65
};
66
67
68
69
70
71
72

/// \brief The data type passed from DataSrcClientsMgr to
/// DataSrcClientsBuilder.
///
/// The first element of the pair is the command ID, and the second element
/// is its argument.  If the command doesn't take an argument it should be
/// a null pointer.
73
typedef std::pair<CommandID, data::ConstElementPtr> Command;
74
} // namespace datasrc_clientmgr_internal
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/// \brief Frontend to the manager object for data source clients.
///
/// This class provides interfaces for configuring and updating a set of
/// data source clients "in the background".  The user of this class can
/// assume any operation on this class can be done effectively non-blocking,
/// not suspending any delay-sensitive operations such as DNS query
/// processing.  The only exception is the time when this class object
/// is destroyed (normally as a result of an implicit call to the destructor);
/// in the current implementation it can take time depending on what is
/// running "in the background" at the time of the call.
///
/// Internally, an object of this class invokes a separate thread to perform
/// time consuming operations such as loading large zone data into memory,
/// but such details are completely hidden from the user of this class.
///
/// This class is templated only so that we can test the class without
/// involving actual threads or mutex.  Normal applications will only
/// need one specific specialization that has a typedef of
/// \c DataSrcClientsMgr.
95
96
template <typename ThreadType, typename BuilderType, typename MutexType,
          typename CondVarType>
97
class DataSrcClientsMgrBase : boost::noncopyable {
98
99
100
101
102
private:
    typedef std::map<dns::RRClass,
                     boost::shared_ptr<datasrc::ConfigurableClientList> >
    ClientListsMap;

103
public:
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    /// \brief Thread-safe accessor to the data source client lists.
    ///
    /// This class provides a simple wrapper for searching the client lists
    /// stored in the DataSrcClientsMgr in a thread-safe manner.
    /// It ensures the result of \c getClientList() can be used without
    /// causing a race condition with other threads that can possibly use
    /// the same manager throughout the lifetime of the holder object.
    ///
    /// This also means the holder object is expected to have a short lifetime.
    /// The application shouldn't try to keep it unnecessarily long.
    /// It's normally expected to create the holder object on the stack
    /// of a small scope and automatically let it be destroyed at the end
    /// of the scope.
    class Holder {
    public:
        Holder(DataSrcClientsMgrBase& mgr) :
            mgr_(mgr), locker_(mgr_.map_mutex_)
        {}

        /// \brief Find a data source client list of a specified RR class.
        ///
        /// It returns a pointer to the list stored in the manager if found,
        /// otherwise it returns NULL.  The manager keeps the ownership of
        /// the pointed object.  Also, it's not safe to get access to the
        /// object beyond the scope of the holder object.
129
130
131
132
133
134
135
136
137
        ///
        /// \note Since the ownership isn't transferred the return value
        /// could be a bare pointer (and it's probably better in several
        /// points).  Unfortunately, some unit tests currently don't work
        /// unless this method effectively shares the ownership with the
        /// tests.  That's the only reason why we return a shared pointer
        /// for now.  We should eventually fix it and change the return value
        /// type (see Trac ticket #2395).  Other applications must not
        /// assume the ownership is actually shared.
138
        boost::shared_ptr<datasrc::ConfigurableClientList> findClientList(
139
140
141
142
143
            const dns::RRClass& rrclass)
        {
            const ClientListsMap::const_iterator
                it = mgr_.clients_map_->find(rrclass);
            if (it == mgr_.clients_map_->end()) {
144
                return (boost::shared_ptr<datasrc::ConfigurableClientList>());
145
            } else {
146
                return (it->second);
147
148
149
150
151
152
153
            }
        }
    private:
        DataSrcClientsMgrBase& mgr_;
        typename MutexType::Locker locker_;
    };

154
155
156
157
158
159
160
161
162
163
164
165
    /// \brief Constructor.
    ///
    /// It internally invokes a separate thread and waits for further
    /// operations from the user application.
    ///
    /// This method is basically exception free except in case of really
    /// rare system-level errors.  When that happens the only reasonable
    /// action that the application can take would be to terminate the program
    /// in practice.
    ///
    /// \throw std::bad_alloc internal memory allocation failure.
    /// \throw isc::Unexpected general unexpected system errors.
166
    DataSrcClientsMgrBase() :
167
        clients_map_(new ClientListsMap),
Jelte Jansen's avatar
Jelte Jansen committed
168
169
        builder_(&command_queue_, &cond_, &queue_mutex_, &clients_map_,
                 &map_mutex_),
170
        builder_thread_(boost::bind(&BuilderType::run, &builder_))
171
    {}
172
173
174
175
176
177
178
179
180
181
182
183

    /// \brief The destructor.
    ///
    /// It tells the internal thread to stop and waits for it completion.
    /// In the current implementation, it can block for some unpredictably
    /// long period depending on what the thread is doing at that time
    /// (in future we may want to implement a rapid way of killing the thread
    /// and/or provide a separate interface for waiting so that the application
    /// can choose the timing).
    ///
    /// The waiting operation can result in an exception, but this method
    /// catches any of them so this method itself is exception free.
184
    ~DataSrcClientsMgrBase() {
185
186
187
        // We share class member variables with the builder, which will be
        // invalidated after the call to the destructor, so we need to make
        // sure the builder thread is terminated.  Depending on the timing
188
189
190
        // this could take a long time; if we don't want that to happen in
        // this context, we may want to introduce a separate 'shutdown()'
        // method.
191
192
193
194
        // Also, since we don't want to propagate exceptions from a destructor,
        // we catch any possible ones.  In fact the only really expected one
        // is Thread::UncaughtException when the builder thread died due to
        // an exception.  We specifically log it and just ignore others.
195
        try {
196
197
            sendCommand(datasrc_clientmgr_internal::SHUTDOWN,
                        data::ConstElementPtr());
198
            builder_thread_.wait();
199
        } catch (const util::thread::Thread::UncaughtException& ex) {
200
201
202
            // technically, logging this could throw, which will be propagated.
            // But such an exception would be a fatal one anyway, so we
            // simply let it go through.
203
204
            LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR).
                arg(ex.what());
205
206
207
208
        } catch (...) {
            LOG_ERROR(auth_logger,
                      AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
        }
209
210

        cleanup();              // see below
211
212
    }

213
214
215
216
    /// \brief Handle new full configuration for data source clients.
    ///
    /// This method simply passes the new configuration to the builder
    /// and immediately returns.  This method is basically exception free
217
218
    /// as long as the caller passes a non NULL value for \c config_arg;
    /// it doesn't validate the argument further.
219
220
221
222
223
224
225
226
227
228
    ///
    /// \brief isc::InvalidParameter config_arg is NULL.
    /// \brief std::bad_alloc
    ///
    /// \param config_arg The new data source configuration.  Must not be NULL.
    void reconfigure(data::ConstElementPtr config_arg) {
        if (!config_arg) {
            isc_throw(InvalidParameter, "Invalid null config argument");
        }
        sendCommand(datasrc_clientmgr_internal::RECONFIGURE, config_arg);
229
        reconfigureHook();      // for test's customization
230
231
    }

232
    /// \brief Set the underlying data source client lists to new lists.
233
234
235
236
    ///
    /// This is provided only for some existing tests until we support a
    /// cleaner way to use faked data source clients.  Non test code or
    /// newer tests must not use this.
237
    void setDataSrcClientLists(datasrc::ClientListMapPtr new_lists) {
238
        typename MutexType::Locker locker(map_mutex_);
239
        clients_map_ = new_lists;
240
241
    }

242
private:
243
244
245
246
247
248
    // This is expected to be called at the end of the destructor.  It
    // actually does nothing, but provides a customization point for
    // specialized class for tests so that the tests can inspect the last
    // state of the class.
    void cleanup() {}

249
250
251
    // same as cleanup(), for reconfigure().
    void reconfigureHook() {}

252
    void sendCommand(datasrc_clientmgr_internal::CommandID command,
253
254
                     data::ConstElementPtr arg)
    {
255
256
257
258
259
260
        // The lock will be held until the end of this method.  Only
        // push_back has to be protected, but we can avoid having an extra
        // block this way.
        typename MutexType::Locker locker(queue_mutex_);
        command_queue_.push_back(
            datasrc_clientmgr_internal::Command(command, arg));
261
        cond_.signal();
262
    }
263

264
265
266
267
    //
    // The following are shared with the builder.
    //
    // The list is used as a one-way queue: back-in, front-out
268
    std::list<datasrc_clientmgr_internal::Command> command_queue_;
269
270
    CondVarType cond_;          // condition variable for queue operations
    MutexType queue_mutex_;     // mutex to protect the queue
271
    datasrc::ClientListMapPtr clients_map_;
Jelte Jansen's avatar
Jelte Jansen committed
272
273
                                // map of actual data source client objects
    MutexType map_mutex_;       // mutex to protect the clients map
274

275
    BuilderType builder_;
276
    ThreadType builder_thread_; // for safety this should be placed last
277
278
};

279
namespace datasrc_clientmgr_internal {
280
281
282
283
284

/// \brief A class that maintains a set of data source clients.
///
/// An object of this class is supposed to run on a dedicated thread, whose
/// main function is a call to its \c run() method.  It runs in a loop
285
/// waiting for commands from the manager and handles each command (including
286
/// reloading a new version of zone data into memory or fully reconfiguration
287
/// of specific set of data source clients).  When it receives a SHUTDOWN
288
289
/// command, it exits from the loop, which will terminate the thread.
///
290
291
292
/// While this class is defined in a publicly visible namespace, it's
/// essentially private to \c DataSrcClientsMgr.  Except for tests,
/// applications should not directly access this class.
293
294
295
///
/// This class is templated so that we can test it without involving actual
/// threads or locks.
296
template <typename MutexType, typename CondVarType>
297
class DataSrcClientsBuilderBase : boost::noncopyable {
298
299
300
301
302
private:
    typedef std::map<dns::RRClass,
                     boost::shared_ptr<datasrc::ConfigurableClientList> >
    ClientListsMap;

303
public:
304
    /// \brief Internal errors in handling commands.
305
306
307
308
    ///
    /// This exception is expected to be caught within the
    /// \c DataSrcClientsBuilder implementation, but is defined as public
    /// so tests can be checked it.
309
    class InternalCommandError : public isc::Exception {
310
    public:
311
        InternalCommandError(const char* file, size_t line, const char* what) :
312
313
314
            isc::Exception(file, line, what) {}
    };

315
316
317
318
319
320
321
322
    /// \brief Constructor.
    ///
    /// It simply sets up a local copy of shared data with the manager.
    ///
    /// Note: this will take actual set (map) of data source clients and
    /// a mutex object for it in #2210 or #2212.
    ///
    /// \throw None
323
    DataSrcClientsBuilderBase(std::list<Command>* command_queue,
324
                              CondVarType* cond, MutexType* queue_mutex,
325
                              datasrc::ClientListMapPtr* clients_map,
326
                              MutexType* map_mutex
327
        ) :
328
329
        command_queue_(command_queue), cond_(cond), queue_mutex_(queue_mutex),
        clients_map_(clients_map), map_mutex_(map_mutex)
330
331
    {}

332
    /// \brief The main loop.
333
334
    void run();

335
336
337
338
339
340
    /// \brief Handle one command from the manager.
    ///
    /// This is a dedicated subroutine of run() and is essentially private,
    /// but is defined as a separate public method so we can test each
    /// command test individually.  In any case, this class itself is
    /// generally considered private.
341
    ///
342
    /// \return true if the builder should keep running; false otherwise.
343
344
345
    bool handleCommand(const Command& command);

private:
346
347
    // NOOP command handler.  We use this so tests can override it; the default
    // implementation really does nothing.
348
349
    void doNoop() {}

Jelte Jansen's avatar
Jelte Jansen committed
350
    void doReconfigure(const data::ConstElementPtr& config) {
351
        if (config) {
Jelte Jansen's avatar
Jelte Jansen committed
352
353
            LOG_INFO(auth_logger,
                     AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_STARTED);
354
            try {
Jelte Jansen's avatar
Jelte Jansen committed
355
356
357
358
359
                // Define new_clients_map outside of the block that
                // has the lock scope; this way, after the swap,
                // the lock is guaranteed to be released before
                // the old data is destroyed, minimizing the lock
                // duration.
360
                datasrc::ClientListMapPtr new_clients_map =
361
                    configureDataSource(config);
Jelte Jansen's avatar
Jelte Jansen committed
362
363
364
365
366
367
368
369
                {
                    typename MutexType::Locker locker(*map_mutex_);
                    new_clients_map.swap(*clients_map_);
                } // lock is released by leaving scope
                LOG_INFO(auth_logger,
                         AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS);
            } catch (const datasrc::ConfigurableClientList::ConfigurationError&
                     config_error) {
Jelte Jansen's avatar
Jelte Jansen committed
370
371
                LOG_ERROR(auth_logger,
                    AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR).
Jelte Jansen's avatar
Jelte Jansen committed
372
                    arg(config_error.what());
373
            } catch (const datasrc::DataSourceError& ds_error) {
Jelte Jansen's avatar
Jelte Jansen committed
374
                LOG_ERROR(auth_logger,
375
376
377
                    AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_DATASRC_ERROR).
                    arg(ds_error.what());
            } catch (const isc::Exception& isc_error) {
Jelte Jansen's avatar
Jelte Jansen committed
378
379
                LOG_ERROR(auth_logger,
                    AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_ERROR).
380
                    arg(isc_error.what());
381
            }
382
383
384
            // other exceptions are propagated, see
            // http://bind10.isc.org/ticket/2210#comment:13

Jelte Jansen's avatar
Jelte Jansen committed
385
            // old clients_map_ data is released by leaving scope
386
387
388
        }
    }

389
    void doLoadZone(const isc::data::ConstElementPtr& arg);
390
391
392
    boost::shared_ptr<datasrc::memory::ZoneWriter> getZoneWriter(
        datasrc::ConfigurableClientList& client_list,
        const dns::RRClass& rrclass, const dns::Name& origin);
393

394
    // The following are shared with the manager
395
396
397
    std::list<Command>* command_queue_;
    CondVarType* cond_;
    MutexType* queue_mutex_;
398
    datasrc::ClientListMapPtr* clients_map_;
399
    MutexType* map_mutex_;
400
401
402
403
404
405
};

// Shortcut typedef for normal use
typedef DataSrcClientsBuilderBase<util::thread::Mutex, util::thread::CondVar>
DataSrcClientsBuilder;

406
407
408
template <typename MutexType, typename CondVarType>
void
DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
409
    LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STARTED);
410

411
412
413
414
415
416
    try {
        bool keep_running = true;
        while (keep_running) {
            std::list<Command> current_commands;
            {
                // Move all new commands to local queue under the protection of
417
                // queue_mutex_.
418
419
420
421
                typename MutexType::Locker locker(*queue_mutex_);
                while (command_queue_->empty()) {
                    cond_->wait(*queue_mutex_);
                }
422
                current_commands.swap(*command_queue_);
Jelte Jansen's avatar
Jelte Jansen committed
423
            } // the lock is released here.
424
425

            while (keep_running && !current_commands.empty()) {
426
427
428
429
430
431
432
                try {
                    keep_running = handleCommand(current_commands.front());;
                } catch (const InternalCommandError& e) {
                    LOG_ERROR(auth_logger,
                              AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR).
                        arg(e.what());
                }
433
                current_commands.pop_front();
434
435
            }
        }
436

437
438
439
        LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STOPPED);
    } catch (const std::exception& ex) {
        // We explicitly catch exceptions so we can log it as soon as possible.
440
        LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED).
441
            arg(ex.what());
442
        std::terminate();
443
    } catch (...) {
444
        LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED_UNEXPECTED);
445
        std::terminate();
446
    }
447
448
449
450
451
452
453
}

template <typename MutexType, typename CondVarType>
bool
DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
    const Command& command)
{
454
455
456
457
458
    const CommandID cid = command.first;
    if (cid >= NUM_COMMANDS) {
        // This shouldn't happen except for a bug within this file.
        isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
    }
459

460
    const boost::array<const char*, NUM_COMMANDS> command_desc = {
461
        {"NOOP", "RECONFIGURE", "LOADZONE", "SHUTDOWN"}
462
463
464
    };
    LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
              AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
465
    switch (command.first) {
466
467
468
    case RECONFIGURE:
        doReconfigure(command.second);
        break;
469
470
471
    case LOADZONE:
        doLoadZone(command.second);
        break;
472
473
474
475
    case SHUTDOWN:
        return (false);
    case NOOP:
        doNoop();
476
477
478
        break;
    case NUM_COMMANDS:
        assert(false);          // we rejected this case above
479
480
481
    }
    return (true);
}
482
483
484
485
486
487

template <typename MutexType, typename CondVarType>
void
DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
    const isc::data::ConstElementPtr& arg)
{
488
    // We assume some basic level validation as this method can only be
JINMEI Tatuya's avatar
JINMEI Tatuya committed
489
490
    // called via the manager in practice.  manager is expected to do the
    // minimal validation.
491
    assert(arg);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
492
493
    assert(arg->get("class"));
    assert(arg->get("origin"));
494

495
496
497
498
    const dns::RRClass rrclass(arg->get("class")->stringValue());
    const dns::Name origin(arg->get("origin")->stringValue());
    ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
    if (found == (*clients_map_)->end()) {
499
500
        isc_throw(InternalCommandError, "failed to load a zone " << origin <<
                  "/" << rrclass << ": not configured for the class");
501
502
503
504
505
506
    }

    boost::shared_ptr<datasrc::ConfigurableClientList> client_list =
        found->second;
    assert(client_list);

507
    try {
508
        boost::shared_ptr<datasrc::memory::ZoneWriter> zwriter =
509
            getZoneWriter(*client_list, rrclass, origin);
510

511
512
513
514
515
516
517
518
519
        zwriter->load(); // this can take time but doesn't cause a race
        {   // install() can cause a race and must be in a critical section
            typename MutexType::Locker locker(*map_mutex_);
            zwriter->install();
        }
        LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
                  AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE)
            .arg(origin).arg(rrclass);

520
521
        // same as load(). We could let the destructor do it, but do it
        // ourselves explicitly just in case.
522
523
        zwriter->cleanup();
    } catch (const InternalCommandError& ex) {
524
        throw;     // this comes from getZoneWriter.  just let it go through.
525
526
527
528
529
530
531
    } catch (const isc::Exception& ex) {
        // We catch our internal exceptions (which will be just ignored) and
        // propagated others (which should generally be considered fatal and
        // will make the thread terminate)
        isc_throw(InternalCommandError, "failed to load a zone " << origin <<
                  "/" << rrclass << ": error occurred in reload: " <<
                  ex.what());
532
533
    }
}
534
535
536
537
538
539
540
541
542
543
544
545
546

// A dedicated subroutine of doLoadZone().  Separated just for keeping the
// main method concise.
template <typename MutexType, typename CondVarType>
boost::shared_ptr<datasrc::memory::ZoneWriter>
DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
    datasrc::ConfigurableClientList& client_list,
    const dns::RRClass& rrclass, const dns::Name& origin)
{
    const datasrc::ConfigurableClientList::ZoneWriterPair writerpair =
        client_list.getCachedZoneWriter(origin);

    switch (writerpair.first) {
547
    case datasrc::ConfigurableClientList::ZONE_SUCCESS:
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
        assert(writerpair.second);
        return (writerpair.second);
    case datasrc::ConfigurableClientList::ZONE_NOT_FOUND:
        isc_throw(InternalCommandError, "failed to load zone " << origin
                  << "/" << rrclass << ": not found in any configured "
                  "data source.");
    case datasrc::ConfigurableClientList::ZONE_NOT_CACHED:
        isc_throw(InternalCommandError, "failed to load zone " << origin
                  << "/" << rrclass << ": not served from memory");
    case datasrc::ConfigurableClientList::CACHE_DISABLED:
        // This is an internal error. Auth server must have the cache
        // enabled.
        isc_throw(InternalCommandError, "failed to load zone " << origin
                  << "/" << rrclass << ": internal failure, in-memory cache "
                  "is somehow disabled");
    }
564
565
566
567

    // all cases above should either return or throw, but some compilers
    // still need a return statement
    return (boost::shared_ptr<datasrc::memory::ZoneWriter>());
568
}
569
} // namespace datasrc_clientmgr_internal
570
571
572
573
574

/// \brief Shortcut type for normal data source clients manager.
///
/// In fact, for non test applications this is the only type of this kind
/// to be considered.
575
576
577
578
typedef DataSrcClientsMgrBase<
    util::thread::Thread,
    datasrc_clientmgr_internal::DataSrcClientsBuilder,
    util::thread::Mutex, util::thread::CondVar> DataSrcClientsMgr;
579
580
581
582
583
584
585
586
} // namespace auth
} // namespace isc

#endif  // DATASRC_CLIENTS_MGR_H

// Local Variables:
// mode: c++
// End: