Skip to content

Commit 759915b

Browse files
committed
CCBC-1611: handle 0x0d CONFIG_ONLY status code
Treat the CONFIG_ONLY status as a signal to refresh configuration. The new or failed over nodes are set into config-only mode, where all data operatios will be failed with code 0x0d. It is possible that the SDK might be using stale configuration and send requests to the node, that is not part of the cluster anymore, so to work around this, the library will update the configuration and retry the operation. Change-Id: I32eab64cd99e04681a9111bc66e1e597663c8de7 Reviewed-on: https://review.couchbase.org/c/libcouchbase/+/197126 Reviewed-by: Brett Lawson <brett19@gmail.com> Tested-by: Build Bot <build@couchbase.com>
1 parent 6a1bce4 commit 759915b

9 files changed

Lines changed: 59 additions & 5 deletions

File tree

include/memcached/protocol_binary.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ typedef enum {
113113
/** The requested resource is locked */
114114
PROTOCOL_BINARY_RESPONSE_LOCKED = 0x09,
115115

116+
/** The node cannot execute operation, because the bucket is in config-only mode */
117+
PROTOCOL_BINARY_RESPONSE_CONFIG_ONLY = 0x0d,
118+
116119
/** The authentication context is stale. You should reauthenticate*/
117120
PROTOCOL_BINARY_RESPONSE_AUTH_STALE = 0x1f,
118121
/** Authentication failure (invalid user/password combination,

src/bootstrap.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ lcb_STATUS Bootstrap::bootstrap(unsigned options)
265265
"Not requesting a config refresh because of throttling parameters. Next refresh possible in %" PRIu64
266266
"ms or %u errors. "
267267
"See LCB_CNTL_CONFDELAY_THRESH and LCB_CNTL_CONFERRTHRESH to modify the throttling settings",
268-
LCB_NS2US(next_ts - now) / 1000, (unsigned)errthresh - errcounter);
268+
LCB_NS2US(next_ts - now) / 1000, errthresh - errcounter);
269269
return LCB_SUCCESS;
270270
}
271271
}

src/bucketconfig/bc_cccp.cc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,15 @@ void CccpProvider::stop_current_request(bool is_clean)
171171
*/
172172
lcb_STATUS CccpProvider::expect_config_with_version(const lcb_host_t *origin, config_version requested)
173173
{
174-
175174
auto current = parent->get_current_version();
176175
auto previous_expected = expected_config_version;
177-
if (current < requested && previous_expected < requested) {
176+
if (expected_config_version < requested) {
177+
expected_config_version = requested;
178+
}
179+
if (current < expected_config_version) {
178180
/*
179181
* The requested version is newer than we've seen so far (both current and previously requested)
180182
*/
181-
expected_config_version = requested;
182183
if (has_pending_request()) {
183184
/*
184185
* Only one configuration request could be in-flight, but the version is stored in expected_config_version
@@ -303,9 +304,21 @@ lcb_STATUS lcb::clconfig::cccp_update(Provider *provider, const char *host, cons
303304

304305
lcb_STATUS lcb::clconfig::schedule_get_config(Provider *provider, const lcb_host_t *origin, config_version version)
305306
{
307+
if (provider->type != CLCONFIG_CCCP) {
308+
return LCB_ERR_INVALID_ARGUMENT;
309+
}
306310
return static_cast<CccpProvider *>(provider)->expect_config_with_version(origin, version);
307311
}
308312

313+
lcb_STATUS lcb::clconfig::schedule_get_config(Provider *provider)
314+
{
315+
if (provider->type != CLCONFIG_CCCP) {
316+
return LCB_ERR_INVALID_ARGUMENT;
317+
}
318+
return static_cast<CccpProvider *>(provider)->schedule_next_request(LCB_SUCCESS, /* can_rollover */ true,
319+
/* skip_if_push_supported */ false);
320+
}
321+
309322
lcb_STATUS CccpProvider::update(const char *host, const std::string &config_json)
310323
{
311324
if (config_json.empty()) {

src/bucketconfig/clconfig.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,8 @@ void cccp_update(const void *cookie, lcb_STATUS err, const lcb_host_t *origin, c
733733

734734
lcb_STATUS schedule_get_config(Provider *provider, const lcb_host_t *origin, config_version version);
735735

736+
lcb_STATUS schedule_get_config(Provider *provider);
737+
736738
/**
737739
* @brief record status of SELECT_BUCKET command
738740
* @param cookie_

src/mcserver/mcserver.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,29 @@ void Server::handle_clustermap_notification(const MemcachedResponse &request)
502502
lcb::clconfig::config_version{epoch, revision});
503503
}
504504

505+
void Server::handle_config_only(const mc_PACKET *oldpkt)
506+
{
507+
const auto *address = has_valid_host() ? &get_host() : nullptr;
508+
509+
lcb_log(LOGARGS_T(DEBUG),
510+
LOGFMT "The bucket is configured in config-only mode on " LCB_HOST_FMT
511+
", refresh configuration and retry operation",
512+
LOGID_T(), LCB_HOST_ARG(this->settings, address));
513+
514+
auto *cccp = instance->confmon->get_provider(lcb::clconfig::CLCONFIG_CCCP);
515+
if (cccp != nullptr && cccp->enabled) {
516+
lcb::clconfig::schedule_get_config(cccp);
517+
} else {
518+
lcb_log(LOGARGS_T(DEBUG), LOGFMT "CCCP configuration provider is not enabled, using next available provider",
519+
LOGID_T());
520+
instance->confmon->do_next_provider();
521+
}
522+
523+
mc_PACKET *newpkt = mcreq_renew_packet(oldpkt);
524+
newpkt->flags &= ~MCREQ_STATE_FLAGS;
525+
instance->retryq->config_only_add((mc_EXPACKET *)newpkt);
526+
}
527+
505528
/**
506529
* Handles requests that are initiated by the KV engine. It only happens when PROTOCOL_BINARY_FEATURE_DUPLEX (0x0c) is
507530
* enabled for the connection.
@@ -647,6 +670,12 @@ Server::ReadState Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
647670
}
648671
DO_SWALLOW_PAYLOAD()
649672
goto GT_DONE;
673+
} else if (status == PROTOCOL_BINARY_RESPONSE_CONFIG_ONLY) {
674+
/* consume the header */
675+
DO_ASSIGN_PAYLOAD()
676+
handle_config_only(request);
677+
DO_SWALLOW_PAYLOAD()
678+
goto GT_DONE;
650679
} else if (status == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COLLECTION ||
651680
status == PROTOCOL_BINARY_RESPONSE_UNKNOWN_SCOPE) {
652681
/* consume the header */

src/mcserver/mcserver.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ class Server : public mc_PIPELINE
220220
bool handle_unknown_collection(MemcachedResponse &resinfo, mc_PACKET *oldpkt);
221221
void handle_server_request(const MemcachedResponse &request);
222222
void handle_clustermap_notification(const MemcachedResponse &request);
223+
void handle_config_only(const mc_PACKET *oldpkt);
223224

224225
bool maybe_retry_packet(mc_PACKET *pkt, lcb_STATUS err, protocol_binary_response_status status);
225226
bool maybe_reconnect_on_fake_timeout(lcb_STATUS received_error);

src/retryq.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ void RetryQueue::ucadd(mc_EXPACKET *pkt, lcb_STATUS orig_err, protocol_binary_re
474474
add(pkt, orig_err, status, nullptr, 0);
475475
}
476476

477+
void RetryQueue::config_only_add(mc_EXPACKET *pkt)
478+
{
479+
add(pkt, LCB_ERR_UNSUPPORTED_OPERATION, PROTOCOL_BINARY_RESPONSE_CONFIG_ONLY, nullptr, 0);
480+
}
481+
477482
static void fallback_handler(mc_CMDQUEUE *cq, mc_PACKET *pkt)
478483
{
479484
auto *instance = reinterpret_cast<lcb_INSTANCE *>(cq->cqdata);

src/retryq.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class RetryQueue
8888
*/
8989
void nmvadd(mc_EXPACKET *detchpkt);
9090
void ucadd(mc_EXPACKET *pkt, lcb_STATUS orig_err, protocol_binary_response_status status);
91+
void config_only_add(mc_EXPACKET *pkt);
9192

9293
/**
9394
* @brief Retry all queued operations

src/vbucket/vbucket.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ char *lcbvb_save_json(lcbvb_CONFIG *cfg)
934934
cJSON *root = cJSON_CreateObject();
935935

936936
switch (cfg->dtype) {
937-
case LCBVB_DIST_VBUCKET:
937+
case LCBVB_DIST_VBUCKET:
938938
tmp = cJSON_CreateString("vbucket");
939939
break;
940940
case LCBVB_DIST_KETAMA:

0 commit comments

Comments
 (0)