Skip to content
Browse files

CCBC-190: Create compat mode that cache configuration

Connecting to ns_server to retrieve the cluster topology is slow
and in 99% of the cases the cluster topology is stable anyway.
This patch allows you to create an instance that cache the cluster
topology on a local file and allows you to reuse that configuration.

The configuration cache is still considered as experimental.

Change-Id: Ie027600fd28f0934aa7e6e280d3b32f2ff6294bf
Reviewed-on: http://review.couchbase.org/24875
Tested-by: Trond Norbye <trond.norbye@gmail.com>
Reviewed-by: Sergey Avseyev <sergey.avseyev@gmail.com>
  • Loading branch information...
1 parent f74d651 commit 1561ee2d95944594dd347dcaf912d94a3407e1e4 @trondn trondn committed with avsej Feb 26, 2013
View
1 Makefile.am
@@ -125,6 +125,7 @@ libcouchbase_la_SOURCES = \
src/behavior.c \
src/compat.c \
src/config_static.h \
+ src/config_cache.c \
src/cookie.c \
src/error.c \
src/event.c \
View
2 NMakefile
@@ -37,7 +37,7 @@ libcouchbase_SOURCES = src\arithmetic.c src\base64.c src\behavior.c \
src\wait.c src\gethrtime.c plugins\io\win32\plugin-win32.c src\isasl.c \
src\compat.c contrib\http_parser\http_parser.c src\http.c \
src\observe.c src\timer.c src\verbosity.c src\sanitycheck.c \
- src\url_encoding.c \
+ src\url_encoding.c src\config_cache.c \
contrib\libvbucket\crc32.c contrib\libvbucket\cJSON.c \
contrib\libvbucket\vbucket.c contrib\libvbucket\ketama.c \
contrib\libvbucket\rfc1321\md5c.c
View
5 RELEASE_NOTES.markdown
@@ -3,6 +3,11 @@
This document is a list of user visible feature changes and important
bugfixes. Do not forget to update this doc in every important patch.
+
+## (Not released)
+* [minor] CCBC-190 New compat mode (experimental) for configuration
+ caching. See man lcb_create_compat
+
## 2.0.4 (2013-03-06)
* [minor] CCBC-185 The bootstrap URI is not parsed correctly
View
17 include/libcouchbase/compat.h
@@ -31,13 +31,17 @@
extern "C" {
#endif
- enum lcb_cluster_t {
- LCB_MEMCACHED_CLUSTER = 0x00
+ enum lcb_compat_t {
+ LCB_MEMCACHED_CLUSTER = 0x00,
+ LCB_CACHED_CONFIG = 0x01
};
- typedef enum lcb_cluster_t lcb_cluster_t;
+
+ typedef enum lcb_compat_t lcb_compat_t;
+ /* For backwards compatibility mode */
+ typedef enum lcb_compat_t lcb_cluster_t;
LIBCOUCHBASE_API
- lcb_error_t lcb_create_compat(lcb_cluster_t type,
+ lcb_error_t lcb_create_compat(lcb_compat_t type,
const void *specific,
lcb_t *instance,
struct lcb_io_opt_st *io);
@@ -48,6 +52,11 @@ extern "C" {
const char *password;
};
+ struct lcb_cached_config_st {
+ struct lcb_create_st createopt;
+ const char *cachefile;
+ };
+
#ifdef __cplusplus
}
#endif
View
105 man/bsd/man3/lcb_create_compat.3
@@ -2,12 +2,12 @@
.\" Title: lcb_create_compat
.\" Author: Trond Norbye <trond.norbye@couchbase.com>
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
-.\" Date: 01/07/2013
+.\" Date: 03/21/2013
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "LCB_CREATE_COMPAT" "3" "01/07/2013" "\ \&" "\ \&"
+.TH "LCB_CREATE_COMPAT" "3" "03/21/2013" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -33,14 +33,43 @@ lcb_create_compat \- Create instance
.sp
cc [ flag \&... ] file\&... \-lcouchbase [ library \&... ] #include <libcouchbase\&.h>
.sp
-lcb_error_t lcb_create_compat(lcb_cluster_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io);
+lcb_error_t lcb_create_compat(lcb_compat_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io);
.SH "DESCRIPTION"
.sp
-lcb_create_compat creates an instance of libcouchbase to be used without a Couchbase cluster\&.
+lcb_create_compat creates an instance of libcouchbase with special configuration options\&.
.sp
-The type parameter specifices the type of compatibility backend to use\&. Currently this must be set to LCB_MEMCACHED_CLUSTER\&.
+The type parameter specifices the type of compatibility backend to use\&. The following types are defined:
.sp
-The content of the specific parameter depends on the type parameter\&. For LCB_MEMCACHED_CLUSTER the spesific parameter points to a struct lcb_memcached_st:
+.if n \{\
+.RS 4
+.\}
+.nf
++LCB_MEMCACHED_CLUSTER+ Disable the REST configuration
+ protocol and connect to a fixed
+ list of memcached servers\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
++LCB_CACHED_CONFIG+ Use a cached copy of the cluster
+ topology to bypass the REST
+ bootstrapping\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The content of the specific parameter depends on the type parameter\&.
+.sp
+See lcb_create() for more information about the instance and io parameter\&.
+.SS "LCB_MEMCACHED_CLUSTER"
+.sp
+For LCB_MEMCACHED_CLUSTER the specific parameter points to a struct lcb_memcached_st:
.sp
.if n \{\
.RS 4
@@ -57,13 +86,31 @@ struct lcb_memcached_st {
.\}
.sp
The serverlist is a list of server separated with a semicolon\&. username and password may be specified to use SASL authentication to the memcached cluster\&.
+.SS "LCB_CACHED_CONFIG"
.sp
-See lcb_create() for more information about the instance and io parameter\&.
+For LCB_MEMCACHED_CLUSTER the specific parameter points to a struct lcb_cached_config_st:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+struct lcb_cached_config_st {
+ struct lcb_create_st createopt;
+ const char *cachefile;
+};
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+See lcb_create() for a description of the internals of the createopt structure\&. The information in the createopt structure is used if the cachefile is missing or stale\&. The cachefile is used to specify a file (absolute or relative) where the cluster topology is stored\&. If the file exists the entire bootstrapping logic is skipped during startup, and delayed until an error is returned from the cluster indicating that the information in the cache is stale\&.
.SH "RETURN VALUES"
.sp
lcb_create() returns the LCB_SUCCESS on success, or a specific error code upon failure\&. See lcb_strerror(3) for more information\&.
.SH "EXAMPLES"
.sp
+Example 1: Create an instance that may be used with a memcached cluster\&.
+.sp
.if n \{\
.RS 4
.\}
@@ -100,6 +147,50 @@ if (err != LCB_SUCCESS) {
.if n \{\
.RE
.\}
+.sp
+Example 2: Create an instance that uses configuration caching:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+lcb_t instance;
+lcb_error_t err;
+struct lcb_cached_config_st config;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+memset(&config, 0, sizeof(config));
+config\&.createopt\&.version = 1;
+config\&.createopt\&.v\&.v1\&.host = "host1";
+config\&.createopt\&.v\&.v1\&.user = "mybucket";
+config\&.createopt\&.v\&.v1\&.passwd = "secret";
+config\&.createopt\&.v\&.v1\&.bucket = "mybucket";
+config\&.cachefile = "/var/tmp/couchbase\-config\-cache";
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+err = lcb_create_compat(LCB_CACHED_CONFIG, &config,
+ &instance, NULL);
+if (err != LCB_SUCCESS) {
+ \&.\&.\&. error \&.\&.\&.
+}
+.fi
+.if n \{\
+.RE
+.\}
.SH "ATTRIBUTES"
.sp
See lcb_attributes(5) for descriptions of the following attributes:
View
65 man/man3couchbase/lcb_create_compat.3couchbase.txt
@@ -11,22 +11,37 @@ SYNOPSIS
cc [ flag ... ] file... -lcouchbase [ library ... ]
#include <libcouchbase.h>
-lcb_error_t lcb_create_compat(lcb_cluster_t type,
+lcb_error_t lcb_create_compat(lcb_compat_t type,
const void *specific,
lcb_t *instance,
struct lcb_io_opt_st *io);
DESCRIPTION
-----------
-lcb_create_compat creates an instance of libcouchbase to be used
-without a Couchbase cluster.
+lcb_create_compat creates an instance of libcouchbase with special
+configuration options.
The +type+ parameter specifices the type of compatibility backend to
-use. Currently this must be set to +LCB_MEMCACHED_CLUSTER+.
+use. The following types are defined:
+
+ +LCB_MEMCACHED_CLUSTER+ Disable the REST configuration
+ protocol and connect to a fixed
+ list of memcached servers.
+
+ +LCB_CACHED_CONFIG+ Use a cached copy of the cluster
+ topology to bypass the REST
+ bootstrapping.
The content of the +specific+ parameter depends on the +type+
-parameter. For +LCB_MEMCACHED_CLUSTER+ the +spesific+ parameter points
+parameter.
+
+See lcb_create() for more information about the +instance+ and +io+
+parameter.
+
+LCB_MEMCACHED_CLUSTER
+~~~~~~~~~~~~~~~~~~~~~
+For +LCB_MEMCACHED_CLUSTER+ the +specific+ parameter points
to a struct lcb_memcached_st:
struct lcb_memcached_st {
@@ -39,8 +54,23 @@ The +serverlist+ is a list of server separated with a
semicolon. +username+ and +password+ may be specified to use SASL
authentication to the memcached cluster.
-See lcb_create() for more information about the +instance+ and +io+
-parameter.
+LCB_CACHED_CONFIG
+~~~~~~~~~~~~~~~~~
+For +LCB_MEMCACHED_CLUSTER+ the +specific+ parameter points
+to a struct lcb_cached_config_st:
+
+ struct lcb_cached_config_st {
+ struct lcb_create_st createopt;
+ const char *cachefile;
+ };
+
+See lcb_create() for a description of the internals of the +createopt+
+structure. The information in the +createopt+ structure is used if the
++cachefile+ is missing or stale. The +cachefile+ is used to specify a
+file (absolute or relative) where the cluster topology is stored. If
+the file exists the entire bootstrapping logic is skipped during
+startup, and delayed until an error is returned from the cluster
+indicating that the information in the cache is stale.
RETURN VALUES
-------------
@@ -49,6 +79,7 @@ code upon failure. See lcb_strerror(3couchbase) for more information.
EXAMPLES
--------
+Example 1: Create an instance that may be used with a memcached cluster.
struct lcb_memcached_st memcached;
lcb_t instance;
@@ -63,6 +94,26 @@ EXAMPLES
... error ...
}
+Example 2: Create an instance that uses configuration caching:
+
+ lcb_t instance;
+ lcb_error_t err;
+ struct lcb_cached_config_st config;
+
+ memset(&config, 0, sizeof(config));
+ config.createopt.version = 1;
+ config.createopt.v.v1.host = "host1";
+ config.createopt.v.v1.user = "mybucket";
+ config.createopt.v.v1.passwd = "secret";
+ config.createopt.v.v1.bucket = "mybucket";
+ config.cachefile = "/var/tmp/couchbase-config-cache";
+
+ err = lcb_create_compat(LCB_CACHED_CONFIG, &config,
+ &instance, NULL);
+ if (err != LCB_SUCCESS) {
+ ... error ...
+ }
+
ATTRIBUTES
----------
See lcb_attributes(5) for descriptions of the following attributes:
View
105 man/svr4/man3couchbase/lcb_create_compat.3couchbase
@@ -2,12 +2,12 @@
.\" Title: lcb_create_compat
.\" Author: Trond Norbye <trond.norbye@couchbase.com>
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
-.\" Date: 01/07/2013
+.\" Date: 03/21/2013
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
-.TH "LCB_CREATE_COMPAT" "3couchbase" "01/07/2013" "\ \&" "\ \&"
+.TH "LCB_CREATE_COMPAT" "3couchbase" "03/21/2013" "\ \&" "\ \&"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -33,14 +33,43 @@ lcb_create_compat \- Create instance
.sp
cc [ flag \&... ] file\&... \-lcouchbase [ library \&... ] #include <libcouchbase\&.h>
.sp
-lcb_error_t lcb_create_compat(lcb_cluster_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io);
+lcb_error_t lcb_create_compat(lcb_compat_t type, const void *specific, lcb_t *instance, struct lcb_io_opt_st *io);
.SH "DESCRIPTION"
.sp
-lcb_create_compat creates an instance of libcouchbase to be used without a Couchbase cluster\&.
+lcb_create_compat creates an instance of libcouchbase with special configuration options\&.
.sp
-The type parameter specifices the type of compatibility backend to use\&. Currently this must be set to LCB_MEMCACHED_CLUSTER\&.
+The type parameter specifices the type of compatibility backend to use\&. The following types are defined:
.sp
-The content of the specific parameter depends on the type parameter\&. For LCB_MEMCACHED_CLUSTER the spesific parameter points to a struct lcb_memcached_st:
+.if n \{\
+.RS 4
+.\}
+.nf
++LCB_MEMCACHED_CLUSTER+ Disable the REST configuration
+ protocol and connect to a fixed
+ list of memcached servers\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
++LCB_CACHED_CONFIG+ Use a cached copy of the cluster
+ topology to bypass the REST
+ bootstrapping\&.
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The content of the specific parameter depends on the type parameter\&.
+.sp
+See lcb_create() for more information about the instance and io parameter\&.
+.SS "LCB_MEMCACHED_CLUSTER"
+.sp
+For LCB_MEMCACHED_CLUSTER the specific parameter points to a struct lcb_memcached_st:
.sp
.if n \{\
.RS 4
@@ -57,13 +86,31 @@ struct lcb_memcached_st {
.\}
.sp
The serverlist is a list of server separated with a semicolon\&. username and password may be specified to use SASL authentication to the memcached cluster\&.
+.SS "LCB_CACHED_CONFIG"
.sp
-See lcb_create() for more information about the instance and io parameter\&.
+For LCB_MEMCACHED_CLUSTER the specific parameter points to a struct lcb_cached_config_st:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+struct lcb_cached_config_st {
+ struct lcb_create_st createopt;
+ const char *cachefile;
+};
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+See lcb_create() for a description of the internals of the createopt structure\&. The information in the createopt structure is used if the cachefile is missing or stale\&. The cachefile is used to specify a file (absolute or relative) where the cluster topology is stored\&. If the file exists the entire bootstrapping logic is skipped during startup, and delayed until an error is returned from the cluster indicating that the information in the cache is stale\&.
.SH "RETURN VALUES"
.sp
lcb_create() returns the LCB_SUCCESS on success, or a specific error code upon failure\&. See lcb_strerror(3couchbase) for more information\&.
.SH "EXAMPLES"
.sp
+Example 1: Create an instance that may be used with a memcached cluster\&.
+.sp
.if n \{\
.RS 4
.\}
@@ -100,6 +147,50 @@ if (err != LCB_SUCCESS) {
.if n \{\
.RE
.\}
+.sp
+Example 2: Create an instance that uses configuration caching:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+lcb_t instance;
+lcb_error_t err;
+struct lcb_cached_config_st config;
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+memset(&config, 0, sizeof(config));
+config\&.createopt\&.version = 1;
+config\&.createopt\&.v\&.v1\&.host = "host1";
+config\&.createopt\&.v\&.v1\&.user = "mybucket";
+config\&.createopt\&.v\&.v1\&.passwd = "secret";
+config\&.createopt\&.v\&.v1\&.bucket = "mybucket";
+config\&.cachefile = "/var/tmp/couchbase\-config\-cache";
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+err = lcb_create_compat(LCB_CACHED_CONFIG, &config,
+ &instance, NULL);
+if (err != LCB_SUCCESS) {
+ \&.\&.\&. error \&.\&.\&.
+}
+.fi
+.if n \{\
+.RE
+.\}
.SH "ATTRIBUTES"
.sp
See lcb_attributes(5) for descriptions of the following attributes:
View
117 src/compat.c
@@ -24,17 +24,35 @@
*/
#include "internal.h"
-static lcb_error_t create_memcached(const struct lcb_memcached_st *user,
- VBUCKET_CONFIG_HANDLE vbconfig);
-
+static lcb_error_t create_memcached_config(const struct lcb_memcached_st *user,
+ VBUCKET_CONFIG_HANDLE vbconfig);
+static lcb_error_t create_memcached_compat(const struct lcb_memcached_st *user,
+ lcb_t *instance,
+ struct lcb_io_opt_st *io);
+static lcb_error_t create_cached_compat(const struct lcb_cached_config_st *cfg,
+ lcb_t *instance,
+ struct lcb_io_opt_st *io);
LIBCOUCHBASE_API
lcb_error_t lcb_create_compat(lcb_cluster_t type,
const void *specific,
lcb_t *instance,
struct lcb_io_opt_st *io)
{
- lcb_error_t ret = LCB_NOT_SUPPORTED;
+ switch (type) {
+ case LCB_MEMCACHED_CLUSTER:
+ return create_memcached_compat(specific, instance, io);
+ case LCB_CACHED_CONFIG:
+ return create_cached_compat(specific, instance, io);
+ }
+
+ return LCB_NOT_SUPPORTED;
+}
+
+static lcb_error_t create_memcached_compat(const struct lcb_memcached_st *user,
+ lcb_t *instance,
+ struct lcb_io_opt_st *io)
+{
VBUCKET_CONFIG_HANDLE config;
lcb_error_t rc;
@@ -54,23 +72,20 @@ lcb_error_t lcb_create_compat(lcb_cluster_t type,
return LCB_CLIENT_ENOMEM;
}
- if (type == LCB_MEMCACHED_CLUSTER) {
- ret = create_memcached(specific, config);
- }
-
- if (ret == LCB_SUCCESS) {
+ rc = create_memcached_config(user, config);
+ if (rc == LCB_SUCCESS) {
lcb_apply_vbucket_config(*instance, config);
} else {
vbucket_config_destroy(config);
lcb_destroy(*instance);
*instance = NULL;
}
- return ret;
+ return rc;
}
-static lcb_error_t create_memcached(const struct lcb_memcached_st *user,
- VBUCKET_CONFIG_HANDLE vbconfig)
+static lcb_error_t create_memcached_config(const struct lcb_memcached_st *user,
+ VBUCKET_CONFIG_HANDLE vbconfig)
{
ringbuffer_t buffer;
char *copy = strdup(user->serverlist);
@@ -164,3 +179,81 @@ static lcb_error_t create_memcached(const struct lcb_memcached_st *user,
return LCB_SUCCESS;
}
+
+static const char *get_tmp_dir(void) {
+ const char *ret;
+ if ((ret = getenv("TMPDIR")) != NULL) {
+ return ret;
+ } else if ((ret = getenv("TEMPDIR")) != NULL) {
+ return ret;
+ } else if ((ret = getenv("TEMP")) != NULL) {
+ return ret;
+ } else if ((ret = getenv("TMP")) != NULL) {
+ return ret;
+ }
+
+ return NULL;
+}
+
+static char *mkcachefile(const char *name, const char *bucket) {
+ if (name != NULL) {
+ return strdup(name);
+ } else {
+ char buffer[1024];
+ const char *tmpdir = get_tmp_dir();
+
+ snprintf(buffer, sizeof(buffer),
+ "%s/%s", tmpdir ? tmpdir : ".", bucket);
+ return strdup(buffer);
+ }
+}
+
+static lcb_error_t create_cached_compat(const struct lcb_cached_config_st *cfg,
+ lcb_t *instance,
+ struct lcb_io_opt_st *io)
+{
+ lcb_error_t rc;
+ lcb_t inst;
+ const char *bucket;
+ struct lcb_create_st cst;
+
+ switch (cfg->createopt.version) {
+ case 0:
+ bucket = cfg->createopt.v.v0.bucket;
+ break;
+ case 1:
+ bucket = cfg->createopt.v.v1.bucket;
+ break;
+ default:
+ return LCB_NOT_SUPPORTED;
+ }
+
+ if (bucket == NULL) {
+ bucket = "default";
+ }
+
+ memcpy(&cst, &cfg->createopt, sizeof(cst));
+ if (cst.v.v0.io == NULL) {
+ cst.v.v0.io = io;
+ }
+
+ rc = lcb_create(instance, &cst);
+ if (rc != LCB_SUCCESS) {
+ return rc;
+ }
+
+ inst = *instance;
+ inst->compat.type = LCB_CACHED_CONFIG;
+ inst->compat.value.cached.cachefile = mkcachefile(cfg->cachefile,
+ bucket);
+
+ if (inst->compat.value.cached.cachefile == NULL) {
+ lcb_destroy(*instance);
+ *instance = NULL;
+ return LCB_CLIENT_ENOMEM;
+ }
+
+ lcb_load_config_cache(inst);
+
+ return LCB_SUCCESS;
+}
View
104 src/config_cache.c
@@ -0,0 +1,104 @@
+/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2010-2013 Couchbase, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "internal.h"
+
+int lcb_load_config_cache(lcb_t instance)
+{
+ ringbuffer_t buffer;
+ char line[1024];
+ ssize_t nr;
+ int fail;
+ FILE *fp;
+ VBUCKET_CONFIG_HANDLE config;
+ char *end;
+ struct stat st;
+
+ fp = fopen(instance->compat.value.cached.cachefile, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ if (fstat(fileno(fp), &st) == 0) {
+ if (instance->compat.value.cached.mtime == st.st_mtime) {
+ /* this is the one we have!!! */
+ fclose(fp);
+ return -1;
+ }
+ }
+
+ if ((config = vbucket_config_create()) == NULL ||
+ ringbuffer_initialize(&buffer, 2048) == -1) {
+ /* You'll have to do full bootsrap ;-) */
+ if (config != NULL) {
+ vbucket_config_destroy(config);
+ }
+ fclose(fp);
+ return -1;
+ }
+
+ while ((nr = fread(line, 1, sizeof(line), fp)) > 0) {
+ if (ringbuffer_ensure_capacity(&buffer, (lcb_size_t)nr) == -1) {
+ ringbuffer_destruct(&buffer);
+ fclose(fp);
+ return -1;
+ }
+ ringbuffer_write(&buffer, line, (lcb_size_t)nr);
+ }
+
+ if (ferror(fp)) {
+ ringbuffer_destruct(&buffer);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ end = strstr((char *)ringbuffer_get_read_head(&buffer),
+ "{{{fb85b563d0a8f65fa8d3d58f1b3a0708}}}");
+ if (end == NULL) {
+ /* This in an incomplete read */
+ return -1;
+ }
+
+ *end = '\0';
+ fail = vbucket_config_parse(config, LIBVBUCKET_SOURCE_MEMORY,
+ (char *)ringbuffer_get_read_head(&buffer));
+ ringbuffer_destruct(&buffer);
+
+ if (!fail) {
+ lcb_apply_vbucket_config(instance, config);
+ instance->compat.value.cached.mtime = st.st_mtime;
+ return 0;
+ }
+
+ return -1;
+}
+
+void lcb_refresh_config_cache(lcb_t instance)
+{
+ if (instance->compat.value.cached.updating) {
+ /* we are currently in the progress of updating the cache */
+ return ;
+ }
+
+ if (lcb_load_config_cache(instance) == -1) {
+ /* try to bootstrap it */
+ instance->compat.value.cached.updating = 1;
+ lcb_connect(instance);
+ }
+}
View
18 src/event.c
@@ -46,13 +46,26 @@ static int do_fill_input_buffer(lcb_server_t *c)
#endif
return 0;
default:
+ if (c->instance->compat.type == LCB_CACHED_CONFIG) {
+ /* Try to update the cache :S */
+ lcb_refresh_config_cache(c->instance);
+ return 0;
+ }
+
lcb_failout_server(c, LCB_NETWORK_ERROR);
return -1;
}
} else if (nr == 0) {
assert((iov[0].iov_len + iov[1].iov_len) != 0);
/* TODO stash error message somewhere
* "Connection closed... we should resend to other nodes or reconnect!!" */
+
+ if (c->instance->compat.type == LCB_CACHED_CONFIG) {
+ /* Try to update the cache :S */
+ lcb_refresh_config_cache(c->instance);
+ return 0;
+ }
+
lcb_failout_server(c, LCB_NETWORK_ERROR);
return -1;
} else {
@@ -184,6 +197,11 @@ static int parse_single(lcb_server_t *c, hrtime_t stop)
char *body;
lcb_size_t nbody;
lcb_server_t *new_srv;
+
+ if (c->instance->compat.type == LCB_CACHED_CONFIG) {
+ lcb_refresh_config_cache(c->instance);
+ }
+
/* re-schedule command to new server */
nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req));
assert(nr == sizeof(req));
View
43 src/instance.c
@@ -331,6 +331,7 @@ lcb_error_t lcb_create(lcb_t *instance,
}
*instance = obj;
obj->type = type;
+ obj->compat.type = (lcb_compat_t)0xdead;
lcb_initialize_packet_handlers(obj);
lcb_behavior_set_syncmode(obj, LCB_ASYNCHRONOUS);
lcb_behavior_set_ipv6(obj, LCB_IPV6_DISABLED);
@@ -672,6 +673,18 @@ static void lcb_update_serverlist(lcb_t instance)
"Failed to allocate memory for config");
return;
}
+
+ if (instance->compat.type == LCB_CACHED_CONFIG) {
+ FILE *fp = fopen(instance->compat.value.cached.cachefile, "w");
+ if (fp) {
+ fprintf(fp, "%s{{{fb85b563d0a8f65fa8d3d58f1b3a0708}}}",
+ instance->vbucket_stream.input.data);
+ fclose(fp);
+ }
+ instance->compat.value.cached.updating = 0;
+ instance->compat.value.cached.mtime = time(NULL) - 1;
+ }
+
if (vbucket_config_parse(next_config, LIBVBUCKET_SOURCE_MEMORY,
instance->vbucket_stream.input.data) != 0) {
lcb_error_handler(instance, LCB_PROTOCOL_ERROR,
@@ -827,7 +840,14 @@ static int parse_header(lcb_t instance)
err = LCB_PROTOCOL_ERROR;
break;
}
+
lcb_error_handler(instance, err, buffer->data);
+
+ if (instance->compat.type == LCB_CACHED_CONFIG) {
+ /* cached config should try a bootsrapping logic */
+ return -2;
+ }
+
return -1;
}
@@ -1049,10 +1069,22 @@ static void vbucket_stream_handler(lcb_socket_t sock, short which, void *arg)
} while ((lcb_size_t)nr == avail);
if (instance->vbucket_stream.header == NULL) {
- if (parse_header(instance) == -1) {
+ switch (parse_header(instance)) {
+ case -1:
/* error already reported */
lcb_maybe_breakout(instance);
return;
+ case -2:
+ release_socket(instance);
+ instance->backup_idx++;
+ if (lcb_switch_to_backup_node(instance, LCB_CONNECT_ERROR,
+ "Failed to parse REST response") != -1) {
+ return;
+ }
+ /* We should try another server */
+ return;
+ default:
+ ;
}
}
@@ -1246,7 +1278,7 @@ static lcb_error_t try_to_connect(lcb_t instance)
freeaddrinfo(instance->ai);
instance->ai = NULL;
}
-
+ instance->n_http_uri_sent = 0;
do {
setup_current_host(instance,
instance->backup_nodes[instance->backup_idx++]);
@@ -1313,6 +1345,13 @@ LIBCOUCHBASE_API
lcb_error_t lcb_connect(lcb_t instance)
{
instance->backup_idx = 0;
+ if (instance->compat.type == LCB_CACHED_CONFIG &&
+ instance->vbucket_config != NULL &&
+ instance->compat.value.cached.updating == 0)
+ {
+ return LCB_SUCCESS;
+ }
+
instance->io->v.v0.update_timer(instance->io,
instance->timeout.event,
instance->timeout.usec,
View
16 src/internal.h
@@ -32,6 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <memcached/protocol_binary.h>
#include <ep-engine/command_ids.h>
@@ -220,6 +221,18 @@ extern "C" {
void *event;
lcb_uint32_t usec;
} timeout;
+
+ struct {
+ lcb_compat_t type;
+ union {
+ struct {
+ time_t mtime;
+ char *cachefile;
+ int updating;
+ } cached;
+ } value;
+ } compat;
+
#ifdef LCB_DEBUG
lcb_debug_st debug;
#endif
@@ -534,6 +547,9 @@ extern "C" {
lcb_size_t npacket,
lcb_error_t err);
+ int lcb_load_config_cache(lcb_t instance);
+ void lcb_refresh_config_cache(lcb_t instance);
+
#ifdef __cplusplus
}
#endif
View
6 src/server.c
@@ -725,6 +725,12 @@ static void server_connect(lcb_server_t *server)
break;
} /* Else, we fallthrough */
+ if (server->instance->compat.type == LCB_CACHED_CONFIG) {
+ /* Try to update the cache :S */
+ lcb_refresh_config_cache(server->instance);
+ return;
+ }
+
default:
lcb_failout_server(server, LCB_CONNECT_ERROR);
return;
View
7 src/wait.c
@@ -42,6 +42,13 @@ lcb_error_t lcb_wait(lcb_t instance)
if (instance->wait != 0) {
return instance->last_error;
}
+
+ if (!instance->connected && !lcb_has_data_in_buffers(instance)
+ && instance->compat.type == LCB_CACHED_CONFIG
+ && instance->vbucket_config != NULL) {
+ return LCB_SUCCESS;
+ }
+
/*
* The API is designed for you to run your own event loop,
* but should also work if you don't do that.. In order to be

0 comments on commit 1561ee2

Please sign in to comment.
Something went wrong with that request. Please try again.