libcouchbase 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/ext/libcouchbase/.gitignore +2 -0
  3. data/ext/libcouchbase/CMakeLists.txt +5 -7
  4. data/ext/libcouchbase/README.markdown +2 -2
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +49 -0
  6. data/ext/libcouchbase/cmake/Modules/ConfigureDtrace.cmake +11 -0
  7. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -0
  8. data/ext/libcouchbase/cmake/Modules/GetLibcouchbaseFlags.cmake +2 -1
  9. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  10. data/ext/libcouchbase/cmake/config-cmake.h.in +2 -0
  11. data/ext/libcouchbase/cmake/defs.mk.in +0 -2
  12. data/ext/libcouchbase/cmake/source_files.cmake +34 -14
  13. data/ext/libcouchbase/configure.pl +1 -1
  14. data/ext/libcouchbase/contrib/genhash/genhash.h +6 -0
  15. data/ext/libcouchbase/include/libcouchbase/auth.h +10 -0
  16. data/ext/libcouchbase/include/libcouchbase/couchbase.h +10 -0
  17. data/ext/libcouchbase/include/libcouchbase/error.h +7 -0
  18. data/ext/libcouchbase/include/libcouchbase/n1ql.h +13 -1
  19. data/ext/libcouchbase/include/libcouchbase/plugins/io/bsdio-inl.c +1 -1
  20. data/ext/libcouchbase/include/libcouchbase/subdoc.h +9 -0
  21. data/ext/libcouchbase/include/libcouchbase/views.h +7 -1
  22. data/ext/libcouchbase/include/libcouchbase/visibility.h +1 -0
  23. data/ext/libcouchbase/include/memcached/protocol_binary.h +21 -1132
  24. data/ext/libcouchbase/packaging/parse-git-describe.pl +1 -1
  25. data/ext/libcouchbase/plugins/io/libev/libev_io_opts.h +3 -2
  26. data/ext/libcouchbase/src/README.md +0 -2
  27. data/ext/libcouchbase/src/auth-priv.h +1 -0
  28. data/ext/libcouchbase/src/auth.cc +10 -0
  29. data/ext/libcouchbase/src/bootstrap.cc +216 -0
  30. data/ext/libcouchbase/src/bootstrap.h +50 -39
  31. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +455 -0
  32. data/ext/libcouchbase/src/bucketconfig/bc_file.cc +281 -0
  33. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +528 -0
  34. data/ext/libcouchbase/src/bucketconfig/bc_http.h +50 -25
  35. data/ext/libcouchbase/src/bucketconfig/bc_mcraw.cc +115 -0
  36. data/ext/libcouchbase/src/bucketconfig/clconfig.h +407 -386
  37. data/ext/libcouchbase/src/bucketconfig/confmon.cc +378 -0
  38. data/ext/libcouchbase/src/cbft.cc +22 -27
  39. data/ext/libcouchbase/src/cntl.cc +24 -24
  40. data/ext/libcouchbase/src/connspec.cc +30 -1
  41. data/ext/libcouchbase/src/connspec.h +17 -0
  42. data/ext/libcouchbase/src/dns-srv.cc +143 -0
  43. data/ext/libcouchbase/src/{dump.c → dump.cc} +8 -11
  44. data/ext/libcouchbase/src/getconfig.cc +73 -0
  45. data/ext/libcouchbase/src/handler.cc +84 -85
  46. data/ext/libcouchbase/src/hostlist.cc +0 -1
  47. data/ext/libcouchbase/src/hostlist.h +6 -1
  48. data/ext/libcouchbase/src/http/http-priv.h +125 -112
  49. data/ext/libcouchbase/src/http/http.cc +9 -29
  50. data/ext/libcouchbase/src/http/http.h +1 -34
  51. data/ext/libcouchbase/src/http/http_io.cc +22 -26
  52. data/ext/libcouchbase/src/instance.cc +102 -28
  53. data/ext/libcouchbase/src/internal.h +47 -29
  54. data/ext/libcouchbase/src/jsparse/parser.cc +146 -202
  55. data/ext/libcouchbase/src/jsparse/parser.h +91 -98
  56. data/ext/libcouchbase/src/lcbht/lcbht.cc +177 -0
  57. data/ext/libcouchbase/src/lcbht/lcbht.h +174 -163
  58. data/ext/libcouchbase/src/lcbio/connect.cc +562 -0
  59. data/ext/libcouchbase/src/lcbio/connect.h +9 -2
  60. data/ext/libcouchbase/src/lcbio/ctx.c +1 -1
  61. data/ext/libcouchbase/src/lcbio/iotable.h +61 -16
  62. data/ext/libcouchbase/src/lcbio/ioutils.h +1 -1
  63. data/ext/libcouchbase/src/lcbio/manager.c +2 -2
  64. data/ext/libcouchbase/src/lcbio/timer-cxx.h +87 -0
  65. data/ext/libcouchbase/src/mc/mcreq.h +9 -2
  66. data/ext/libcouchbase/src/mcserver/mcserver.cc +723 -0
  67. data/ext/libcouchbase/src/mcserver/mcserver.h +160 -70
  68. data/ext/libcouchbase/src/mcserver/negotiate.cc +118 -152
  69. data/ext/libcouchbase/src/mcserver/negotiate.h +85 -74
  70. data/ext/libcouchbase/src/mctx-helper.h +51 -0
  71. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +1 -2
  72. data/ext/libcouchbase/src/n1ql/n1ql.cc +56 -32
  73. data/ext/libcouchbase/src/{newconfig.c → newconfig.cc} +42 -70
  74. data/ext/libcouchbase/src/nodeinfo.cc +4 -8
  75. data/ext/libcouchbase/src/operations/{cbflush.c → cbflush.cc} +7 -15
  76. data/ext/libcouchbase/src/operations/{counter.c → counter.cc} +0 -0
  77. data/ext/libcouchbase/src/operations/{durability-cas.c → durability-cas.cc} +92 -76
  78. data/ext/libcouchbase/src/operations/{durability-seqno.c → durability-seqno.cc} +55 -49
  79. data/ext/libcouchbase/src/operations/durability.cc +643 -0
  80. data/ext/libcouchbase/src/operations/durability_internal.h +212 -124
  81. data/ext/libcouchbase/src/operations/{get.c → get.cc} +24 -26
  82. data/ext/libcouchbase/src/operations/{observe-seqno.c → observe-seqno.cc} +5 -8
  83. data/ext/libcouchbase/src/operations/{observe.c → observe.cc} +69 -94
  84. data/ext/libcouchbase/src/operations/{pktfwd.c → pktfwd.cc} +0 -0
  85. data/ext/libcouchbase/src/operations/{remove.c → remove.cc} +0 -0
  86. data/ext/libcouchbase/src/operations/{stats.c → stats.cc} +66 -78
  87. data/ext/libcouchbase/src/operations/{store.c → store.cc} +27 -32
  88. data/ext/libcouchbase/src/operations/subdoc.cc +38 -18
  89. data/ext/libcouchbase/src/operations/{touch.c → touch.cc} +0 -0
  90. data/ext/libcouchbase/src/packetutils.h +200 -137
  91. data/ext/libcouchbase/src/probes.d +1 -1
  92. data/ext/libcouchbase/src/{retrychk.c → retrychk.cc} +3 -4
  93. data/ext/libcouchbase/src/retryq.cc +394 -0
  94. data/ext/libcouchbase/src/retryq.h +116 -104
  95. data/ext/libcouchbase/src/settings.h +2 -1
  96. data/ext/libcouchbase/src/ssl/ssl_c.c +1 -0
  97. data/ext/libcouchbase/src/ssl/ssl_e.c +0 -1
  98. data/ext/libcouchbase/src/trace.h +8 -8
  99. data/ext/libcouchbase/src/vbucket/vbucket.c +0 -1
  100. data/ext/libcouchbase/src/views/{docreq.c → docreq.cc} +48 -54
  101. data/ext/libcouchbase/src/views/docreq.h +24 -30
  102. data/ext/libcouchbase/src/views/viewreq.cc +318 -0
  103. data/ext/libcouchbase/src/views/viewreq.h +43 -13
  104. data/ext/libcouchbase/src/{wait.c → wait.cc} +12 -17
  105. data/ext/libcouchbase/tests/basic/t_connstr.cc +89 -50
  106. data/ext/libcouchbase/tests/basic/t_jsparse.cc +27 -78
  107. data/ext/libcouchbase/tests/basic/t_packet.cc +35 -42
  108. data/ext/libcouchbase/tests/htparse/t_basic.cc +58 -78
  109. data/ext/libcouchbase/tests/iotests/t_confmon.cc +94 -111
  110. data/ext/libcouchbase/tests/iotests/t_sched.cc +1 -2
  111. data/ext/libcouchbase/tests/mc/t_alloc.cc +9 -9
  112. data/ext/libcouchbase/tools/cbc-pillowfight.cc +1 -1
  113. data/lib/libcouchbase/version.rb +1 -1
  114. metadata +36 -39
  115. data/ext/libcouchbase/include/memcached/vbucket.h +0 -42
  116. data/ext/libcouchbase/src/bootstrap.c +0 -269
  117. data/ext/libcouchbase/src/bucketconfig/bc_cccp.c +0 -495
  118. data/ext/libcouchbase/src/bucketconfig/bc_file.c +0 -347
  119. data/ext/libcouchbase/src/bucketconfig/bc_http.c +0 -630
  120. data/ext/libcouchbase/src/bucketconfig/bc_mcraw.c +0 -150
  121. data/ext/libcouchbase/src/bucketconfig/confmon.c +0 -474
  122. data/ext/libcouchbase/src/getconfig.c +0 -100
  123. data/ext/libcouchbase/src/lcbht/lcbht.c +0 -282
  124. data/ext/libcouchbase/src/lcbio/connect.c +0 -557
  125. data/ext/libcouchbase/src/mcserver/mcserver.c +0 -784
  126. data/ext/libcouchbase/src/operations/durability.c +0 -668
  127. data/ext/libcouchbase/src/packetutils.c +0 -60
  128. data/ext/libcouchbase/src/retryq.c +0 -424
  129. data/ext/libcouchbase/src/simplestring.c +0 -211
  130. data/ext/libcouchbase/src/simplestring.h +0 -228
  131. data/ext/libcouchbase/src/ssobuf.h +0 -82
  132. data/ext/libcouchbase/src/views/viewreq.c +0 -358
  133. data/ext/libcouchbase/tests/basic/t_string.cc +0 -112
