Kea 3.1.5
d2_queue_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8#include <d2/d2_queue_mgr.h>
9#include <d2srv/d2_log.h>
10#include <dhcp_ddns/ncr_udp.h>
11#include <stats/stats_mgr.h>
12
13using namespace isc::stats;
14
15namespace isc {
16namespace d2 {
17
18// Makes constant visible to Google test macros.
20
21D2QueueMgr::D2QueueMgr(asiolink::IOServicePtr& io_service, const size_t max_queue_size)
22 : io_service_(io_service), max_queue_size_(max_queue_size),
23 mgr_state_(NOT_INITTED), target_stop_state_(NOT_INITTED) {
24 if (!io_service_) {
25 isc_throw(D2QueueMgrError, "IOServicePtr cannot be null");
26 }
27
28 // Use setter to do validation.
29 setMaxQueueSize(max_queue_size);
30}
31
34
35void
38 try {
39 // Note that error conditions must be handled here without throwing
40 // exceptions. Remember this is the application level "link" in the
41 // callback chain. Throwing an exception here will "break" the
42 // io_service "run" we are operating under. With that in mind,
43 // if we hit a problem, we will stop the listener transition to
44 // the appropriate stopped state. Upper layer(s) must monitor our
45 // state as well as our queue size.
46 switch (result) {
48 // Receive was successful, attempt to queue the request.
49 if (getQueueSize() < getMaxQueueSize()) {
50 // There's room on the queue, add to the end
51 enqueue(ncr);
52
53 // Log that we got the request
57 .arg(ncr->getRequestId());
58 return;
59 }
60
61 // Queue is full, stop the listener.
62 // Note that we can move straight to a STOPPED state as there
63 // is no receive in progress.
65 .arg(max_queue_size_);
66 StatsMgr::instance().addValue("queue-mgr-queue-full", static_cast<int64_t>(1));
68 break;
69
71 if (mgr_state_ == STOPPING) {
72 // This is confirmation that the listener has stopped and its
73 // callback will not be called again, unless its restarted.
74 updateStopState();
75 } else {
76 // We should not get a receive complete status of stopped
77 // unless we canceled the read as part of stopping. Therefore
78 // this is unexpected so we will treat it as a receive error.
79 // This is most likely an unforeseen programmatic issue.
81 .arg(D2QueueMgr::stateToText(mgr_state_));
83 }
84
85 break;
86
87 default:
88 // Receive failed, stop the listener.
89 // Note that we can move straight to a STOPPED state as there
90 // is no receive in progress.
93 break;
94 }
95 } catch (const std::exception& ex) {
96 // On the outside chance a throw occurs, let's log it and swallow it.
98 .arg(ex.what());
99 }
100}
101
102void
104 const uint32_t port,
105 const dhcp_ddns::NameChangeFormat format,
106 const bool reuse_address) {
107
108 if (listener_) {
110 "D2QueueMgr listener is already initialized");
111 }
112
113 // Instantiate a UDP listener and set state to INITTED.
114 // Note UDP listener constructor does not throw.
115 listener_.reset(new dhcp_ddns::NameChangeUDPListener(ip_address, port, format,
116 shared_from_this(), reuse_address));
117 mgr_state_ = INITTED;
118}
119
120void
122 // We can't listen if we haven't initialized the listener yet.
123 if (!listener_) {
124 isc_throw(D2QueueMgrError, "D2QueueMgr "
125 "listener is not initialized, cannot start listening");
126 }
127
128 // If we are already listening, we do not want to "reopen" the listener
129 // and really we shouldn't be trying.
130 if (mgr_state_ == RUNNING) {
131 isc_throw(D2QueueMgrError, "D2QueueMgr "
132 "cannot call startListening from the RUNNING state");
133 }
134
135 // Instruct the listener to start listening and set state accordingly.
136 try {
137 listener_->startListening(io_service_);
138 mgr_state_ = RUNNING;
139 } catch (const isc::Exception& ex) {
140 isc_throw(D2QueueMgrError, "D2QueueMgr listener start failed: "
141 << ex.what());
142 }
143
146}
147
148void
149D2QueueMgr::stopListening(const State target_stop_state) {
150 if (listener_) {
151 // Enforce only valid "stop" states.
152 // This is purely a programmatic error and should never happen.
153 if (target_stop_state != STOPPED &&
154 target_stop_state != STOPPED_QUEUE_FULL &&
155 target_stop_state != STOPPED_RECV_ERROR) {
157 "D2QueueMgr invalid value for stop state: "
158 << target_stop_state);
159 }
160
161 // Remember the state we want to achieve.
162 target_stop_state_ = target_stop_state;
163
164 // Instruct the listener to stop. If the listener reports that it
165 // has IO pending, then we transition to STOPPING to wait for the
166 // cancellation event. Otherwise, we can move directly to the targeted
167 // state.
168 listener_->stopListening();
169 if (listener_->isIoPending()) {
170 mgr_state_ = STOPPING;
171 } else {
172 updateStopState();
173 }
174 }
175}
176
177void
178D2QueueMgr::updateStopState() {
179 mgr_state_ = target_stop_state_;
182}
183
184void
186 // Force our managing layer(s) to stop us properly first.
187 if (mgr_state_ == RUNNING) {
189 "D2QueueMgr cannot delete listener while state is RUNNING");
190 }
191
192 listener_.reset();
193 mgr_state_ = NOT_INITTED;
194}
195
198 if (getQueueSize() == 0) {
200 "D2QueueMgr peek attempted on an empty queue");
201 }
202
203 return (ncr_queue_.front());
204}
205
207D2QueueMgr::peekAt(const size_t index) const {
208 if (index >= getQueueSize()) {
210 "D2QueueMgr peek beyond end of queue attempted"
211 << " index: " << index << " queue size: " << getQueueSize());
212 }
213
214 return (ncr_queue_.at(index));
215}
216
217void
218D2QueueMgr::dequeueAt(const size_t index) {
219 if (index >= getQueueSize()) {
221 "D2QueueMgr dequeue beyond end of queue attempted"
222 << " index: " << index << " queue size: " << getQueueSize());
223 }
224
225 RequestQueue::iterator pos = ncr_queue_.begin() + index;
226 ncr_queue_.erase(pos);
227}
228
229void
231 if (getQueueSize() == 0) {
233 "D2QueueMgr dequeue attempted on an empty queue");
234 }
235
236 ncr_queue_.pop_front();
237}
238
239void
241 ncr_queue_.push_back(ncr);
242}
243
244void
246 ncr_queue_.clear();
247}
248
249void
250D2QueueMgr::setMaxQueueSize(const size_t new_queue_max) {
251 if (new_queue_max < 1) {
253 "D2QueueMgr maximum queue size must be greater than zero");
254 }
255
256 if (new_queue_max < getQueueSize()) {
257 isc_throw(D2QueueMgrError, "D2QueueMgr maximum queue size value cannot"
258 " be less than the current queue size :" << getQueueSize());
259 }
260
261 max_queue_size_ = new_queue_max;
262}
263
264} // namespace isc::d2
265} // namespace isc
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Thrown if the queue manager encounters a general error.
Thrown if a queue index is beyond the end of the queue.
Thrown if the request queue empty and a read is attempted.
const dhcp_ddns::NameChangeRequestPtr & peek() const
Returns the entry at the front of the queue.
D2QueueMgr(asiolink::IOServicePtr &io_service, const size_t max_queue_size=MAX_QUEUE_DEFAULT)
Constructor.
State
Defines the list of possible states for D2QueueMgr.
virtual ~D2QueueMgr()
Destructor.
size_t getMaxQueueSize() const
Returns the maximum number of entries allowed in the queue.
static const size_t MAX_QUEUE_DEFAULT
Maximum number of entries allowed in the request queue.
void dequeue()
Removes the entry at the front of the queue.
const dhcp_ddns::NameChangeRequestPtr & peekAt(const size_t index) const
Returns the entry at a given position in the queue.
static std::string const & stateToText(State const &state)
Convert enum to string.
void removeListener()
Deletes the current listener.
void enqueue(dhcp_ddns::NameChangeRequestPtr &ncr)
Adds a request to the end of the queue.
void startListening()
Starts actively listening for requests.
void setMaxQueueSize(const size_t max_queue_size)
Sets the maximum number of entries allowed in the queue.
size_t getQueueSize() const
Returns the number of entries in the queue.
virtual void operator()(const dhcp_ddns::NameChangeListener::Result result, dhcp_ddns::NameChangeRequestPtr &ncr)
Function operator implementing the NCR receive callback.
void clearQueue()
Removes all entries from the queue.
void initUDPListener(const isc::asiolink::IOAddress &ip_address, const uint32_t port, const dhcp_ddns::NameChangeFormat format, const bool reuse_address=false)
Initializes the listener as a UDP listener.
void dequeueAt(const size_t index)
Removes the entry at a given position in the queue.
void stopListening(const State target_stop_state=STOPPED)
Stops listening for requests.
Result
Defines the outcome of an asynchronous NCR receive.
Definition ncr_io.h:171
Provides the ability to receive NameChangeRequests via UDP socket.
Definition ncr_udp.h:317
static StatsMgr & instance()
Statistics Manager accessor method.
This file defines the class D2QueueMgr.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR
Definition d2_messages.h:57
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE
Definition d2_messages.h:54
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR
Definition d2_messages.h:65
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP
Definition d2_messages.h:66
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED
Definition d2_messages.h:60
isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2")
Definition d2_log.h:19
isc::log::Logger d2_logger("dhcpddns")
Defines the logger used within D2.
Definition d2_log.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL
Definition d2_messages.h:53
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED
Definition d2_messages.h:62
NameChangeFormat
Defines the list of data wire formats supported.
Definition ncr_msg.h:59
boost::shared_ptr< NameChangeRequest > NameChangeRequestPtr
Defines a pointer to a NameChangeRequest.
Definition ncr_msg.h:241
const int DBGLVL_TRACE_BASIC
Trace basic operations.
const int DBGLVL_START_SHUT
This is given a value of 0 as that is the level selected if debugging is enabled without giving a lev...
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Defines the logger used by the top-level component of kea-lfc.
This file provides UDP socket based implementation for sending and receiving NameChangeRequests.