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