@@ -23,7 +23,16 @@
23
23
  #include "settings.h"
24
24
  #include "hostlist.h"
25
25
  #ifdef __cplusplus
26
+ namespace lcb {
27
+ namespace io {
28
+ struct Connstart;
29
+ }
30
+ }
31
+ typedef lcb::io::Connstart* lcbio_pCONNSTART;
26
32
  extern "C" {
33
+ #else
34
+ struct lcbio_CONNSTART;
35
+ typedef struct lcbio_CONNSTART* lcbio_pCONNSTART;
27
36
  #endif
28
37
 
29
38
  /**
@@ -39,11 +48,9 @@ extern "C" {
39
48
  * @{
40
49
  */
41
50
 
42
- struct lcbio_CONNSTART;
43
51
  struct lcbio_MGRREQ;
44
52
 
45
53
  /** @brief Pending connection request */
46
- typedef struct lcbio_CONNSTART *lcbio_pCONNSTART;
47
54
  typedef struct lcbio_MGRREQ *lcbio_pMGRREQ;
48
55
  typedef struct lcbio_TABLE *lcbio_pTABLE;
49
56
  typedef struct lcbio_TIMER *lcbio_pTIMER, *lcbio_pASYNC;
@@ -148,7 +148,7 @@ lcbio_ctx_close_ex(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb cb, void *arg,
148
148
  }
149
149
 
150
150
  oldrc = ctx->sock->refcount;
151
- lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Destroying. PND=%d,ENT=%d,SORC=%d", CTX_LOGID(ctx), (int)ctx->npending, (int)ctx->entered, oldrc);
151
+ lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Destroying context. Pending Writes=%d, Entered=%s, Socket Refcount=%d", CTX_LOGID(ctx), (int)ctx->npending, (int)ctx->entered ? "true": "false", oldrc);
152
152
 
153
153
  if (cb) {
154
154
  int reusable =
@@ -37,22 +37,6 @@
37
37
  extern "C" {
38
38
  #endif
39
39
 
40
- typedef struct lcbio_TABLE {
41
- lcb_io_opt_t p;
42
- lcb_iomodel_t model;
43
- lcb_timer_procs timer;
44
- lcb_loop_procs loop;
45
-
46
- union {
47
- struct {
48
- lcb_ev_procs ev;
49
- lcb_bsd_procs io;
50
- } v0;
51
- lcb_completion_procs completion;
52
- } u_io;
53
- unsigned refcount;
54
- void (*dtor)(void *);
55
- } lcbio_TABLE;
56
40
 
57
41
  /** Whether the underlying model is event-based */
58
42
  #define IOT_IS_EVENT(iot) ((iot)->model == LCB_IOMODEL_EVENT)
@@ -78,6 +62,67 @@ typedef struct lcbio_TABLE {
78
62
  /** First argument to IO Table */
79
63
  #define IOT_ARG(iot) (iot)->p
80
64
 
65
+ typedef struct lcbio_TABLE {
66
+ lcb_io_opt_t p;
67
+ lcb_iomodel_t model;
68
+ lcb_timer_procs timer;
69
+ lcb_loop_procs loop;
70
+
71
+ union {
72
+ struct {
73
+ lcb_ev_procs ev;
74
+ lcb_bsd_procs io;
75
+ } v0;
76
+ lcb_completion_procs completion;
77
+ } u_io;
78
+ unsigned refcount;
79
+ void (*dtor)(void *);
80
+
81
+ #ifdef __cplusplus
82
+ bool is_E() const { return IOT_IS_EVENT(this); }
83
+ bool is_C() const { return !is_E(); }
84
+ int get_errno() const { return IOT_ERRNO(this); }
85
+
86
+ void run_loop() { IOT_START(this); }
87
+ void stop_loop() { IOT_STOP(this); }
88
+
89
+ int E_connect(lcb_socket_t sock, const sockaddr* saddr, unsigned addrlen) {
90
+ return IOT_V0IO(this).connect0(p, sock, saddr, addrlen);
91
+ }
92
+
93
+ void E_close(lcb_socket_t sock) {
94
+ IOT_V0IO(this).close(p, sock);
95
+ }
96
+
97
+ void *E_event_create() {
98
+ return IOT_V0EV(this).create(p);
99
+ }
100
+
101
+ void E_event_watch(lcb_socket_t fd, void *event, short mask, void *arg,
102
+ lcb_ioE_callback cb) {
103
+ IOT_V0EV(this).watch(p, fd, event, mask, arg, cb);
104
+ }
105
+
106
+ void E_event_destroy(void *event) {
107
+ IOT_V0EV(this).destroy(p, event);
108
+ }
109
+
110
+ void E_event_cancel(lcb_socket_t fd, void *event) {
111
+ IOT_V0EV(this).cancel(p, fd, event);
112
+ }
113
+
114
+ void C_close(lcb_sockdata_t *sd) {
115
+ IOT_V1(this).close(p, sd);
116
+ }
117
+
118
+ int C_connect(lcb_sockdata_t *sd, const sockaddr *addr, unsigned addrlen,
119
+ lcb_io_connect_cb callback) {
120
+ return IOT_V1(this).connect(p, sd, addr, addrlen, callback);
121
+ }
122
+ #endif
123
+
124
+ } lcbio_TABLE;
125
+
81
126
  #ifdef __cplusplus
82
127
  }
83
128
  #endif
@@ -142,7 +142,7 @@ typedef struct {
142
142
  #define LCBIO_CONNREQ_POOLED 2
143
143
  #define LCBIO_CONNREQ_GENERIC 3
144
144
  union {
145
- struct lcbio_CONNSTART *cs; /**< from lcbio_connect() */
145
+ lcbio_pCONNSTART cs; /**< from lcbio_connect() */
146
146
  struct lcbio_MGRREQ *preq; /**< from lcbio_mgr_get() */
147
147
  void *p_generic; /**< Generic pointer. Destroyed via the dtor field */
148
148
  } u;
@@ -314,11 +314,11 @@ start_new_connection(mgr_HOST *he, uint32_t tmo)
314
314
 
315
315
  err = lcb_host_parsez(&tmphost, he->key, 80);
316
316
  if (err != LCB_SUCCESS) {
317
- lcb_log(LOGARGS(he->parent, ERROR), HE_LOGFMT "Could not parse host! Will supply dummy host", HE_LOGID(he));
317
+ lcb_log(LOGARGS(he->parent, ERROR), HE_LOGFMT "Could not parse host! Will supply dummy host (I=%p)", HE_LOGID(he), (void*)info);
318
318
  strcpy(tmphost.host, "BADHOST");
319
319
  strcpy(tmphost.port, "BADPORT");
320
320
  }
321
- lcb_log(LOGARGS(he->parent, DEBUG), HE_LOGFMT "Starting connection on I=%p", HE_LOGID(he), (void*)info);
321
+ lcb_log(LOGARGS(he->parent, TRACE), HE_LOGFMT "New pool entry: I=%p", HE_LOGID(he), (void*)info);
322
322
 
323
323
  info->cs = lcbio_connect(he->parent->io, he->parent->settings, &tmphost,
324
324
  tmo, on_connected, info);
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Copyright 2016 Couchbase, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #ifndef LCBIO_TIMER_CXX
18
+ #define LCBIO_TIMER_CXX
19
+
20
+ #include <lcbio/timer-ng.h>
21
+ #include <cstdlib>
22
+
23
+ namespace lcb {
24
+ namespace io {
25
+
26
+ class SimpleTimer {
27
+ public:
28
+ typedef void (Callback)(void*);
29
+ SimpleTimer(lcbio_pTABLE iot, void *data, Callback cb)
30
+ : inner(lcbio_timer_new(iot, data, cb)) {
31
+ }
32
+ ~SimpleTimer() {
33
+ release();
34
+ }
35
+ void release() {
36
+ if (inner != NULL) {
37
+ lcbio_timer_destroy(inner);
38
+ inner = NULL;
39
+ }
40
+ }
41
+ void signal() {
42
+ lcbio_async_signal(inner);
43
+ }
44
+ void cancel() {
45
+ lcbio_timer_disarm(inner);
46
+ }
47
+ bool is_armed() const {
48
+ return lcbio_timer_armed(inner);
49
+ }
50
+ void rearm(uint32_t usec) {
51
+ lcbio_timer_rearm(inner, usec);
52
+ }
53
+ void arm_if_disarmed(uint32_t usec) {
54
+ if (!is_armed()) {
55
+ rearm(usec);
56
+ }
57
+ }
58
+ void dump(FILE *fp) const {
59
+ lcbio_timer_dump(inner, fp);
60
+ }
61
+ private:
62
+ lcbio_pTIMER inner;
63
+ SimpleTimer(const SimpleTimer&);
64
+ };
65
+
66
+ template <typename T, void (T::*M)(void)>
67
+ class Timer : public SimpleTimer {
68
+ public:
69
+ Timer(lcbio_pTABLE iot, T* ptr)
70
+ : SimpleTimer(iot, ptr, cb) {
71
+ }
72
+
73
+ ~Timer() {
74
+ release();
75
+ }
76
+
77
+ private:
78
+ static void cb(void *arg) {
79
+ T *obj = reinterpret_cast<T*>(arg);
80
+ (obj->*M)();
81
+ }
82
+ Timer(const Timer&);
83
+ };
84
+
85
+ }
86
+ }
87
+ #endif
@@ -179,10 +179,17 @@ typedef struct {
179
179
  * packets, or when the packet itself is generated internally rather than
180
180
  * on behalf of an API request.
181
181
  */
182
- typedef struct {
182
+ typedef struct mc_REQDATAEX {
183
183
  const void *cookie; /**< User data */
184
184
  hrtime_t start; /**< Start time */
185
- mc_REQDATAPROCS *procs; /**< Common routines for the packet */
185
+ const mc_REQDATAPROCS *procs; /**< Common routines for the packet */
186
+
187
+ #ifdef __cplusplus
188
+ mc_REQDATAEX(const void *cookie_,
189
+ const mc_REQDATAPROCS &procs_, hrtime_t start_)
190
+ : cookie(cookie_), start(start_), procs(&procs_) {
191
+ }
192
+ #endif
186
193
  } mc_REQDATAEX;
187
194
 
188
195
  /**
@@ -0,0 +1,723 @@
1
+ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2011-2014 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "internal.h"
19
+ #include "logging.h"
20
+ #include "vbucket/aliases.h"
21
+ #include "settings.h"
22
+ #include "negotiate.h"
23
+ #include "bucketconfig/clconfig.h"
24
+ #include "mc/mcreq-flush-inl.h"
25
+ #include <lcbio/ssl.h>
26
+ #include "ctx-log-inl.h"
27
+
28
+ #define LOGARGS(c, lvl) (c)->settings, "server", LCB_LOG_##lvl, __FILE__, __LINE__
29
+ #define LOGARGS_T(lvl) LOGARGS(this, lvl)
30
+
31
+ #define LOGFMT "<%s:%s> (SRV=%p,IX=%d) "
32
+
33
+ #define LOGID(server) get_ctx_host(server->connctx), get_ctx_port(server->connctx), (void*)server, server->index
34
+ #define LOGID_T() LOGID(this)
35
+
36
+ #define MCREQ_MAXIOV 32
37
+ #define LCBCONN_UNWANT(conn, flags) (conn)->want &= ~(flags)
38
+
39
+ using namespace lcb;
40
+
41
+ static void on_error(lcbio_CTX *ctx, lcb_error_t err);
42
+
43
+ static void
44
+ on_flush_ready(lcbio_CTX *ctx)
45
+ {
46
+ Server *server = Server::get(ctx);
47
+ nb_IOV iov[MCREQ_MAXIOV];
48
+ int ready;
49
+
50
+ do {
51
+ int niov = 0;
52
+ unsigned nb;
53
+ nb = mcreq_flush_iov_fill(server, iov, MCREQ_MAXIOV, &niov);
54
+ if (!nb) {
55
+ return;
56
+ }
57
+ ready = lcbio_ctx_put_ex(ctx, (lcb_IOV *)iov, niov, nb);
58
+ } while (ready);
59
+ lcbio_ctx_wwant(ctx);
60
+ }
61
+
62
+ static void
63
+ on_flush_done(lcbio_CTX *ctx, unsigned expected, unsigned actual)
64
+ {
65
+ Server *server = Server::get(ctx);
66
+ lcb_U64 now = 0;
67
+ if (server->settings->readj_ts_wait) {
68
+ now = gethrtime();
69
+ }
70
+
71
+ mcreq_flush_done_ex(server, actual, expected, now);
72
+ server->check_closed();
73
+ }
74
+
75
+ void
76
+ Server::flush()
77
+ {
78
+ /** Call into the wwant stuff.. */
79
+ if (!connctx->rdwant) {
80
+ lcbio_ctx_rwant(connctx, 24);
81
+ }
82
+
83
+ lcbio_ctx_wwant(connctx);
84
+ lcbio_ctx_schedule(connctx);
85
+
86
+ if (!lcbio_timer_armed(io_timer)) {
87
+ /**
88
+ * XXX: Maybe use get_next_timeout(), although here we can assume
89
+ * that a command was just scheduled
90
+ */
91
+ lcbio_timer_rearm(io_timer, default_timeout());
92
+ }
93
+ }
94
+
95
+ LIBCOUCHBASE_API
96
+ void
97
+ lcb_sched_flush(lcb_t instance)
98
+ {
99
+ for (size_t ii = 0; ii < LCBT_NSERVERS(instance); ii++) {
100
+ Server *server = instance->get_server(ii);
101
+
102
+ if (!server->has_pending()) {
103
+ continue;
104
+ }
105
+ server->flush_start(server);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON
111
+ * payload then we refresh the configuration with it.
112
+ *
113
+ * This function returns 1 if the operation was successfully rescheduled;
114
+ * otherwise it returns 0. If it returns 0 then we give the error back to the
115
+ * user.
116
+ */
117
+ bool
118
+ Server::handle_nmv(MemcachedResponse& resinfo, mc_PACKET *oldpkt)
119
+ {
120
+ protocol_binary_request_header hdr;
121
+ lcb_error_t err = LCB_ERROR;
122
+ lcb_U16 vbid;
123
+ lcb::clconfig::Provider *cccp =
124
+ instance->confmon->get_provider(lcb::clconfig::CLCONFIG_CCCP);
125
+
126
+ mcreq_read_hdr(oldpkt, &hdr);
127
+ vbid = ntohs(hdr.request.vbucket);
128
+ lcb_log(LOGARGS_T(WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID_T(), (void*)oldpkt, oldpkt->opaque, vbid);
129
+
130
+ /* Notify of new map */
131
+ lcb_vbguess_remap(instance, vbid, index);
132
+
133
+ if (resinfo.bodylen() && cccp->enabled) {
134
+ std::string s(resinfo.body<const char*>(), resinfo.vallen());
135
+ err = lcb::clconfig::cccp_update(cccp, curhost->host, s.c_str());
136
+ }
137
+
138
+ if (err != LCB_SUCCESS) {
139
+ int bs_options;
140
+ if (instance->cur_configinfo->get_origin() == lcb::clconfig::CLCONFIG_CCCP) {
141
+ /**
142
+ * XXX: Not enough to see if cccp was enabled, since cccp might
143
+ * be requested by a user, but would still not actually be active
144
+ * for clusters < 2.5 If our current config is from CCCP
145
+ * then we can be fairly certain that CCCP is indeed working.
146
+ *
147
+ * For this reason, we don't use if (cccp->enabled) {...}
148
+ */
149
+ bs_options = BS_REFRESH_THROTTLE;
150
+ } else {
151
+ bs_options = BS_REFRESH_ALWAYS;
152
+ }
153
+ instance->bootstrap(bs_options);
154
+ }
155
+
156
+ if (!lcb_should_retry(settings, oldpkt, LCB_NOT_MY_VBUCKET)) {
157
+ return false;
158
+ }
159
+
160
+ /** Reschedule the packet again .. */
161
+ mc_PACKET *newpkt = mcreq_renew_packet(oldpkt);
162
+ newpkt->flags &= ~MCREQ_STATE_FLAGS;
163
+ instance->retryq->nmvadd((mc_EXPACKET*)newpkt);
164
+ return true;
165
+ }
166
+
167
+ /* This function is called within a loop to process a single packet.
168
+ *
169
+ * If a full packet is available, it will process the packet and return
170
+ * PKT_READ_COMPLETE, resulting in the `on_read()` function calling this
171
+ * function in a loop.
172
+ *
173
+ * When a complete packet is not available, PKT_READ_PARTIAL will be returned
174
+ * and the `on_read()` loop will exit, scheduling any required pending I/O.
175
+ */
176
+ Server::ReadState
177
+ Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
178
+ {
179
+ MemcachedResponse mcresp;
180
+ mc_PACKET *request;
181
+ unsigned pktsize = 24, is_last = 1;
182
+
183
+ #define RETURN_NEED_MORE(n) \
184
+ if (has_pending()) { \
185
+ lcbio_ctx_rwant(ctx, n); \
186
+ } \
187
+ return PKT_READ_PARTIAL; \
188
+
189
+ #define DO_ASSIGN_PAYLOAD() \
190
+ rdb_consumed(ior, mcresp.hdrsize()); \
191
+ if (mcresp.bodylen()) { \
192
+ mcresp.payload = rdb_get_consolidated(ior, mcresp.bodylen()); \
193
+ } {
194
+
195
+ #define DO_SWALLOW_PAYLOAD() \
196
+ } if (mcresp.bodylen()) { \
197
+ rdb_consumed(ior, mcresp.bodylen()); \
198
+ }
199
+
200
+ if (rdb_get_nused(ior) < pktsize) {
201
+ RETURN_NEED_MORE(pktsize)
202
+ }
203
+
204
+ /* copy bytes into the info structure */
205
+ rdb_copyread(ior, mcresp.hdrbytes(), mcresp.hdrsize());
206
+
207
+ pktsize += mcresp.bodylen();
208
+ if (rdb_get_nused(ior) < pktsize) {
209
+ RETURN_NEED_MORE(pktsize);
210
+ }
211
+
212
+ /* Find the packet */
213
+ if (mcresp.opcode() == PROTOCOL_BINARY_CMD_STAT && mcresp.keylen() != 0) {
214
+ is_last = 0;
215
+ request = mcreq_pipeline_find(this, mcresp.opaque());
216
+ } else {
217
+ is_last = 1;
218
+ request = mcreq_pipeline_remove(this, mcresp.opaque());
219
+ }
220
+
221
+ if (!request) {
222
+ lcb_log(LOGARGS_T(WARN), LOGFMT "Server sent us reply for a timed-out command. (OP=0x%x, RC=0x%x, SEQ=%u)", LOGID_T(), mcresp.opcode(), mcresp.status(), mcresp.opaque());
223
+ rdb_consumed(ior, pktsize);
224
+ return PKT_READ_COMPLETE;
225
+ }
226
+
227
+ if (mcresp.status() == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) {
228
+ /* consume the header */
229
+ DO_ASSIGN_PAYLOAD()
230
+ if (!handle_nmv(mcresp, request)) {
231
+ mcreq_dispatch_response(this, request, &mcresp, LCB_NOT_MY_VBUCKET);
232
+ }
233
+ DO_SWALLOW_PAYLOAD()
234
+ goto GT_DONE;
235
+ }
236
+
237
+ /* Figure out if the request is 'ufwd' or not */
238
+ if (!(request->flags & MCREQ_F_UFWD)) {
239
+ DO_ASSIGN_PAYLOAD();
240
+ mcresp.bufh = rdb_get_first_segment(ior);
241
+ mcreq_dispatch_response(this, request, &mcresp, LCB_SUCCESS);
242
+ DO_SWALLOW_PAYLOAD()
243
+
244
+ } else {
245
+ /* figure out how many buffers we want to use as an upper limit for the
246
+ * IOV arrays. Currently we'll keep it simple and ensure the entire
247
+ * response is contiguous. */
248
+ lcb_PKTFWDRESP resp = { 0 };
249
+ rdb_ROPESEG *segs;
250
+ nb_IOV iov;
251
+
252
+ rdb_consolidate(ior, pktsize);
253
+ rdb_refread_ex(ior, &iov, &segs, 1, pktsize);
254
+
255
+ resp.bufs = &segs;
256
+ resp.iovs = (lcb_IOV*)&iov;
257
+ resp.nitems = 1;
258
+ resp.header = mcresp.hdrbytes();
259
+ instance->callbacks.pktfwd(
260
+ instance, MCREQ_PKT_COOKIE(request), LCB_SUCCESS, &resp);
261
+ rdb_consumed(ior, pktsize);
262
+ }
263
+
264
+ GT_DONE:
265
+ if (is_last) {
266
+ mcreq_packet_handled(this, request);
267
+ }
268
+ return PKT_READ_COMPLETE;
269
+ }
270
+
271
+ static void
272
+ on_read(lcbio_CTX *ctx, unsigned)
273
+ {
274
+ Server *server = Server::get(ctx);
275
+ rdb_IOROPE *ior = &ctx->ior;
276
+
277
+ if (server->check_closed()) {
278
+ return;
279
+ }
280
+
281
+ Server::ReadState rv;
282
+ while ((rv = server->try_read(ctx, ior)) == Server::PKT_READ_COMPLETE);
283
+ lcbio_ctx_schedule(ctx);
284
+ lcb_maybe_breakout(server->instance);
285
+ }
286
+
287
+ static void flush_noop(mc_PIPELINE *pipeline) {
288
+ (void)pipeline;
289
+ }
290
+
291
+ static void server_connect(Server *server) {
292
+ server->connect();
293
+ }
294
+
295
+ bool
296
+ Server::maybe_retry_packet(mc_PACKET *pkt, lcb_error_t err)
297
+ {
298
+ lcbvb_DISTMODE dist_t = lcbvb_get_distmode(parent->config);
299
+
300
+ if (dist_t != LCBVB_DIST_VBUCKET) {
301
+ /** memcached bucket */
302
+ return false;
303
+ }
304
+ if (!lcb_should_retry(settings, pkt, err)) {
305
+ return false;
306
+ }
307
+
308
+ mc_PACKET *newpkt = mcreq_renew_packet(pkt);
309
+ newpkt->flags &= ~MCREQ_STATE_FLAGS;
310
+ instance->retryq->add((mc_EXPACKET *)newpkt, err);
311
+ return true;
312
+ }
313
+
314
+ static void
315
+ fail_callback(mc_PIPELINE *pipeline, mc_PACKET *pkt, lcb_error_t err, void *) {
316
+ static_cast<Server*>(pipeline)->purge_single(pkt, err);
317
+ }
318
+
319
+ void Server::purge_single(mc_PACKET *pkt, lcb_error_t err) {
320
+ if (maybe_retry_packet(pkt, err)) {
321
+ return;
322
+ }
323
+
324
+ if (err == LCB_AUTH_ERROR) {
325
+ /* In-situ auth errors are actually dead servers. Let's provide this
326
+ * as the actual error code. */
327
+ err = LCB_MAP_CHANGED;
328
+ }
329
+
330
+ if (err == LCB_ETIMEDOUT) {
331
+ lcb_error_t tmperr = lcb::RetryQueue::error_for(pkt);
332
+ if (tmperr != LCB_SUCCESS) {
333
+ err = tmperr;
334
+ }
335
+ }
336
+
337
+ protocol_binary_request_header hdr;
338
+ memcpy(hdr.bytes, SPAN_BUFFER(&pkt->kh_span), sizeof(hdr.bytes));
339
+ MemcachedResponse resp(protocol_binary_command(hdr.request.opcode),
340
+ hdr.request.opaque,
341
+ PROTOCOL_BINARY_RESPONSE_EINVAL);
342
+
343
+ lcb_log(LOGARGS_T(WARN), LOGFMT "Failing command (pkt=%p, opaque=%lu, opcode=0x%x) with error %s", LOGID_T(), (void*)pkt, (unsigned long)pkt->opaque, hdr.request.opcode, lcb_strerror_short(err));
344
+ int rv = mcreq_dispatch_response(this, pkt, &resp, err);
345
+ lcb_assert(rv == 0);
346
+ }
347
+
348
+ int
349
+ Server::purge(lcb_error_t error, hrtime_t thresh, hrtime_t *next,
350
+ RefreshPolicy policy)
351
+ {
352
+ unsigned affected;
353
+
354
+ if (thresh) {
355
+ affected = mcreq_pipeline_timeout(
356
+ this, error, fail_callback, NULL, thresh, next);
357
+
358
+ } else {
359
+ mcreq_pipeline_fail(this, error, fail_callback, NULL);
360
+ affected = -1;
361
+ }
362
+
363
+ if (policy == REFRESH_NEVER) {
364
+ return affected;
365
+ }
366
+
367
+ if (affected || policy == REFRESH_ALWAYS) {
368
+ instance->bootstrap(BS_REFRESH_THROTTLE|BS_REFRESH_INCRERR);
369
+ }
370
+ return affected;
371
+ }
372
+
373
+ static void flush_errdrain(mc_PIPELINE *pipeline)
374
+ {
375
+ /* Called when we are draining errors. */
376
+ Server *server = (Server *)pipeline;
377
+ if (!lcbio_timer_armed(server->io_timer)) {
378
+ lcbio_timer_rearm(server->io_timer, server->default_timeout());
379
+ }
380
+ }
381
+
382
+ uint32_t
383
+ Server::next_timeout() const
384
+ {
385
+ hrtime_t now, expiry, diff;
386
+ mc_PACKET *pkt = mcreq_first_packet(this);
387
+
388
+ if (!pkt) {
389
+ return default_timeout();
390
+ }
391
+
392
+ now = gethrtime();
393
+ expiry = MCREQ_PKT_RDATA(pkt)->start + LCB_US2NS(default_timeout());
394
+ if (expiry <= now) {
395
+ diff = 0;
396
+ } else {
397
+ diff = expiry - now;
398
+ }
399
+
400
+ return LCB_NS2US(diff);
401
+ }
402
+
403
+ static void
404
+ timeout_server(void *arg)
405
+ {
406
+ reinterpret_cast<Server*>(arg)->io_timeout();
407
+ }
408
+
409
+ void Server::io_timeout()
410
+ {
411
+ hrtime_t now = gethrtime();
412
+ hrtime_t min_valid = now - LCB_US2NS(default_timeout());
413
+
414
+ hrtime_t next_ns;
415
+ int npurged = purge(LCB_ETIMEDOUT, min_valid, &next_ns,
416
+ Server::REFRESH_ONFAILED);
417
+ if (npurged) {
418
+ lcb_log(LOGARGS_T(ERR), LOGFMT "Server timed out. Some commands have failed", LOGID_T());
419
+ }
420
+
421
+ uint32_t next_us = next_timeout();
422
+ lcb_log(LOGARGS_T(TRACE), LOGFMT "Scheduling next timeout for %u ms. This is not an error", LOGID_T(), next_us / 1000);
423
+ lcbio_timer_rearm(io_timer, next_us);
424
+ lcb_maybe_breakout(instance);
425
+ }
426
+
427
+ bool
428
+ Server::maybe_reconnect_on_fake_timeout(lcb_error_t err)
429
+ {
430
+ if (err != LCB_ETIMEDOUT) {
431
+ return false; /* not a timeout */
432
+ }
433
+ if (!settings->readj_ts_wait) {
434
+ return false; /* normal timeout behavior */
435
+ }
436
+ if (!has_pending()) {
437
+ return false; /* nothing pending */
438
+ }
439
+
440
+ uint32_t next_tmo = next_timeout();
441
+ if (next_tmo < default_timeout() / 2) {
442
+ /* Ideally we'd have a fuzz interval to shave off the actual timeout,
443
+ * since there will inevitably be some time taken off the next timeout */
444
+ return false;
445
+ }
446
+
447
+ lcb_log(LOGARGS_T(INFO), LOGFMT "Retrying connection. Assuming timeout because of stalled event loop", LOGID_T());
448
+ connect();
449
+ return true;
450
+ }
451
+
452
+ static void
453
+ on_connected(lcbio_SOCKET *sock, void *data, lcb_error_t err, lcbio_OSERR syserr)
454
+ {
455
+ Server *server = reinterpret_cast<Server*>(data);
456
+ server->handle_connected(sock, err, syserr);
457
+ }
458
+
459
+ static void mcserver_flush(Server *s) { s->flush(); }
460
+
461
+ void
462
+ Server::handle_connected(lcbio_SOCKET *sock, lcb_error_t err, lcbio_OSERR syserr)
463
+ {
464
+ LCBIO_CONNREQ_CLEAR(&connreq);
465
+
466
+ if (err != LCB_SUCCESS) {
467
+ lcb_log(LOGARGS_T(ERR), LOGFMT "Connection attempt failed. Received %s from libcouchbase, received %d from operating system", LOGID_T(), lcb_strerror_short(err), syserr);
468
+ if (!maybe_reconnect_on_fake_timeout(err)) {
469
+ socket_failed(err);
470
+ }
471
+ return;
472
+ }
473
+
474
+ lcb_assert(sock);
475
+
476
+ /** Do we need sasl? */
477
+ SessionInfo* sessinfo = SessionInfo::get(sock);
478
+ if (sessinfo == NULL) {
479
+ lcb_log(LOGARGS_T(TRACE), "<%s:%s> (SRV=%p) Session not yet negotiated. Negotiating", curhost->host, curhost->port, (void*)this);
480
+ SessionRequest *sreq = SessionRequest::start(
481
+ sock, settings, default_timeout(), on_connected, this);
482
+ LCBIO_CONNREQ_MKGENERIC(&connreq, sreq, lcb::sessreq_cancel);
483
+ return;
484
+ } else {
485
+ compsupport = sessinfo->has_feature(PROTOCOL_BINARY_FEATURE_DATATYPE);
486
+ mutation_tokens = sessinfo->has_feature(PROTOCOL_BINARY_FEATURE_MUTATION_SEQNO);
487
+ }
488
+
489
+ lcbio_CTXPROCS procs;
490
+ procs.cb_err = on_error;
491
+ procs.cb_read = on_read;
492
+ procs.cb_flush_done = on_flush_done;
493
+ procs.cb_flush_ready = on_flush_ready;
494
+ connctx = lcbio_ctx_new(sock, this, &procs);
495
+ connctx->subsys = "memcached";
496
+ flush_start = (mcreq_flushstart_fn)mcserver_flush;
497
+
498
+ uint32_t tmo = next_timeout();
499
+ lcbio_timer_rearm(io_timer, tmo);
500
+ flush();
501
+ }
502
+
503
+ void
504
+ Server::connect()
505
+ {
506
+ lcbio_pMGRREQ mr = lcbio_mgr_get(instance->memd_sockpool, curhost,
507
+ default_timeout(), on_connected, this);
508
+ LCBIO_CONNREQ_MKPOOLED(&connreq, mr);
509
+ flush_start = flush_noop;
510
+ state = Server::S_CLEAN;
511
+ }
512
+
513
+ static void
514
+ buf_done_cb(mc_PIPELINE *pl, const void *cookie, void *, void *)
515
+ {
516
+ Server *server = static_cast<Server*>(pl);
517
+ server->instance->callbacks.pktflushed(server->instance, cookie);
518
+ }
519
+
520
+ Server::Server(lcb_t instance_, int ix)
521
+ : mc_PIPELINE(), state(S_CLEAN),
522
+ io_timer(lcbio_timer_new(instance_->iotable, this, timeout_server)),
523
+ instance(instance_),
524
+ settings(lcb_settings_ref2(instance_->settings)),
525
+ compsupport(0),
526
+ mutation_tokens(0),
527
+ connctx(NULL),
528
+ curhost(new lcb_host_t())
529
+ {
530
+ mcreq_pipeline_init(this);
531
+ flush_start = (mcreq_flushstart_fn)server_connect;
532
+ buf_done_callback = buf_done_cb;
533
+ index = ix;
534
+
535
+ std::memset(&connreq, 0, sizeof connreq);
536
+ std::memset(curhost, 0, sizeof *curhost);
537
+
538
+ const char *datahost = lcbvb_get_hostport(
539
+ LCBT_VBCONFIG(instance), ix,
540
+ LCBVB_SVCTYPE_DATA, settings->sslopts & LCB_SSL_ENABLED ?
541
+ LCBVB_SVCMODE_SSL : LCBVB_SVCMODE_PLAIN);
542
+ if (datahost) {
543
+ lcb_host_parsez(curhost, datahost, LCB_CONFIG_MCD_PORT);
544
+ }
545
+ }
546
+
547
+ Server::Server()
548
+ : state(S_TEMPORARY),
549
+ io_timer(NULL), instance(NULL), settings(NULL), compsupport(0),
550
+ mutation_tokens(0), connctx(NULL), curhost(NULL)
551
+ {
552
+ }
553
+
554
+ Server::~Server() {
555
+ if (state == S_TEMPORARY) {
556
+ return;
557
+ }
558
+
559
+ mcreq_pipeline_cleanup(this);
560
+
561
+ if (io_timer) {
562
+ lcbio_timer_destroy(io_timer);
563
+ }
564
+
565
+ delete curhost;
566
+ lcb_settings_unref(settings);
567
+ }
568
+
569
+ static void
570
+ close_cb(lcbio_SOCKET *sock, int, void *)
571
+ {
572
+ lcbio_ref(sock);
573
+ lcbio_mgr_discard(sock);
574
+ }
575
+
576
+ static void
577
+ on_error(lcbio_CTX *ctx, lcb_error_t err)
578
+ {
579
+ Server *server = Server::get(ctx);
580
+ lcb_log(LOGARGS(server, WARN), LOGFMT "Got socket error %s", LOGID(server), lcb_strerror_short(err));
581
+ if (server->check_closed()) {
582
+ return;
583
+ }
584
+ server->socket_failed(err);
585
+ }
586
+
587
+ /**Handle a socket error. This function will close the current connection
588
+ * and trigger a failout of any pending commands.
589
+ * This function triggers a configuration refresh */
590
+ void
591
+ Server::socket_failed(lcb_error_t err)
592
+ {
593
+ if (check_closed()) {
594
+ return;
595
+ }
596
+
597
+ purge(err, 0, NULL, REFRESH_ALWAYS);
598
+ lcb_maybe_breakout(instance);
599
+ start_errored_ctx(S_ERRDRAIN);
600
+ }
601
+
602
+ void
603
+ Server::close()
604
+ {
605
+ /* Should never be called twice */
606
+ lcb_assert(state != Server::S_CLOSED);
607
+ start_errored_ctx(S_CLOSED);
608
+ }
609
+
610
+ /**
611
+ * Call to signal an error or similar on the current socket.
612
+ * @param server The server
613
+ * @param next_state The next state (S_CLOSED or S_ERRDRAIN)
614
+ */
615
+ void
616
+ Server::start_errored_ctx(State next_state)
617
+ {
618
+ lcbio_CTX *ctx = connctx;
619
+
620
+ state = next_state;
621
+ /* Cancel any pending connection attempt? */
622
+ lcbio_connreq_cancel(&connreq);
623
+
624
+ /* If the server is being destroyed, silence the timer */
625
+ if (next_state == Server::S_CLOSED && io_timer != NULL) {
626
+ lcbio_timer_destroy(io_timer);
627
+ io_timer = NULL;
628
+ }
629
+
630
+ if (ctx == NULL) {
631
+ if (next_state == Server::S_CLOSED) {
632
+ delete this;
633
+ return;
634
+ } else {
635
+ /* Not closed but don't have a current context */
636
+ if (has_pending()) {
637
+ if (!lcbio_timer_armed(io_timer)) {
638
+ /* TODO: Maybe throttle reconnection attempts? */
639
+ lcbio_timer_rearm(io_timer, default_timeout());
640
+ }
641
+ connect();
642
+ } else {
643
+ // Connect once someone actually wants a connection.
644
+ flush_start = (mcreq_flushstart_fn)server_connect;
645
+ }
646
+ }
647
+
648
+ } else {
649
+ if (ctx->npending) {
650
+ /* Have pending items? */
651
+
652
+ /* Flush any remaining events */
653
+ lcbio_ctx_schedule(ctx);
654
+
655
+ /* Close the socket not to leak resources */
656
+ lcbio_shutdown(lcbio_ctx_sock(ctx));
657
+ if (next_state == Server::S_ERRDRAIN) {
658
+ flush_start = (mcreq_flushstart_fn)flush_errdrain;
659
+ }
660
+ } else {
661
+ finalize_errored_ctx();
662
+ }
663
+ }
664
+ }
665
+
666
+ /**
667
+ * This function actually finalizes a ctx which has an error on it. If the
668
+ * ctx has pending operations remaining then this function returns immediately.
669
+ * Otherwise this will either reinitialize the connection or free the server
670
+ * object depending on the actual object state (i.e. if it was closed or
671
+ * simply errored).
672
+ */
673
+ void
674
+ Server::finalize_errored_ctx()
675
+ {
676
+ if (connctx->npending) {
677
+ return;
678
+ }
679
+
680
+ lcb_log(LOGARGS_T(DEBUG), LOGFMT "Finalizing ctx %p", LOGID_T(), (void*)connctx);
681
+
682
+ /* Always close the existing context. */
683
+ lcbio_ctx_close(connctx, close_cb, NULL);
684
+ connctx = NULL;
685
+
686
+ /**Marks any unflushed data inside this server as being already flushed. This
687
+ * should be done within error handling. If subsequent data is flushed on this
688
+ * pipeline to the same connection, the results are undefined. */
689
+
690
+ unsigned toflush;
691
+ nb_IOV iov;
692
+ while ((toflush = mcreq_flush_iov_fill(this, &iov, 1, NULL))) {
693
+ mcreq_flush_done(this, toflush, toflush);
694
+ }
695
+
696
+ if (state == Server::S_CLOSED) {
697
+ /* If the server is closed, time to free it */
698
+ delete this;
699
+ } else {
700
+ /* Otherwise, cycle the state back to CLEAN and reinit
701
+ * the connection */
702
+ state = Server::S_CLEAN;
703
+ connect();
704
+ }
705
+ }
706
+
707
+ /**
708
+ * This little function checks to see if the server struct is still valid, or
709
+ * whether it should just be cleaned once no pending I/O remainds.
710
+ *
711
+ * If this function returns false then the server is still valid; otherwise it
712
+ * is invalid and must not be used further.
713
+ */
714
+ bool
715
+ Server::check_closed()
716
+ {
717
+ if (state == Server::S_CLEAN) {
718
+ return false;
719
+ }
720
+ lcb_log(LOGARGS_T(INFO), LOGFMT "Got handler after close. Checking pending calls (pending=%u)", LOGID_T(), connctx->npending);
721
+ finalize_errored_ctx();
722
+ return 1;
723
+ }