Update cursor
[m6w6/libmemcached] / libmemcached / connect.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2010 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38
39 #include <libmemcached/common.h>
40
41 #include <cassert>
42 #include <ctime>
43 #include <sys/time.h>
44
45 #ifndef SOCK_CLOEXEC
46 #define SOCK_CLOEXEC 0
47 #endif
48
49 static memcached_return_t connect_poll(org::libmemcached::Instance* server)
50 {
51 struct pollfd fds[1];
52 fds[0].fd= server->fd;
53 fds[0].events= POLLOUT;
54
55 size_t loop_max= 5;
56
57 if (server->root->poll_timeout == 0)
58 {
59 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
60 }
61
62 while (--loop_max) // Should only loop on cases of ERESTART or EINTR
63 {
64 int number_of;
65 if ((number_of= poll(fds, 1, server->root->connect_timeout)) <= 0)
66 {
67 if (number_of == -1)
68 {
69 int local_errno= get_socket_errno(); // We cache in case closesocket() modifies errno
70 switch (local_errno)
71 {
72 #ifdef TARGET_OS_LINUX
73 case ERESTART:
74 #endif
75 case EINTR:
76 continue;
77
78 case EFAULT:
79 case ENOMEM:
80 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
81
82 case EINVAL:
83 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
84
85 default: // This should not happen
86 if (fds[0].revents & POLLERR)
87 {
88 int err;
89 socklen_t len= sizeof(err);
90 if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0)
91 {
92 if (err == 0)
93 {
94 // This should never happen, if it does? Punt.
95 continue;
96 }
97 local_errno= err;
98 }
99 }
100
101 assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
102 (void)closesocket(server->fd);
103 server->fd= INVALID_SOCKET;
104 server->state= MEMCACHED_SERVER_STATE_NEW;
105
106 return memcached_set_errno(*server, local_errno, MEMCACHED_AT);
107 }
108 }
109 assert(number_of == 0);
110
111 server->io_wait_count.timeouts++;
112 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
113 }
114
115 if (fds[0].revents & POLLERR or
116 fds[0].revents & POLLHUP or
117 fds[0].revents & POLLNVAL)
118 {
119 int err;
120 socklen_t len= sizeof (err);
121 if (getsockopt(fds[0].fd, SOL_SOCKET, SO_ERROR, &err, &len) == 0)
122 {
123 // We check the value to see what happened wth the socket.
124 if (err == 0)
125 {
126 return MEMCACHED_SUCCESS;
127 }
128 errno= err;
129 }
130
131 return memcached_set_errno(*server, err, MEMCACHED_AT);
132 }
133 assert(fds[0].revents & POLLIN or fds[0].revents & POLLOUT);
134
135 return MEMCACHED_SUCCESS;
136 }
137
138 // This should only be possible from ERESTART or EINTR;
139 return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
140 }
141
142 static memcached_return_t set_hostinfo(org::libmemcached::Instance* server)
143 {
144 assert(server->type != MEMCACHED_CONNECTION_UNIX_SOCKET);
145 if (server->address_info)
146 {
147 freeaddrinfo(server->address_info);
148 server->address_info= NULL;
149 server->address_info_next= NULL;
150 }
151
152 char str_port[NI_MAXSERV];
153 int length= snprintf(str_port, NI_MAXSERV, "%u", uint32_t(server->port()));
154 if (length >= NI_MAXSERV or length <= 0)
155 {
156 return MEMCACHED_FAILURE;
157 }
158
159 struct addrinfo hints;
160 memset(&hints, 0, sizeof(struct addrinfo));
161
162 #if 0
163 hints.ai_family= AF_INET;
164 #endif
165 if (memcached_is_udp(server->root))
166 {
167 hints.ai_protocol= IPPROTO_UDP;
168 hints.ai_socktype= SOCK_DGRAM;
169 }
170 else
171 {
172 hints.ai_socktype= SOCK_STREAM;
173 hints.ai_protocol= IPPROTO_TCP;
174 }
175
176 server->address_info= NULL;
177 int errcode;
178 switch(errcode= getaddrinfo(server->hostname, str_port, &hints, &server->address_info))
179 {
180 case 0:
181 break;
182
183 case EAI_AGAIN:
184 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
185
186 case EAI_SYSTEM:
187 return memcached_set_errno(*server, errno, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
188
189 case EAI_BADFLAGS:
190 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
191
192 case EAI_MEMORY:
193 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
194
195 default:
196 {
197 return memcached_set_error(*server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
198 }
199 }
200 server->address_info_next= server->address_info;
201 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
202
203 return MEMCACHED_SUCCESS;
204 }
205
206 static inline void set_socket_nonblocking(org::libmemcached::Instance* server)
207 {
208 #ifdef WIN32
209 u_long arg= 1;
210 if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR)
211 {
212 memcached_set_errno(*server, get_socket_errno(), NULL);
213 }
214 #else
215 int flags;
216
217 do
218 {
219 flags= fcntl(server->fd, F_GETFL, 0);
220 } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
221
222 if (flags == -1)
223 {
224 memcached_set_errno(*server, errno, NULL);
225 }
226 else if ((flags & O_NONBLOCK) == 0)
227 {
228 int rval;
229
230 do
231 {
232 rval= fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
233 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
234
235 if (rval == -1)
236 {
237 memcached_set_errno(*server, errno, NULL);
238 }
239 }
240 #endif
241 }
242
243 static void set_socket_options(org::libmemcached::Instance* server)
244 {
245 assert_msg(server->fd != INVALID_SOCKET, "invalid socket was passed to set_socket_options()");
246
247 if (memcached_is_udp(server->root))
248 {
249 return;
250 }
251
252 #ifdef HAVE_SNDTIMEO
253 if (server->root->snd_timeout)
254 {
255 struct timeval waittime;
256
257 waittime.tv_sec= 0;
258 waittime.tv_usec= server->root->snd_timeout;
259
260 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO,
261 &waittime, (socklen_t)sizeof(struct timeval));
262 assert(error == 0);
263 }
264 #endif
265
266 #ifdef HAVE_RCVTIMEO
267 if (server->root->rcv_timeout)
268 {
269 struct timeval waittime;
270
271 waittime.tv_sec= 0;
272 waittime.tv_usec= server->root->rcv_timeout;
273
274 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO,
275 &waittime, (socklen_t)sizeof(struct timeval));
276 assert(error == 0);
277 }
278 #endif
279
280
281 #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
282 {
283 int set= 1;
284 int error= setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
285
286 // This is not considered a fatal error
287 if (error == -1)
288 {
289 WATCHPOINT_ERRNO(get_socket_errno());
290 perror("setsockopt(SO_NOSIGPIPE)");
291 }
292 }
293 #endif
294
295 if (server->root->flags.no_block)
296 {
297 struct linger linger;
298
299 linger.l_onoff= 1;
300 linger.l_linger= 0; /* By default on close() just drop the socket */
301 int error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
302 &linger, (socklen_t)sizeof(struct linger));
303 assert(error == 0);
304 }
305
306 if (server->root->flags.tcp_nodelay)
307 {
308 int flag= 1;
309
310 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
311 &flag, (socklen_t)sizeof(int));
312 assert(error == 0);
313 }
314
315 if (server->root->flags.tcp_keepalive)
316 {
317 int flag= 1;
318
319 int error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
320 &flag, (socklen_t)sizeof(int));
321 assert(error == 0);
322 }
323
324 #ifdef TCP_KEEPIDLE
325 if (server->root->tcp_keepidle > 0)
326 {
327 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
328 &server->root->tcp_keepidle, (socklen_t)sizeof(int));
329 assert(error == 0);
330 }
331 #endif
332
333 if (server->root->send_size > 0)
334 {
335 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
336 &server->root->send_size, (socklen_t)sizeof(int));
337 assert(error == 0);
338 }
339
340 if (server->root->recv_size > 0)
341 {
342 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
343 &server->root->recv_size, (socklen_t)sizeof(int));
344 assert(error == 0);
345 }
346
347
348 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
349 set_socket_nonblocking(server);
350 }
351
352 static memcached_return_t unix_socket_connect(org::libmemcached::Instance* server)
353 {
354 #ifndef WIN32
355 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
356
357 if ((server->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
358 {
359 memcached_set_errno(*server, errno, NULL);
360 return MEMCACHED_CONNECTION_FAILURE;
361 }
362
363 struct sockaddr_un servAddr;
364
365 memset(&servAddr, 0, sizeof (struct sockaddr_un));
366 servAddr.sun_family= AF_UNIX;
367 strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
368
369 do {
370 if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
371 {
372 switch (errno)
373 {
374 case EINPROGRESS:
375 case EALREADY:
376 case EINTR:
377 continue;
378
379 case EISCONN: /* We were spinning waiting on connect */
380 {
381 assert(0); // Programmer error
382 break;
383 }
384
385 default:
386 WATCHPOINT_ERRNO(errno);
387 memcached_set_errno(*server, errno, MEMCACHED_AT);
388 return MEMCACHED_CONNECTION_FAILURE;
389 }
390 }
391 } while (0);
392 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
393
394 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
395
396 return MEMCACHED_SUCCESS;
397 #else
398 (void)server;
399 return MEMCACHED_NOT_SUPPORTED;
400 #endif
401 }
402
403 static memcached_return_t network_connect(org::libmemcached::Instance* server)
404 {
405 bool timeout_error_occured= false;
406
407 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
408 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
409
410 /*
411 We want to check both of these because if address_info_next has been fully tried, we want to do a new lookup to make sure we have picked up on any new DNS information.
412 */
413 if (server->address_info == NULL or server->address_info_next == NULL)
414 {
415 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
416 server->address_info_next= NULL;
417 memcached_return_t rc;
418 uint32_t counter= 5;
419 while (--counter)
420 {
421 if ((rc= set_hostinfo(server)) != MEMCACHED_TIMEOUT)
422 {
423 break;
424 }
425
426 #ifndef WIN32
427 struct timespec dream, rem;
428
429 dream.tv_nsec= 1000;
430 dream.tv_sec= 0;
431
432 nanosleep(&dream, &rem);
433 #endif
434 }
435
436 if (memcached_failed(rc))
437 {
438 return rc;
439 }
440 }
441
442 if (server->address_info_next == NULL)
443 {
444 server->address_info_next= server->address_info;
445 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
446 }
447
448 /* Create the socket */
449 while (server->address_info_next and server->fd == INVALID_SOCKET)
450 {
451 /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
452 if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
453 {
454 server->address_info_next= server->address_info_next->ai_next;
455 continue;
456 }
457
458 int type= server->address_info_next->ai_socktype;
459 if (HAVE_SOCK_CLOEXEC)
460 {
461 type|= SOCK_CLOEXEC;
462 }
463
464 if ((server->fd= socket(server->address_info_next->ai_family,
465 type,
466 server->address_info_next->ai_protocol)) < 0)
467 {
468 return memcached_set_errno(*server, get_socket_errno(), NULL);
469 }
470
471 if (HAVE_SOCK_CLOEXEC == 0)
472 {
473 #ifdef FD_CLOEXEC
474 int rval;
475 do
476 {
477 rval= fcntl (server->fd, F_SETFD, FD_CLOEXEC);
478 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
479 #endif
480 }
481
482 set_socket_options(server);
483
484 /* connect to server */
485 if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
486 {
487 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
488 return MEMCACHED_SUCCESS;
489 }
490
491 /* An error occurred */
492 switch (get_socket_errno())
493 {
494 case ETIMEDOUT:
495 timeout_error_occured= true;
496 break;
497
498 case EAGAIN:
499 #if EWOULDBLOCK != EAGAIN
500 case EWOULDBLOCK:
501 #endif
502 case EINPROGRESS: // nonblocking mode - first return
503 case EALREADY: // nonblocking mode - subsequent returns
504 {
505 server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
506 memcached_return_t rc= connect_poll(server);
507
508 if (memcached_success(rc))
509 {
510 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
511 return MEMCACHED_SUCCESS;
512 }
513
514 // A timeout here is treated as an error, we will not retry
515 if (rc == MEMCACHED_TIMEOUT)
516 {
517 timeout_error_occured= true;
518 }
519 }
520 break;
521
522 case EISCONN: // we are connected :-)
523 WATCHPOINT_ASSERT(0); // This is a programmer's error
524 break;
525
526 case EINTR: // Special case, we retry ai_addr
527 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
528 (void)closesocket(server->fd);
529 server->fd= INVALID_SOCKET;
530 continue;
531
532 default:
533 break;
534 }
535
536 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
537 (void)closesocket(server->fd);
538 server->fd= INVALID_SOCKET;
539 server->address_info_next= server->address_info_next->ai_next;
540 }
541
542 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
543
544 if (timeout_error_occured)
545 {
546 if (server->fd != INVALID_SOCKET)
547 {
548 (void)closesocket(server->fd);
549 server->fd= INVALID_SOCKET;
550 }
551 }
552
553 WATCHPOINT_STRING("Never got a good file descriptor");
554
555 if (memcached_has_current_error(*server))
556 {
557 return memcached_instance_error_return(server);
558 }
559
560 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
561 {
562 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
563 }
564
565 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
566 }
567
568
569 /*
570 backoff_handling()
571
572 Based on time/failure count fail the connect without trying. This prevents waiting in a state where
573 we get caught spending cycles just waiting.
574 */
575 static memcached_return_t backoff_handling(org::libmemcached::Instance* server, bool& in_timeout)
576 {
577 struct timeval curr_time;
578 bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
579
580 /*
581 If we hit server_failure_limit then something is completely wrong about the server.
582
583 1) If autoeject is enabled we do that.
584 2) If not? We go into timeout again, there is much else to do :(
585 */
586 if (server->server_failure_counter >= server->root->server_failure_limit)
587 {
588 /*
589 We just auto_eject if we hit this point
590 */
591 if (_is_auto_eject_host(server->root))
592 {
593 set_last_disconnected_host(server);
594
595 // Retry dead servers if requested
596 if (_gettime_success and server->root->dead_timeout > 0)
597 {
598 server->next_retry= curr_time.tv_sec +server->root->dead_timeout;
599
600 // We only retry dead servers once before assuming failure again
601 server->server_failure_counter= server->root->server_failure_limit -1;
602 }
603
604 memcached_return_t rc;
605 if (memcached_failed(rc= run_distribution((memcached_st *)server->root)))
606 {
607 return memcached_set_error(*server, rc, MEMCACHED_AT, memcached_literal_param("Backoff handling failed during run_distribution"));
608 }
609
610 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
611 }
612
613 server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
614
615 // Sanity check/setting
616 if (server->next_retry == 0)
617 {
618 server->next_retry= 1;
619 }
620 }
621
622 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
623 {
624 /*
625 If next_retry is less then our current time, then we reset and try everything again.
626 */
627 if (_gettime_success and server->next_retry < curr_time.tv_sec)
628 {
629 server->state= MEMCACHED_SERVER_STATE_NEW;
630 }
631 else
632 {
633 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
634 }
635
636 in_timeout= true;
637 }
638
639 return MEMCACHED_SUCCESS;
640 }
641
642 static memcached_return_t _memcached_connect(org::libmemcached::Instance* server, const bool set_last_disconnected)
643 {
644 if (server->fd != INVALID_SOCKET)
645 {
646 return MEMCACHED_SUCCESS;
647 }
648
649 LIBMEMCACHED_MEMCACHED_CONNECT_START();
650
651 bool in_timeout= false;
652 memcached_return_t rc;
653 if (memcached_failed(rc= backoff_handling(server, in_timeout)))
654 {
655 set_last_disconnected_host(server);
656 return rc;
657 }
658
659 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
660 {
661 return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
662 }
663
664 if (server->hostname[0] == '/')
665 {
666 server->type= MEMCACHED_CONNECTION_UNIX_SOCKET;
667 }
668
669 /* We need to clean up the multi startup piece */
670 switch (server->type)
671 {
672 case MEMCACHED_CONNECTION_UDP:
673 case MEMCACHED_CONNECTION_TCP:
674 rc= network_connect(server);
675
676 if (LIBMEMCACHED_WITH_SASL_SUPPORT)
677 {
678 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
679 {
680 rc= memcached_sasl_authenticate_connection(server);
681 if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
682 {
683 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
684 (void)closesocket(server->fd);
685 server->fd= INVALID_SOCKET;
686 }
687 }
688 }
689 break;
690
691 case MEMCACHED_CONNECTION_UNIX_SOCKET:
692 rc= unix_socket_connect(server);
693 break;
694 }
695
696 if (memcached_success(rc))
697 {
698 server->mark_server_as_clean();
699 return rc;
700 }
701 else if (set_last_disconnected)
702 {
703 set_last_disconnected_host(server);
704 if (memcached_has_current_error(*server))
705 {
706 memcached_mark_server_for_timeout(server);
707 assert(memcached_failed(memcached_instance_error_return(server)));
708 }
709 else
710 {
711 memcached_set_error(*server, rc, MEMCACHED_AT);
712 memcached_mark_server_for_timeout(server);
713 }
714
715 LIBMEMCACHED_MEMCACHED_CONNECT_END();
716
717 if (in_timeout)
718 {
719 char buffer[1024];
720 int snprintf_length= snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname, int(server->port()));
721 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT, buffer, snprintf_length);
722 }
723 }
724
725 return rc;
726 }
727
728 memcached_return_t memcached_connect_try(org::libmemcached::Instance* server)
729 {
730 return _memcached_connect(server, false);
731 }
732
733 memcached_return_t memcached_connect(org::libmemcached::Instance* server)
734 {
735 return _memcached_connect(server, true);
736 }