3 * Author: Mingqiang Zhuang
5 * Created on February 10, 2009
7 * (c) Copyright 2009, Schooner Information Technology, Inc.
8 * http://www.schoonerinfotech.com/
19 #include <netinet/tcp.h>
20 #include <arpa/inet.h>
21 #include "ms_setting.h"
22 #include "ms_thread.h"
23 #include "ms_atomic.h"
25 /* for network write */
26 #define TRANSMIT_COMPLETE 0
27 #define TRANSMIT_INCOMPLETE 1
28 #define TRANSMIT_SOFT_ERROR 2
29 #define TRANSMIT_HARD_ERROR 3
31 /* for generating key */
32 #define KEY_PREFIX_BASE 0x1010101010101010 /* not include ' ' '\r' '\n' '\0' */
33 #define KEY_PREFIX_MASK 0x1010101010101010
35 /* For parse the value length return by server */
37 #define VALUELEN_TOKEN 3
39 /* global increasing counter, to ensure the key prefix unique */
40 static uint64_t key_prefix_seq
= KEY_PREFIX_BASE
;
42 /* global increasing counter, generating request id for UDP */
43 static volatile uint32_t udp_request_id
= 0;
45 extern __thread ms_thread_t ms_thread
;
47 /* generate upd request id */
48 static uint32_t ms_get_udp_request_id(void);
51 /* connect initialize */
52 static void ms_task_init(ms_conn_t
*c
);
53 static int ms_conn_udp_init(ms_conn_t
*c
, const bool is_udp
);
54 static int ms_conn_sock_init(ms_conn_t
*c
);
55 static int ms_conn_event_init(ms_conn_t
*c
);
56 static int ms_conn_init(ms_conn_t
*c
,
58 const int read_buffer_size
,
60 static void ms_warmup_num_init(ms_conn_t
*c
);
61 static int ms_item_win_init(ms_conn_t
*c
);
64 /* connection close */
65 void ms_conn_free(ms_conn_t
*c
);
66 static void ms_conn_close(ms_conn_t
*c
);
69 /* create network connection */
70 static int ms_new_socket(struct addrinfo
*ai
);
71 static void ms_maximize_sndbuf(const int sfd
);
72 static int ms_network_connect(ms_conn_t
*c
,
77 static int ms_reconn(ms_conn_t
*c
);
81 static int ms_tokenize_command(char *command
,
83 const int max_tokens
);
84 static int ms_ascii_process_line(ms_conn_t
*c
, char *command
);
85 static int ms_try_read_line(ms_conn_t
*c
);
86 static int ms_sort_udp_packet(ms_conn_t
*c
, char *buf
, int rbytes
);
87 static int ms_udp_read(ms_conn_t
*c
, char *buf
, int len
);
88 static int ms_try_read_network(ms_conn_t
*c
);
89 static void ms_verify_value(ms_conn_t
*c
,
90 ms_mlget_task_item_t
*mlget_item
,
93 static void ms_ascii_complete_nread(ms_conn_t
*c
);
94 static void ms_bin_complete_nread(ms_conn_t
*c
);
95 static void ms_complete_nread(ms_conn_t
*c
);
99 static int ms_add_msghdr(ms_conn_t
*c
);
100 static int ms_ensure_iov_space(ms_conn_t
*c
);
101 static int ms_add_iov(ms_conn_t
*c
, const void *buf
, int len
);
102 static int ms_build_udp_headers(ms_conn_t
*c
);
103 static int ms_transmit(ms_conn_t
*c
);
106 /* status adjustment */
107 static void ms_conn_shrink(ms_conn_t
*c
);
108 static void ms_conn_set_state(ms_conn_t
*c
, int state
);
109 static bool ms_update_event(ms_conn_t
*c
, const int new_flags
);
110 static int ms_get_rep_sock_index(ms_conn_t
*c
, int cmd
);
111 static int ms_get_next_sock_index(ms_conn_t
*c
);
112 static int ms_update_conn_sock_event(ms_conn_t
*c
);
113 static bool ms_need_yield(ms_conn_t
*c
);
114 static void ms_update_start_time(ms_conn_t
*c
);
118 static void ms_drive_machine(ms_conn_t
*c
);
119 void ms_event_handler(const int fd
, const short which
, void *arg
);
123 static int ms_build_ascii_write_buf_set(ms_conn_t
*c
, ms_task_item_t
*item
);
124 static int ms_build_ascii_write_buf_get(ms_conn_t
*c
, ms_task_item_t
*item
);
125 static int ms_build_ascii_write_buf_mlget(ms_conn_t
*c
);
128 /* binary protocol */
129 static int ms_bin_process_response(ms_conn_t
*c
);
130 static void ms_add_bin_header(ms_conn_t
*c
,
135 static void ms_add_key_to_iov(ms_conn_t
*c
, ms_task_item_t
*item
);
136 static int ms_build_bin_write_buf_set(ms_conn_t
*c
, ms_task_item_t
*item
);
137 static int ms_build_bin_write_buf_get(ms_conn_t
*c
, ms_task_item_t
*item
);
138 static int ms_build_bin_write_buf_mlget(ms_conn_t
*c
);
142 * each key has two parts, prefix and suffix. The suffix is a
143 * string random get form the character table. The prefix is a
144 * uint64_t variable. And the prefix must be unique. we use the
145 * prefix to identify a key. And the prefix can't include
146 * character ' ' '\r' '\n' '\0'.
150 uint64_t ms_get_key_prefix(void)
154 pthread_mutex_lock(&ms_global
.seq_mutex
);
155 key_prefix_seq
|= KEY_PREFIX_MASK
;
156 key_prefix
= key_prefix_seq
;
158 pthread_mutex_unlock(&ms_global
.seq_mutex
);
161 } /* ms_get_key_prefix */
165 * get an unique udp request id
167 * @return an unique UDP request id
169 static uint32_t ms_get_udp_request_id(void)
171 return atomic_add_32_nv(&udp_request_id
, 1);
176 * initialize current task structure
178 * @param c, pointer of the concurrency
180 static void ms_task_init(ms_conn_t
*c
)
182 c
->curr_task
.cmd
= CMD_NULL
;
183 c
->curr_task
.item
= 0;
184 c
->curr_task
.verify
= false;
185 c
->curr_task
.finish_verify
= true;
186 c
->curr_task
.get_miss
= true;
188 c
->curr_task
.get_opt
= 0;
189 c
->curr_task
.set_opt
= 0;
190 c
->curr_task
.cycle_undo_get
= 0;
191 c
->curr_task
.cycle_undo_set
= 0;
192 c
->curr_task
.verified_get
= 0;
193 c
->curr_task
.overwrite_set
= 0;
198 * initialize udp for the connection structure
200 * @param c, pointer of the concurrency
201 * @param is_udp, whether it's udp
203 * @return int, if success, return 0, else return -1
205 static int ms_conn_udp_init(ms_conn_t
*c
, const bool is_udp
)
211 c
->rudpsize
= UDP_DATA_BUFFER_SIZE
;
222 if (c
->udp
|| (! c
->udp
&& ms_setting
.facebook_test
))
224 c
->rudpbuf
= (char *)malloc((size_t)c
->rudpsize
);
225 c
->udppkt
= (ms_udppkt_t
*)malloc(MAX_UDP_PACKET
* sizeof(ms_udppkt_t
));
227 if ((c
->rudpbuf
== NULL
) || (c
->udppkt
== NULL
))
229 if (c
->rudpbuf
!= NULL
)
231 if (c
->udppkt
!= NULL
)
233 fprintf(stderr
, "malloc()\n");
236 memset(c
->udppkt
, 0, MAX_UDP_PACKET
* sizeof(ms_udppkt_t
));
240 } /* ms_conn_udp_init */
244 * initialize the connection structure
246 * @param c, pointer of the concurrency
247 * @param init_state, (conn_read, conn_write, conn_closing)
248 * @param read_buffer_size
249 * @param is_udp, whether it's udp
251 * @return int, if success, return 0, else return -1
253 static int ms_conn_init(ms_conn_t
*c
,
254 const int init_state
,
255 const int read_buffer_size
,
264 c
->rsize
= read_buffer_size
;
265 c
->wsize
= WRITE_BUFFER_SIZE
;
266 c
->iovsize
= IOV_LIST_INITIAL
;
267 c
->msgsize
= MSG_LIST_INITIAL
;
269 /* for replication, each connection need connect all the server */
270 if (ms_setting
.rep_write_srv
> 0)
272 c
->total_sfds
= ms_setting
.srv_cnt
;
276 c
->total_sfds
= ms_setting
.sock_per_conn
;
280 c
->rbuf
= (char *)malloc((size_t)c
->rsize
);
281 c
->wbuf
= (char *)malloc((size_t)c
->wsize
);
282 c
->iov
= (struct iovec
*)malloc(sizeof(struct iovec
) * (size_t)c
->iovsize
);
283 c
->msglist
= (struct msghdr
*)malloc(
284 sizeof(struct msghdr
) * (size_t)c
->msgsize
);
285 if (ms_setting
.mult_key_num
> 1)
287 c
->mlget_task
.mlget_item
= (ms_mlget_task_item_t
*)
289 sizeof(ms_mlget_task_item_t
) * (size_t)ms_setting
.mult_key_num
);
291 c
->tcpsfd
= (int *)malloc((size_t)c
->total_sfds
* sizeof(int));
293 if ((c
->rbuf
== NULL
) || (c
->wbuf
== NULL
) || (c
->iov
== NULL
)
294 || (c
->msglist
== NULL
) || (c
->tcpsfd
== NULL
)
295 || ((ms_setting
.mult_key_num
> 1)
296 && (c
->mlget_task
.mlget_item
== NULL
)))
304 if (c
->msglist
!= NULL
)
306 if (c
->mlget_task
.mlget_item
!= NULL
)
307 free(c
->mlget_task
.mlget_item
);
308 if (c
->tcpsfd
!= NULL
)
310 fprintf(stderr
, "malloc()\n");
314 c
->state
= init_state
;
322 c
->cur_idx
= c
->total_sfds
; /* default index is a invalid value */
326 c
->change_sfd
= false;
328 c
->precmd
.cmd
= c
->currcmd
.cmd
= CMD_NULL
;
329 c
->precmd
.isfinish
= true; /* default the previous command finished */
330 c
->currcmd
.isfinish
= false;
331 c
->precmd
.retstat
= c
->currcmd
.retstat
= MCD_FAILURE
;
332 c
->precmd
.key_prefix
= c
->currcmd
.key_prefix
= 0;
334 c
->mlget_task
.mlget_num
= 0;
335 c
->mlget_task
.value_index
= -1; /* default invalid value */
337 if (ms_setting
.binary_prot
)
339 c
->protocol
= binary_prot
;
343 c
->protocol
= ascii_udp_prot
;
347 c
->protocol
= ascii_prot
;
351 if (ms_conn_udp_init(c
, is_udp
) != 0)
356 /* initialize task */
359 if (! (ms_setting
.facebook_test
&& is_udp
))
361 atomic_add_32(&ms_stats
.active_conns
, 1);
369 * when doing 100% get operation, it could preset some objects
370 * to warmup the server. this function is used to initialize the
371 * number of the objects to preset.
373 * @param c, pointer of the concurrency
375 static void ms_warmup_num_init(ms_conn_t
*c
)
377 /* no set operation, preset all the items in the window */
378 if (ms_setting
.cmd_distr
[CMD_SET
].cmd_prop
< PROP_ERROR
)
380 c
->warmup_num
= c
->win_size
;
381 c
->remain_warmup_num
= c
->warmup_num
;
386 c
->remain_warmup_num
= c
->warmup_num
;
388 } /* ms_warmup_num_init */
392 * each connection has an item window, this function initialize
393 * the window. The window is used to generate task.
395 * @param c, pointer of the concurrency
397 * @return int, if success, return 0, else return -1
399 static int ms_item_win_init(ms_conn_t
*c
)
403 c
->win_size
= (int)ms_setting
.win_size
;
405 c
->exec_num
= ms_thread
.thread_ctx
->exec_num_perconn
;
406 c
->remain_exec_num
= c
->exec_num
;
408 c
->item_win
= (ms_task_item_t
*)malloc(
409 sizeof(ms_task_item_t
) * (size_t)c
->win_size
);
410 if (c
->item_win
== NULL
)
412 fprintf(stderr
, "Can't allocate task item array for conn.\n");
415 memset(c
->item_win
, 0, sizeof(ms_task_item_t
) * (size_t)c
->win_size
);
417 for (int i
= 0; i
< c
->win_size
; i
++)
419 c
->item_win
[i
].key_size
= (int)ms_setting
.distr
[i
].key_size
;
420 c
->item_win
[i
].key_prefix
= ms_get_key_prefix();
421 c
->item_win
[i
].key_suffix_offset
= ms_setting
.distr
[i
].key_offset
;
422 c
->item_win
[i
].value_size
= (int)ms_setting
.distr
[i
].value_size
;
423 c
->item_win
[i
].value_offset
= INVALID_OFFSET
; /* default in invalid offset */
424 c
->item_win
[i
].client_time
= 0;
426 /* set expire time base on the proportion */
427 if (exp_cnt
< ms_setting
.exp_ver_per
* i
)
429 c
->item_win
[i
].exp_time
= FIXED_EXPIRE_TIME
;
434 c
->item_win
[i
].exp_time
= 0;
438 ms_warmup_num_init(c
);
441 } /* ms_item_win_init */
445 * each connection structure can include one or more sock
446 * handlers. this function create these socks and connect the
449 * @param c, pointer of the concurrency
451 * @return int, if success, return 0, else return -1
453 static int ms_conn_sock_init(ms_conn_t
*c
)
460 assert(c
->tcpsfd
!= NULL
);
462 for (i
= 0; i
< c
->total_sfds
; i
++)
465 if (ms_setting
.rep_write_srv
> 0)
467 /* for replication, each connection need connect all the server */
472 /* all the connections in a thread connects the same server */
473 srv_idx
= ms_thread
.thread_ctx
->srv_idx
;
476 if (ms_network_connect(c
, ms_setting
.servers
[srv_idx
].srv_host_name
,
477 ms_setting
.servers
[srv_idx
].srv_port
,
478 ms_setting
.udp
, &ret_sfd
) != 0)
488 if (! ms_setting
.udp
)
490 c
->tcpsfd
[i
]= ret_sfd
;
496 /* initialize udp sock handler if necessary */
497 if (ms_setting
.facebook_test
)
500 if (ms_network_connect(c
, ms_setting
.servers
[srv_idx
].srv_host_name
,
501 ms_setting
.servers
[srv_idx
].srv_port
,
502 true, &ret_sfd
) != 0)
512 if ((i
!= c
->total_sfds
) || (ms_setting
.facebook_test
&& (c
->udpsfd
== 0)))
520 for (int j
= 0; j
< i
; j
++)
535 } /* ms_conn_sock_init */
539 * each connection is managed by libevent, this function
540 * initialize the event of the connection structure.
542 * @param c, pointer of the concurrency
544 * @return int, if success, return 0, else return -1
546 static int ms_conn_event_init(ms_conn_t
*c
)
548 /* default event timeout 10 seconds */
551 .tv_sec
= EVENT_TIMEOUT
, .tv_usec
= 0
553 short event_flags
= EV_WRITE
| EV_PERSIST
;
555 event_set(&c
->event
, c
->sfd
, event_flags
, ms_event_handler
, (void *)c
);
556 event_base_set(ms_thread
.base
, &c
->event
);
557 c
->ev_flags
= event_flags
;
559 if (c
->total_sfds
== 1)
561 if (event_add(&c
->event
, NULL
) == -1)
568 if (event_add(&c
->event
, &t
) == -1)
575 } /* ms_conn_event_init */
579 * setup a connection, each connection structure of each
580 * thread must call this function to initialize.
582 * @param c, pointer of the concurrency
584 * @return int, if success, return 0, else return -1
586 int ms_setup_conn(ms_conn_t
*c
)
588 if (ms_item_win_init(c
) != 0)
593 if (ms_conn_init(c
, conn_write
, DATA_BUFFER_SIZE
, ms_setting
.udp
) != 0)
598 if (ms_conn_sock_init(c
) != 0)
603 if (ms_conn_event_init(c
) != 0)
609 } /* ms_setup_conn */
613 * Frees a connection.
615 * @param c, pointer of the concurrency
617 void ms_conn_free(ms_conn_t
*c
)
621 if (c
->hdrbuf
!= NULL
)
623 if (c
->msglist
!= NULL
)
631 if (c
->mlget_task
.mlget_item
!= NULL
)
632 free(c
->mlget_task
.mlget_item
);
633 if (c
->rudpbuf
!= NULL
)
635 if (c
->udppkt
!= NULL
)
637 if (c
->item_win
!= NULL
)
639 if (c
->tcpsfd
!= NULL
)
642 if (--ms_thread
.nactive_conn
== 0)
644 free(ms_thread
.conn
);
653 * @param c, pointer of the concurrency
655 static void ms_conn_close(ms_conn_t
*c
)
659 /* delete the event, the socket and the connection */
660 event_del(&c
->event
);
662 for (int i
= 0; i
< c
->total_sfds
; i
++)
664 if (c
->tcpsfd
[i
] > 0)
671 if (ms_setting
.facebook_test
)
676 atomic_dec_32(&ms_stats
.active_conns
);
680 if (ms_setting
.run_time
== 0)
682 pthread_mutex_lock(&ms_global
.run_lock
.lock
);
683 ms_global
.run_lock
.count
++;
684 pthread_cond_signal(&ms_global
.run_lock
.cond
);
685 pthread_mutex_unlock(&ms_global
.run_lock
.lock
);
688 if (ms_thread
.nactive_conn
== 0)
692 } /* ms_conn_close */
698 * @param ai, server address information
700 * @return int, if success, return 0, else return -1
702 static int ms_new_socket(struct addrinfo
*ai
)
706 if ((sfd
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
)) == -1)
708 fprintf(stderr
, "socket() error: %s.\n", strerror(errno
));
713 } /* ms_new_socket */
717 * Sets a socket's send buffer size to the maximum allowed by the system.
719 * @param sfd, file descriptor of socket
721 static void ms_maximize_sndbuf(const int sfd
)
723 socklen_t intsize
= sizeof(int);
724 unsigned int last_good
= 0;
725 unsigned int min
, max
, avg
;
726 unsigned int old_size
;
728 /* Start with the default size. */
729 if (getsockopt(sfd
, SOL_SOCKET
, SO_SNDBUF
, &old_size
, &intsize
) != 0)
731 fprintf(stderr
, "getsockopt(SO_SNDBUF)\n");
735 /* Binary-search for the real maximum. */
737 max
= MAX_SENDBUF_SIZE
;
741 avg
= ((unsigned int)(min
+ max
)) / 2;
742 if (setsockopt(sfd
, SOL_SOCKET
, SO_SNDBUF
, (void *)&avg
, intsize
) == 0)
752 } /* ms_maximize_sndbuf */
756 * socket connects the server
758 * @param c, pointer of the concurrency
759 * @param srv_host_name, the host name of the server
760 * @param srv_port, port of server
761 * @param is_udp, whether it's udp
762 * @param ret_sfd, the connected socket file descriptor
764 * @return int, if success, return 0, else return -1
766 static int ms_network_connect(ms_conn_t
*c
,
778 struct addrinfo
*next
;
779 struct addrinfo hints
;
780 char port_buf
[NI_MAXSERV
];
787 * the memset call clears nonstandard fields in some impementations
788 * that otherwise mess things up.
790 memset(&hints
, 0, sizeof(hints
));
791 hints
.ai_flags
= AI_PASSIVE
| AI_ADDRCONFIG
;
794 hints
.ai_protocol
= IPPROTO_UDP
;
795 hints
.ai_socktype
= SOCK_DGRAM
;
796 hints
.ai_family
= AF_INET
; /* This left here because of issues with OSX 10.5 */
800 hints
.ai_family
= AF_UNSPEC
;
801 hints
.ai_protocol
= IPPROTO_TCP
;
802 hints
.ai_socktype
= SOCK_STREAM
;
805 snprintf(port_buf
, NI_MAXSERV
, "%d", srv_port
);
806 error
= getaddrinfo(srv_host_name
, port_buf
, &hints
, &ai
);
809 if (error
!= EAI_SYSTEM
)
810 fprintf(stderr
, "getaddrinfo(): %s.\n", gai_strerror(error
));
812 perror("getaddrinfo()\n");
817 for (next
= ai
; next
; next
= next
->ai_next
)
819 if ((sfd
= ms_new_socket(next
)) == -1)
825 setsockopt(sfd
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&flags
, sizeof(flags
));
828 ms_maximize_sndbuf(sfd
);
832 setsockopt(sfd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&flags
,
834 setsockopt(sfd
, SOL_SOCKET
, SO_LINGER
, (void *)&ling
, sizeof(ling
));
835 setsockopt(sfd
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&flags
,
841 c
->srv_recv_addr_size
= sizeof(struct sockaddr
);
842 memcpy(&c
->srv_recv_addr
, next
->ai_addr
, c
->srv_recv_addr_size
);
846 if (connect(sfd
, next
->ai_addr
, next
->ai_addrlen
) == -1)
854 if (((flags
= fcntl(sfd
, F_GETFL
, 0)) < 0)
855 || (fcntl(sfd
, F_SETFL
, flags
| O_NONBLOCK
) < 0))
857 fprintf(stderr
, "setting O_NONBLOCK\n");
873 /* Return zero if we detected no errors in starting up connections */
875 } /* ms_network_connect */
879 * reconnect a disconnected sock
881 * @param c, pointer of the concurrency
883 * @return int, if success, return 0, else return -1
885 static int ms_reconn(ms_conn_t
*c
)
888 int32_t srv_conn_cnt
= 0;
890 if (ms_setting
.rep_write_srv
> 0)
893 srv_conn_cnt
= ms_setting
.nconns
;
897 srv_idx
= ms_thread
.thread_ctx
->srv_idx
;
898 srv_conn_cnt
= ms_setting
.nconns
/ ms_setting
.srv_cnt
;
901 /* close the old socket handler */
903 c
->tcpsfd
[c
->cur_idx
]= 0;
905 if (atomic_add_32_nv((volatile uint32_t *)&ms_setting
.servers
[srv_idx
].disconn_cnt
, 1)
908 gettimeofday(&ms_setting
.servers
[srv_idx
].disconn_time
, NULL
);
909 fprintf(stderr
, "Server %s:%d disconnect\n",
910 ms_setting
.servers
[srv_idx
].srv_host_name
,
911 ms_setting
.servers
[srv_idx
].srv_port
);
914 if (ms_setting
.rep_write_srv
> 0)
917 for (i
= 0; i
< c
->total_sfds
; i
++)
919 if (c
->tcpsfd
[i
] != 0)
925 /* all socks disconnect */
926 if (i
== c
->total_sfds
)
935 /* reconnect success, break the loop */
936 if (ms_network_connect(c
, ms_setting
.servers
[srv_idx
].srv_host_name
,
937 ms_setting
.servers
[srv_idx
].srv_port
,
938 ms_setting
.udp
, &c
->sfd
) == 0)
940 c
->tcpsfd
[c
->cur_idx
]= c
->sfd
;
941 if (atomic_add_32_nv((volatile uint32_t *)(&ms_setting
.servers
[srv_idx
].reconn_cnt
), 1)
944 gettimeofday(&ms_setting
.servers
[srv_idx
].reconn_time
, NULL
);
946 (int)(ms_setting
.servers
[srv_idx
].reconn_time
.tv_sec
947 - ms_setting
.servers
[srv_idx
].disconn_time
949 fprintf(stderr
, "Server %s:%d reconnect after %ds\n",
950 ms_setting
.servers
[srv_idx
].srv_host_name
,
951 ms_setting
.servers
[srv_idx
].srv_port
, reconn_time
);
956 if (c
->total_sfds
== 1)
958 /* wait a second and reconnect */
962 while (c
->total_sfds
== 1);
965 if ((c
->total_sfds
> 1) && (c
->tcpsfd
[c
->cur_idx
] == 0))
976 * reconnect several disconnected socks in the connection
977 * structure, the ever-1-second timer of the thread will check
978 * whether some socks in the connections disconnect. if
979 * disconnect, reconnect the sock.
981 * @param c, pointer of the concurrency
983 * @return int, if success, return 0, else return -1
985 int ms_reconn_socks(ms_conn_t
*c
)
990 struct timeval cur_time
;
994 if ((c
->total_sfds
== 1) || (c
->total_sfds
== c
->alive_sfds
))
999 for (int i
= 0; i
< c
->total_sfds
; i
++)
1001 if (c
->tcpsfd
[i
] == 0)
1003 gettimeofday(&cur_time
, NULL
);
1006 * For failover test of replication, reconnect the socks after
1007 * it disconnects more than 5 seconds, Otherwise memslap will
1008 * block at connect() function and the work threads can't work
1012 - ms_setting
.servers
[srv_idx
].disconn_time
.tv_sec
< 5)
1017 if (ms_setting
.rep_write_srv
> 0)
1020 srv_conn_cnt
= ms_setting
.nconns
;
1024 srv_idx
= ms_thread
.thread_ctx
->srv_idx
;
1025 srv_conn_cnt
= ms_setting
.nconns
/ ms_setting
.srv_cnt
;
1028 if (ms_network_connect(c
, ms_setting
.servers
[srv_idx
].srv_host_name
,
1029 ms_setting
.servers
[srv_idx
].srv_port
,
1030 ms_setting
.udp
, &ret_sfd
) == 0)
1032 c
->tcpsfd
[i
]= ret_sfd
;
1035 if (atomic_add_32_nv((volatile uint32_t *)(&ms_setting
.servers
[srv_idx
].reconn_cnt
), 1)
1036 % srv_conn_cnt
== 0)
1038 gettimeofday(&ms_setting
.servers
[srv_idx
].reconn_time
, NULL
);
1040 (int)(ms_setting
.servers
[srv_idx
].reconn_time
.tv_sec
1041 - ms_setting
.servers
[srv_idx
].disconn_time
1043 fprintf(stderr
, "Server %s:%d reconnect after %ds\n",
1044 ms_setting
.servers
[srv_idx
].srv_host_name
,
1045 ms_setting
.servers
[srv_idx
].srv_port
, reconn_time
);
1052 } /* ms_reconn_socks */
1056 * Tokenize the command string by replacing whitespace with '\0' and update
1057 * the token array tokens with pointer to start of each token and length.
1058 * Returns total number of tokens. The last valid token is the terminal
1059 * token (value points to the first unprocessed character of the string and
1064 * while(ms_tokenize_command(command, ncommand, tokens, max_tokens) > 0) {
1065 * for(int ix = 0; tokens[ix].length != 0; ix++) {
1068 * ncommand = tokens[ix].value - command;
1069 * command = tokens[ix].value;
1072 * @param command, the command string to token
1073 * @param tokens, array to store tokens
1074 * @param max_tokens, maximum tokens number
1076 * @return int, the number of tokens
1078 static int ms_tokenize_command(char *command
,
1080 const int max_tokens
)
1085 assert(command
!= NULL
&& tokens
!= NULL
&& max_tokens
> 1);
1087 for (s
= e
= command
; ntokens
< max_tokens
- 1; ++e
)
1093 tokens
[ntokens
].value
= s
;
1094 tokens
[ntokens
].length
= (size_t)(e
- s
);
1100 else if (*e
== '\0')
1104 tokens
[ntokens
].value
= s
;
1105 tokens
[ntokens
].length
= (size_t)(e
- s
);
1109 break; /* string end */
1114 } /* ms_tokenize_command */
1118 * parse the response of server.
1120 * @param c, pointer of the concurrency
1121 * @param command, the string responded by server
1123 * @return int, if the command completed return 0, else return
1126 static int ms_ascii_process_line(ms_conn_t
*c
, char *command
)
1130 char *buffer
= command
;
1135 * for command get, we store the returned value into local buffer
1136 * then continue in ms_complete_nread().
1141 case 'V': /* VALUE || VERSION */
1142 if (buffer
[1] == 'A') /* VALUE */
1144 token_t tokens
[MAX_TOKENS
];
1145 ms_tokenize_command(command
, tokens
, MAX_TOKENS
);
1146 value_len
= strtol(tokens
[VALUELEN_TOKEN
].value
, NULL
, 10);
1147 c
->currcmd
.key_prefix
= *(uint64_t *)tokens
[KEY_TOKEN
].value
;
1150 * We read the \r\n into the string since not doing so is more
1151 * cycles then the waster of memory to do so.
1153 * We are null terminating through, which will most likely make
1154 * some people lazy about using the return length.
1156 c
->rvbytes
= (int)(value_len
+ 2);
1164 c
->currcmd
.retstat
= MCD_SUCCESS
;
1166 case 'S': /* STORED STATS SERVER_ERROR */
1167 if (buffer
[2] == 'A') /* STORED STATS */
1169 c
->currcmd
.retstat
= MCD_STAT
;
1171 else if (buffer
[1] == 'E')
1174 printf("<%d %s\n", c
->sfd
, buffer
);
1176 c
->currcmd
.retstat
= MCD_SERVER_ERROR
;
1178 else if (buffer
[1] == 'T')
1181 c
->currcmd
.retstat
= MCD_STORED
;
1185 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
1189 case 'D': /* DELETED DATA */
1190 if (buffer
[1] == 'E')
1192 c
->currcmd
.retstat
= MCD_DELETED
;
1196 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
1201 case 'N': /* NOT_FOUND NOT_STORED*/
1202 if (buffer
[4] == 'F')
1204 c
->currcmd
.retstat
= MCD_NOTFOUND
;
1206 else if (buffer
[4] == 'S')
1208 printf("<%d %s\n", c
->sfd
, buffer
);
1209 c
->currcmd
.retstat
= MCD_NOTSTORED
;
1213 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
1217 case 'E': /* PROTOCOL ERROR or END */
1218 if (buffer
[1] == 'N')
1221 c
->currcmd
.retstat
= MCD_END
;
1223 else if (buffer
[1] == 'R')
1225 printf("<%d ERROR\n", c
->sfd
);
1226 c
->currcmd
.retstat
= MCD_PROTOCOL_ERROR
;
1228 else if (buffer
[1] == 'X')
1230 c
->currcmd
.retstat
= MCD_DATA_EXISTS
;
1231 printf("<%d %s\n", c
->sfd
, buffer
);
1235 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
1239 case 'C': /* CLIENT ERROR */
1240 printf("<%d %s\n", c
->sfd
, buffer
);
1241 c
->currcmd
.retstat
= MCD_CLIENT_ERROR
;
1245 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
1250 } /* ms_ascii_process_line */
1254 * after one operation completes, reset the concurrency
1256 * @param c, pointer of the concurrency
1257 * @param timeout, whether it's timeout
1259 void ms_reset_conn(ms_conn_t
*c
, bool timeout
)
1265 if ((c
->packets
> 0) && (c
->packets
< MAX_UDP_PACKET
))
1267 memset(c
->udppkt
, 0, sizeof(ms_udppkt_t
) * (uint64_t)c
->packets
);
1276 c
->currcmd
.isfinish
= true;
1280 ms_conn_set_state(c
, conn_write
);
1281 memcpy(&c
->precmd
, &c
->currcmd
, sizeof(ms_cmdstat_t
)); /* replicate command state */
1285 ms_drive_machine(c
);
1287 } /* ms_reset_conn */
1291 * if we have a complete line in the buffer, process it.
1293 * @param c, pointer of the concurrency
1295 * @return int, if success, return 0, else return -1
1297 static int ms_try_read_line(ms_conn_t
*c
)
1299 if (c
->protocol
== binary_prot
)
1301 /* Do we have the complete packet header? */
1302 if ((uint64_t)c
->rbytes
< sizeof(c
->binary_header
))
1304 /* need more data! */
1310 if (((long)(c
->rcurr
)) % 8 != 0)
1312 /* must realign input buffer */
1313 memmove(c
->rbuf
, c
->rcurr
, c
->rbytes
);
1315 if (settings
.verbose
)
1317 fprintf(stderr
, "%d: Realign input buffer.\n", c
->sfd
);
1321 protocol_binary_response_header
*rsp
;
1322 rsp
= (protocol_binary_response_header
*)c
->rcurr
;
1324 c
->binary_header
= *rsp
;
1325 c
->binary_header
.response
.extlen
= rsp
->response
.extlen
;
1326 c
->binary_header
.response
.keylen
= ntohl(rsp
->response
.keylen
);
1327 c
->binary_header
.response
.bodylen
= ntohl(rsp
->response
.bodylen
);
1328 c
->binary_header
.response
.status
= ntohl(rsp
->response
.status
);
1330 if (c
->binary_header
.response
.magic
!= PROTOCOL_BINARY_RES
)
1332 fprintf(stderr
, "Invalid magic: %x\n",
1333 c
->binary_header
.response
.magic
);
1334 ms_conn_set_state(c
, conn_closing
);
1338 /* process this complete response */
1339 if (ms_bin_process_response(c
) == 0)
1341 /* current operation completed */
1342 ms_reset_conn(c
, false);
1347 c
->rbytes
-= (int32_t)sizeof(c
->binary_header
);
1348 c
->rcurr
+= sizeof(c
->binary_header
);
1357 assert(c
->rcurr
<= (c
->rbuf
+ c
->rsize
));
1362 el
= memchr(c
->rcurr
, '\n', (size_t)c
->rbytes
);
1367 if (((el
- c
->rcurr
) > 1) && (*(el
- 1) == '\r'))
1373 assert(cont
<= (c
->rcurr
+ c
->rbytes
));
1375 /* process this complete line */
1376 if (ms_ascii_process_line(c
, c
->rcurr
) == 0)
1378 /* current operation completed */
1379 ms_reset_conn(c
, false);
1384 /* current operation didn't complete */
1385 c
->rbytes
-= (int32_t)(cont
- c
->rcurr
);
1389 assert(c
->rcurr
<= (c
->rbuf
+ c
->rsize
));
1393 } /* ms_try_read_line */
1397 * because the packet of UDP can't ensure the order, the
1398 * function is used to sort the received udp packet.
1400 * @param c, pointer of the concurrency
1401 * @param buf, the buffer to store the ordered packages data
1402 * @param rbytes, the maximum capacity of the buffer
1404 * @return int, if success, return the copy bytes, else return
1407 static int ms_sort_udp_packet(ms_conn_t
*c
, char *buf
, int rbytes
)
1412 uint16_t seq_num
= 0;
1413 uint16_t packets
= 0;
1414 unsigned char *header
= NULL
;
1416 /* no enough data */
1418 assert(buf
!= NULL
);
1419 assert(c
->rudpbytes
>= UDP_HEADER_SIZE
);
1421 /* calculate received packets count */
1422 if (c
->rudpbytes
% UDP_MAX_PAYLOAD_SIZE
>= UDP_HEADER_SIZE
)
1424 /* the last packet has some data */
1425 c
->recvpkt
= c
->rudpbytes
/ UDP_MAX_PAYLOAD_SIZE
+ 1;
1429 c
->recvpkt
= c
->rudpbytes
/ UDP_MAX_PAYLOAD_SIZE
;
1432 /* get the total packets count if necessary */
1433 if (c
->packets
== 0)
1435 c
->packets
= HEADER_TO_PACKETS((unsigned char *)c
->rudpbuf
);
1438 /* build the ordered packet array */
1439 for (int i
= c
->pktcurr
; i
< c
->recvpkt
; i
++)
1441 header
= (unsigned char *)c
->rudpbuf
+ i
* UDP_MAX_PAYLOAD_SIZE
;
1442 req_id
= (uint16_t)HEADER_TO_REQID(header
);
1443 assert(req_id
== c
->request_id
% (1 << 16));
1445 packets
= (uint16_t)HEADER_TO_PACKETS(header
);
1446 assert(c
->packets
== HEADER_TO_PACKETS(header
));
1448 seq_num
= (uint16_t)HEADER_TO_SEQNUM(header
);
1449 c
->udppkt
[seq_num
].header
= header
;
1450 c
->udppkt
[seq_num
].data
= (char *)header
+ UDP_HEADER_SIZE
;
1452 if (i
== c
->recvpkt
- 1)
1454 /* last received packet */
1455 if (c
->rudpbytes
% UDP_MAX_PAYLOAD_SIZE
== 0)
1457 c
->udppkt
[seq_num
].rbytes
= UDP_MAX_PAYLOAD_SIZE
- UDP_HEADER_SIZE
;
1462 c
->udppkt
[seq_num
].rbytes
= c
->rudpbytes
% UDP_MAX_PAYLOAD_SIZE
1468 c
->udppkt
[seq_num
].rbytes
= UDP_MAX_PAYLOAD_SIZE
- UDP_HEADER_SIZE
;
1473 for (int i
= c
->ordcurr
; i
< c
->recvpkt
; i
++)
1475 /* there is some data to copy */
1476 if ((c
->udppkt
[i
].data
!= NULL
)
1477 && (c
->udppkt
[i
].copybytes
< c
->udppkt
[i
].rbytes
))
1479 header
= c
->udppkt
[i
].header
;
1480 len
= c
->udppkt
[i
].rbytes
- c
->udppkt
[i
].copybytes
;
1481 if (len
> rbytes
- wbytes
)
1483 len
= rbytes
- wbytes
;
1486 assert(len
<= rbytes
- wbytes
);
1487 assert(i
== HEADER_TO_SEQNUM(header
));
1489 memcpy(buf
+ wbytes
, c
->udppkt
[i
].data
+ c
->udppkt
[i
].copybytes
,
1492 c
->udppkt
[i
].copybytes
+= len
;
1494 if ((c
->udppkt
[i
].copybytes
== c
->udppkt
[i
].rbytes
)
1495 && (c
->udppkt
[i
].rbytes
== UDP_MAX_PAYLOAD_SIZE
- UDP_HEADER_SIZE
))
1497 /* finish copying all the data of this packet, next */
1501 /* last received packet, and finish copying all the data */
1502 if ((c
->recvpkt
== c
->packets
) && (i
== c
->recvpkt
- 1)
1503 && (c
->udppkt
[i
].copybytes
== c
->udppkt
[i
].rbytes
))
1508 /* no space to copy data */
1509 if (wbytes
>= rbytes
)
1514 /* it doesn't finish reading all the data of the packet from network */
1515 if ((i
!= c
->recvpkt
- 1)
1516 && (c
->udppkt
[i
].rbytes
< UDP_MAX_PAYLOAD_SIZE
- UDP_HEADER_SIZE
))
1523 /* no data to copy */
1528 return wbytes
== 0 ? -1 : wbytes
;
1529 } /* ms_sort_udp_packet */
1533 * encapsulate upd read like tcp read
1535 * @param c, pointer of the concurrency
1536 * @param buf, read buffer
1537 * @param len, length to read
1539 * @return int, if success, return the read bytes, else return
1542 static int ms_udp_read(ms_conn_t
*c
, char *buf
, int len
)
1553 if (c
->rudpbytes
+ UDP_MAX_PAYLOAD_SIZE
> c
->rudpsize
)
1555 char *new_rbuf
= realloc(c
->rudpbuf
, (size_t)c
->rudpsize
* 2);
1558 fprintf(stderr
, "Couldn't realloc input buffer.\n");
1559 c
->rudpbytes
= 0; /* ignore what we read */
1562 c
->rudpbuf
= new_rbuf
;
1566 avail
= c
->rudpsize
- c
->rudpbytes
;
1567 /* UDP each time read a packet, 1400 bytes */
1568 res
= (int)read(c
->sfd
, c
->rudpbuf
+ c
->rudpbytes
, (size_t)avail
);
1572 atomic_add_64(&ms_stats
.bytes_read
, res
);
1587 /* "connection" closed */
1593 /* no data to read */
1598 /* copy data to read buffer */
1601 copybytes
= ms_sort_udp_packet(c
, buf
, len
);
1604 if (copybytes
== -1)
1606 atomic_add_64(&ms_stats
.pkt_disorder
, 1);
1614 * read from network as much as we can, handle buffer overflow and connection
1616 * before reading, move the remaining incomplete fragment of a command
1617 * (if any) to the beginning of the buffer.
1618 * return 0 if there's nothing to read on the first read.
1622 * read from network as much as we can, handle buffer overflow and connection
1623 * close. before reading, move the remaining incomplete fragment of a command
1624 * (if any) to the beginning of the buffer.
1626 * @param c, pointer of the concurrency
1629 * return 0 if there's nothing to read on the first read.
1630 * return 1 if get data
1631 * return -1 if error happens
1633 static int ms_try_read_network(ms_conn_t
*c
)
1641 if ((c
->rcurr
!= c
->rbuf
)
1642 && (! c
->readval
|| (c
->rvbytes
> c
->rsize
- (c
->rcurr
- c
->rbuf
))
1643 || (c
->readval
&& (c
->rcurr
- c
->rbuf
> c
->rbytes
))))
1645 if (c
->rbytes
!= 0) /* otherwise there's nothing to copy */
1646 memmove(c
->rbuf
, c
->rcurr
, (size_t)c
->rbytes
);
1652 if (c
->rbytes
>= c
->rsize
)
1654 char *new_rbuf
= realloc(c
->rbuf
, (size_t)c
->rsize
* 2);
1657 fprintf(stderr
, "Couldn't realloc input buffer.\n");
1658 c
->rbytes
= 0; /* ignore what we read */
1661 c
->rcurr
= c
->rbuf
= new_rbuf
;
1665 avail
= c
->rsize
- c
->rbytes
- (c
->rcurr
- c
->rbuf
);
1673 res
= (int32_t)ms_udp_read(c
, c
->rcurr
+ c
->rbytes
, (int32_t)avail
);
1677 res
= (int)read(c
->sfd
, c
->rcurr
+ c
->rbytes
, (size_t)avail
);
1684 atomic_add_64(&ms_stats
.bytes_read
, res
);
1699 /* connection closed */
1700 ms_conn_set_state(c
, conn_closing
);
1705 if ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
))
1707 /* Should close on unhandled errors. */
1708 ms_conn_set_state(c
, conn_closing
);
1714 } /* ms_try_read_network */
1718 * after get the object from server, verify the value if
1721 * @param c, pointer of the concurrency
1722 * @param mlget_item, pointer of mulit-get task item structure
1723 * @param value, received value string
1724 * @param vlen, received value string length
1726 static void ms_verify_value(ms_conn_t
*c
,
1727 ms_mlget_task_item_t
*mlget_item
,
1731 if (c
->curr_task
.verify
)
1733 assert(c
->curr_task
.item
->value_offset
!= INVALID_OFFSET
);
1734 char *orignval
= &ms_setting
.char_block
[c
->curr_task
.item
->value_offset
];
1736 &ms_setting
.char_block
[c
->curr_task
.item
->key_suffix_offset
];
1738 /* verify expire time if necessary */
1739 if (c
->curr_task
.item
->exp_time
> 0)
1741 struct timeval curr_time
;
1742 gettimeofday(&curr_time
, NULL
);
1744 /* object expired but get it now */
1745 if (curr_time
.tv_sec
- c
->curr_task
.item
->client_time
1746 > c
->curr_task
.item
->exp_time
+ EXPIRE_TIME_ERROR
)
1748 atomic_add_64(&ms_stats
.exp_get
, 1);
1750 if (ms_setting
.verbose
)
1754 strftime(set_time
, 64, "%Y-%m-%d %H:%M:%S",
1755 localtime(&c
->curr_task
.item
->client_time
));
1756 strftime(cur_time
, 64, "%Y-%m-%d %H:%M:%S",
1757 localtime(&curr_time
.tv_sec
));
1759 "\n<%d expire time verification failed, "
1760 "object expired but get it now\n"
1763 "\tset time: %s current time: %s "
1764 "diff time: %d expire time: %d\n"
1765 "\texpected data: \n"
1766 "\treceived data len: %d\n"
1767 "\treceived data: %.*s\n",
1769 c
->curr_task
.item
->key_size
,
1770 c
->curr_task
.item
->key_prefix
,
1771 c
->curr_task
.item
->key_size
- (int)KEY_PREFIX_SIZE
,
1775 (int)(curr_time
.tv_sec
- c
->curr_task
.item
->client_time
),
1776 c
->curr_task
.item
->exp_time
,
1786 if ((c
->curr_task
.item
->value_size
!= vlen
)
1787 || (memcmp(orignval
, value
, (size_t)vlen
) != 0))
1789 atomic_add_64(&ms_stats
.vef_failed
, 1);
1791 if (ms_setting
.verbose
)
1794 "\n<%d data verification failed\n"
1797 "\texpected data len: %d\n"
1798 "\texpected data: %.*s\n"
1799 "\treceived data len: %d\n"
1800 "\treceived data: %.*s\n",
1802 c
->curr_task
.item
->key_size
,
1803 c
->curr_task
.item
->key_prefix
,
1804 c
->curr_task
.item
->key_size
- (int)KEY_PREFIX_SIZE
,
1806 c
->curr_task
.item
->value_size
,
1807 c
->curr_task
.item
->value_size
,
1817 c
->curr_task
.finish_verify
= true;
1819 if (mlget_item
!= NULL
)
1821 mlget_item
->finish_verify
= true;
1824 } /* ms_verify_value */
1828 * For ASCII protocol, after store the data into the local
1829 * buffer, run this function to handle the data.
1831 * @param c, pointer of the concurrency
1833 static void ms_ascii_complete_nread(ms_conn_t
*c
)
1836 assert(c
->rbytes
>= c
->rvbytes
);
1837 assert(c
->protocol
== ascii_udp_prot
|| c
->protocol
== ascii_prot
);
1841 c
->rcurr
[c
->rvbytes
- 1] == '\n' && c
->rcurr
[c
->rvbytes
- 2] == '\r');
1845 ms_mlget_task_item_t
*mlget_item
= NULL
;
1846 if (((ms_setting
.mult_key_num
> 1)
1847 && (c
->mlget_task
.mlget_num
>= ms_setting
.mult_key_num
))
1848 || ((c
->remain_exec_num
== 0) && (c
->mlget_task
.mlget_num
> 0)))
1850 c
->mlget_task
.value_index
++;
1851 mlget_item
= &c
->mlget_task
.mlget_item
[c
->mlget_task
.value_index
];
1853 if (mlget_item
->item
->key_prefix
== c
->currcmd
.key_prefix
)
1855 c
->curr_task
.item
= mlget_item
->item
;
1856 c
->curr_task
.verify
= mlget_item
->verify
;
1857 c
->curr_task
.finish_verify
= mlget_item
->finish_verify
;
1858 mlget_item
->get_miss
= false;
1862 /* Try to find the task item in multi-get task array */
1863 for (int i
= 0; i
< c
->mlget_task
.mlget_num
; i
++)
1865 mlget_item
= &c
->mlget_task
.mlget_item
[i
];
1866 if (mlget_item
->item
->key_prefix
== c
->currcmd
.key_prefix
)
1868 c
->curr_task
.item
= mlget_item
->item
;
1869 c
->curr_task
.verify
= mlget_item
->verify
;
1870 c
->curr_task
.finish_verify
= mlget_item
->finish_verify
;
1871 mlget_item
->get_miss
= false;
1879 ms_verify_value(c
, mlget_item
, c
->rcurr
, c
->rvbytes
- 2);
1881 c
->curr_task
.get_miss
= false;
1882 c
->rbytes
-= c
->rvbytes
;
1883 c
->rcurr
= c
->rcurr
+ c
->rvbytes
;
1884 assert(c
->rcurr
<= (c
->rbuf
+ c
->rsize
));
1887 } /* ms_ascii_complete_nread */
1891 * For binary protocol, after store the data into the local
1892 * buffer, run this function to handle the data.
1894 * @param c, pointer of the concurrency
1896 static void ms_bin_complete_nread(ms_conn_t
*c
)
1899 assert(c
->rbytes
>= c
->rvbytes
);
1900 assert(c
->protocol
== binary_prot
);
1902 int extlen
= c
->binary_header
.response
.extlen
;
1903 int keylen
= c
->binary_header
.response
.keylen
;
1904 uint8_t opcode
= c
->binary_header
.response
.opcode
;
1906 /* not get command or not include value, just return */
1907 if (((opcode
!= PROTOCOL_BINARY_CMD_GET
)
1908 && (opcode
!= PROTOCOL_BINARY_CMD_GETQ
))
1909 || (c
->rvbytes
<= extlen
+ keylen
))
1912 if (c
->binary_header
.response
.opcode
== PROTOCOL_BINARY_CMD_GET
)
1914 c
->currcmd
.retstat
= MCD_END
;
1915 c
->curr_task
.get_miss
= true;
1920 ms_reset_conn(c
, false);
1925 ms_mlget_task_item_t
*mlget_item
= NULL
;
1926 if (((ms_setting
.mult_key_num
> 1)
1927 && (c
->mlget_task
.mlget_num
>= ms_setting
.mult_key_num
))
1928 || ((c
->remain_exec_num
== 0) && (c
->mlget_task
.mlget_num
> 0)))
1930 c
->mlget_task
.value_index
++;
1931 mlget_item
= &c
->mlget_task
.mlget_item
[c
->mlget_task
.value_index
];
1933 c
->curr_task
.item
= mlget_item
->item
;
1934 c
->curr_task
.verify
= mlget_item
->verify
;
1935 c
->curr_task
.finish_verify
= mlget_item
->finish_verify
;
1936 mlget_item
->get_miss
= false;
1941 c
->rcurr
+ extlen
+ keylen
,
1942 c
->rvbytes
- extlen
- keylen
);
1944 c
->currcmd
.retstat
= MCD_END
;
1945 c
->curr_task
.get_miss
= false;
1946 c
->rbytes
-= c
->rvbytes
;
1947 c
->rcurr
= c
->rcurr
+ c
->rvbytes
;
1948 assert(c
->rcurr
<= (c
->rbuf
+ c
->rsize
));
1952 if (ms_setting
.mult_key_num
> 1)
1954 /* multi-get have check all the item */
1955 if (c
->mlget_task
.value_index
== c
->mlget_task
.mlget_num
- 1)
1957 ms_reset_conn(c
, false);
1963 ms_reset_conn(c
, false);
1965 } /* ms_bin_complete_nread */
1969 * we get here after reading the value of get commands.
1971 * @param c, pointer of the concurrency
1973 static void ms_complete_nread(ms_conn_t
*c
)
1976 assert(c
->rbytes
>= c
->rvbytes
);
1977 assert(c
->protocol
== ascii_udp_prot
1978 || c
->protocol
== ascii_prot
1979 || c
->protocol
== binary_prot
);
1981 if (c
->protocol
== binary_prot
)
1983 ms_bin_complete_nread(c
);
1987 ms_ascii_complete_nread(c
);
1989 } /* ms_complete_nread */
1993 * Adds a message header to a connection.
1995 * @param c, pointer of the concurrency
1997 * @return int, if success, return 0, else return -1
1999 static int ms_add_msghdr(ms_conn_t
*c
)
2005 if (c
->msgsize
== c
->msgused
)
2008 realloc(c
->msglist
, (uint64_t)c
->msgsize
* 2 * sizeof(struct msghdr
));
2016 msg
= c
->msglist
+ c
->msgused
;
2019 * this wipes msg_iovlen, msg_control, msg_controllen, and
2020 * msg_flags, the last 3 of which aren't defined on solaris:
2022 memset(msg
, 0, sizeof(struct msghdr
));
2024 msg
->msg_iov
= &c
->iov
[c
->iovused
];
2026 if (c
->udp
&& (c
->srv_recv_addr_size
> 0))
2028 msg
->msg_name
= &c
->srv_recv_addr
;
2029 msg
->msg_namelen
= c
->srv_recv_addr_size
;
2037 /* Leave room for the UDP header, which we'll fill in later. */
2038 return ms_add_iov(c
, NULL
, UDP_HEADER_SIZE
);
2042 } /* ms_add_msghdr */
2046 * Ensures that there is room for another structure iovec in a connection's
2049 * @param c, pointer of the concurrency
2051 * @return int, if success, return 0, else return -1
2053 static int ms_ensure_iov_space(ms_conn_t
*c
)
2057 if (c
->iovused
>= c
->iovsize
)
2060 struct iovec
*new_iov
= (struct iovec
*)realloc(c
->iov
,
2061 ((uint64_t)c
->iovsize
2063 * sizeof(struct iovec
));
2070 /* Point all the msghdr structures at the new list. */
2071 for (i
= 0, iovnum
= 0; i
< c
->msgused
; i
++)
2073 c
->msglist
[i
].msg_iov
= &c
->iov
[iovnum
];
2074 iovnum
+= (int)c
->msglist
[i
].msg_iovlen
;
2079 } /* ms_ensure_iov_space */
2083 * Adds data to the list of pending data that will be written out to a
2086 * @param c, pointer of the concurrency
2087 * @param buf, the buffer includes data to send
2088 * @param len, the data length in the buffer
2090 * @return int, if success, return 0, else return -1
2092 static int ms_add_iov(ms_conn_t
*c
, const void *buf
, int len
)
2102 m
= &c
->msglist
[c
->msgused
- 1];
2105 * Limit UDP packets, to UDP_MAX_PAYLOAD_SIZE bytes.
2107 limit_to_mtu
= c
->udp
;
2109 /* We may need to start a new msghdr if this one is full. */
2110 if ((m
->msg_iovlen
== IOV_MAX
)
2111 || (limit_to_mtu
&& (c
->msgbytes
>= UDP_MAX_SEND_PAYLOAD_SIZE
)))
2114 m
= &c
->msglist
[c
->msgused
- 1];
2117 if (ms_ensure_iov_space(c
) != 0)
2120 /* If the fragment is too big to fit in the datagram, split it up */
2121 if (limit_to_mtu
&& (len
+ c
->msgbytes
> UDP_MAX_SEND_PAYLOAD_SIZE
))
2123 leftover
= len
+ c
->msgbytes
- UDP_MAX_SEND_PAYLOAD_SIZE
;
2131 m
= &c
->msglist
[c
->msgused
- 1];
2132 m
->msg_iov
[m
->msg_iovlen
].iov_base
= (void *)buf
;
2133 m
->msg_iov
[m
->msg_iovlen
].iov_len
= (size_t)len
;
2139 buf
= ((char *)buf
) + len
;
2142 while (leftover
> 0);
2149 * Constructs a set of UDP headers and attaches them to the outgoing messages.
2151 * @param c, pointer of the concurrency
2153 * @return int, if success, return 0, else return -1
2155 static int ms_build_udp_headers(ms_conn_t
*c
)
2162 c
->request_id
= ms_get_udp_request_id();
2164 if (c
->msgused
> c
->hdrsize
)
2168 new_hdrbuf
= realloc(c
->hdrbuf
,
2169 (size_t)c
->msgused
* 2 * UDP_HEADER_SIZE
);
2171 new_hdrbuf
= malloc((size_t)c
->msgused
* 2 * UDP_HEADER_SIZE
);
2175 c
->hdrbuf
= (unsigned char *)new_hdrbuf
;
2176 c
->hdrsize
= c
->msgused
* 2;
2179 /* If this is a multi-packet request, drop it. */
2180 if (c
->udp
&& (c
->msgused
> 1))
2182 fprintf(stderr
, "multi-packet request for UDP not supported.\n");
2187 for (i
= 0; i
< c
->msgused
; i
++)
2189 c
->msglist
[i
].msg_iov
[0].iov_base
= (void *)hdr
;
2190 c
->msglist
[i
].msg_iov
[0].iov_len
= UDP_HEADER_SIZE
;
2191 *hdr
++= (unsigned char)(c
->request_id
/ 256);
2192 *hdr
++= (unsigned char)(c
->request_id
% 256);
2193 *hdr
++= (unsigned char)(i
/ 256);
2194 *hdr
++= (unsigned char)(i
% 256);
2195 *hdr
++= (unsigned char)(c
->msgused
/ 256);
2196 *hdr
++= (unsigned char)(c
->msgused
% 256);
2197 *hdr
++= (unsigned char)1; /* support facebook memcached */
2198 *hdr
++= (unsigned char)0;
2200 ((unsigned char *)c
->msglist
[i
].msg_iov
[0].iov_base
2201 + UDP_HEADER_SIZE
));
2205 } /* ms_build_udp_headers */
2209 * Transmit the next chunk of data from our list of msgbuf structures.
2211 * @param c, pointer of the concurrency
2213 * @return TRANSMIT_COMPLETE All done writing.
2214 * TRANSMIT_INCOMPLETE More data remaining to write.
2215 * TRANSMIT_SOFT_ERROR Can't write any more right now.
2216 * TRANSMIT_HARD_ERROR Can't write (c->state is set to conn_closing)
2218 static int ms_transmit(ms_conn_t
*c
)
2222 if ((c
->msgcurr
< c
->msgused
)
2223 && (c
->msglist
[c
->msgcurr
].msg_iovlen
== 0))
2225 /* Finished writing the current msg; advance to the next. */
2229 if (c
->msgcurr
< c
->msgused
)
2232 struct msghdr
*m
= &c
->msglist
[c
->msgcurr
];
2234 res
= sendmsg(c
->sfd
, m
, 0);
2237 atomic_add_64(&ms_stats
.bytes_written
, res
);
2239 /* We've written some of the data. Remove the completed
2240 * iovec entries from the list of pending writes. */
2241 while (m
->msg_iovlen
> 0 && res
>= (ssize_t
)m
->msg_iov
->iov_len
)
2243 res
-= (ssize_t
)m
->msg_iov
->iov_len
;
2248 /* Might have written just part of the last iovec entry;
2249 * adjust it so the next write will do the rest. */
2252 m
->msg_iov
->iov_base
= (void *)((unsigned char *)m
->msg_iov
->iov_base
+ res
);
2253 m
->msg_iov
->iov_len
-= (uint64_t)res
;
2255 return TRANSMIT_INCOMPLETE
;
2257 if ((res
== -1) && ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
)))
2259 if (! ms_update_event(c
, EV_WRITE
| EV_PERSIST
))
2261 fprintf(stderr
, "Couldn't update event.\n");
2262 ms_conn_set_state(c
, conn_closing
);
2263 return TRANSMIT_HARD_ERROR
;
2265 return TRANSMIT_SOFT_ERROR
;
2268 /* if res==0 or res==-1 and error is not EAGAIN or EWOULDBLOCK,
2269 * we have a real error, on which we close the connection */
2270 fprintf(stderr
, "Failed to write, and not due to blocking.\n");
2272 ms_conn_set_state(c
, conn_closing
);
2273 return TRANSMIT_HARD_ERROR
;
2277 return TRANSMIT_COMPLETE
;
2283 * Shrinks a connection's buffers if they're too big. This prevents
2284 * periodic large "mget" response from server chewing lots of client
2287 * This should only be called in between requests since it can wipe output
2290 * @param c, pointer of the concurrency
2292 static void ms_conn_shrink(ms_conn_t
*c
)
2299 if ((c
->rsize
> READ_BUFFER_HIGHWAT
) && (c
->rbytes
< DATA_BUFFER_SIZE
))
2303 if (c
->rcurr
!= c
->rbuf
)
2304 memmove(c
->rbuf
, c
->rcurr
, (size_t)c
->rbytes
);
2306 newbuf
= (char *)realloc((void *)c
->rbuf
, DATA_BUFFER_SIZE
);
2311 c
->rsize
= DATA_BUFFER_SIZE
;
2316 if (c
->udp
&& (c
->rudpsize
> UDP_DATA_BUFFER_HIGHWAT
)
2317 && (c
->rudpbytes
+ UDP_MAX_PAYLOAD_SIZE
< UDP_DATA_BUFFER_SIZE
))
2319 char *new_rbuf
= (char *)realloc(c
->rudpbuf
, (size_t)c
->rudpsize
* 2);
2322 c
->rudpbuf
= new_rbuf
;
2323 c
->rudpsize
= UDP_DATA_BUFFER_SIZE
;
2325 /* TODO check error condition? */
2328 if (c
->msgsize
> MSG_LIST_HIGHWAT
)
2330 struct msghdr
*newbuf
= (struct msghdr
*)realloc(
2333 * sizeof(c
->msglist
[0]));
2337 c
->msgsize
= MSG_LIST_INITIAL
;
2339 /* TODO check error condition? */
2342 if (c
->iovsize
> IOV_LIST_HIGHWAT
)
2344 struct iovec
*newbuf
= (struct iovec
*)realloc((void *)c
->iov
,
2346 * sizeof(c
->iov
[0]));
2350 c
->iovsize
= IOV_LIST_INITIAL
;
2352 /* TODO check return value */
2354 } /* ms_conn_shrink */
2358 * Sets a connection's current state in the state machine. Any special
2359 * processing that needs to happen on certain state transitions can
2362 * @param c, pointer of the concurrency
2363 * @param state, connection state
2365 static void ms_conn_set_state(ms_conn_t
*c
, int state
)
2369 if (state
!= c
->state
)
2371 if (state
== conn_read
)
2377 } /* ms_conn_set_state */
2381 * update the event if socks change state. for example: when
2382 * change the listen scoket read event to sock write event, or
2383 * change socket handler, we could call this function.
2385 * @param c, pointer of the concurrency
2386 * @param new_flags, new event flags
2388 * @return bool, if success, return true, else return false
2390 static bool ms_update_event(ms_conn_t
*c
, const int new_flags
)
2392 /* default event timeout 10 seconds */
2395 .tv_sec
= EVENT_TIMEOUT
, .tv_usec
= 0
2400 struct event_base
*base
= c
->event
.ev_base
;
2401 if ((c
->ev_flags
== new_flags
) && (ms_setting
.rep_write_srv
== 0)
2402 && (! ms_setting
.facebook_test
|| (c
->total_sfds
== 1)))
2407 if (event_del(&c
->event
) == -1)
2409 /* try to delete the event again */
2410 if (event_del(&c
->event
) == -1)
2416 event_set(&c
->event
,
2421 event_base_set(base
, &c
->event
);
2422 c
->ev_flags
= (short)new_flags
;
2424 if (c
->total_sfds
== 1)
2426 if (event_add(&c
->event
, NULL
) == -1)
2433 if (event_add(&c
->event
, &t
) == -1)
2440 } /* ms_update_event */
2444 * If user want to get the expected throughput, we could limit
2445 * the performance of memslap. we could give up some work and
2446 * just wait a short time. The function is used to check this
2449 * @param c, pointer of the concurrency
2451 * @return bool, if success, return true, else return false
2453 static bool ms_need_yield(ms_conn_t
*c
)
2456 int64_t time_diff
= 0;
2457 struct timeval curr_time
;
2458 ms_task_t
*task
= &c
->curr_task
;
2460 if (ms_setting
.expected_tps
> 0)
2462 gettimeofday(&curr_time
, NULL
);
2463 time_diff
= ms_time_diff(&ms_thread
.startup_time
, &curr_time
);
2465 (int64_t)((task
->get_opt
2466 + task
->set_opt
) / ((uint64_t)time_diff
/ 1000000));
2468 /* current throughput is greater than expected throughput */
2469 if (tps
> ms_thread
.thread_ctx
->tps_perconn
)
2476 } /* ms_need_yield */
2480 * used to update the start time of each operation
2482 * @param c, pointer of the concurrency
2484 static void ms_update_start_time(ms_conn_t
*c
)
2486 ms_task_item_t
*item
= c
->curr_task
.item
;
2488 if ((ms_setting
.stat_freq
> 0) || c
->udp
2489 || ((c
->currcmd
.cmd
== CMD_SET
) && (item
->exp_time
> 0)))
2491 gettimeofday(&c
->start_time
, NULL
);
2492 if ((c
->currcmd
.cmd
== CMD_SET
) && (item
->exp_time
> 0))
2494 /* record the current time */
2495 item
->client_time
= c
->start_time
.tv_sec
;
2498 } /* ms_update_start_time */
2502 * run the state machine
2504 * @param c, pointer of the concurrency
2506 static void ms_drive_machine(ms_conn_t
*c
)
2519 if (c
->rbytes
>= c
->rvbytes
)
2521 ms_complete_nread(c
);
2527 if (ms_try_read_line(c
) != 0)
2533 if (ms_try_read_network(c
) != 0)
2538 /* doesn't read all the response data, wait event wake up */
2539 if (! c
->currcmd
.isfinish
)
2541 if (! ms_update_event(c
, EV_READ
| EV_PERSIST
))
2543 fprintf(stderr
, "Couldn't update event.\n");
2544 ms_conn_set_state(c
, conn_closing
);
2551 /* we have no command line and no data to read from network, next write */
2552 ms_conn_set_state(c
, conn_write
);
2553 memcpy(&c
->precmd
, &c
->currcmd
, sizeof(ms_cmdstat_t
)); /* replicate command state */
2558 if (! c
->ctnwrite
&& ms_need_yield(c
))
2562 if (! ms_update_event(c
, EV_WRITE
| EV_PERSIST
))
2564 fprintf(stderr
, "Couldn't update event.\n");
2565 ms_conn_set_state(c
, conn_closing
);
2572 if (! c
->ctnwrite
&& (ms_exec_task(c
) != 0))
2574 ms_conn_set_state(c
, conn_closing
);
2578 /* record the start time before starting to send data if necessary */
2579 if (! c
->ctnwrite
|| (c
->change_sfd
&& c
->ctnwrite
))
2583 c
->change_sfd
= false;
2585 ms_update_start_time(c
);
2588 /* change sfd if necessary */
2596 /* execute task until nothing need be written to network */
2597 if (! c
->ctnwrite
&& (c
->msgcurr
== c
->msgused
))
2599 if (! ms_update_event(c
, EV_WRITE
| EV_PERSIST
))
2601 fprintf(stderr
, "Couldn't update event.\n");
2602 ms_conn_set_state(c
, conn_closing
);
2609 switch (ms_transmit(c
))
2611 case TRANSMIT_COMPLETE
:
2612 /* we have no data to write to network, next wait repose */
2613 if (! ms_update_event(c
, EV_READ
| EV_PERSIST
))
2615 fprintf(stderr
, "Couldn't update event.\n");
2616 ms_conn_set_state(c
, conn_closing
);
2620 ms_conn_set_state(c
, conn_read
);
2625 case TRANSMIT_INCOMPLETE
:
2627 break; /* Continue in state machine. */
2629 case TRANSMIT_HARD_ERROR
:
2633 case TRANSMIT_SOFT_ERROR
:
2645 /* recovery mode, need reconnect if connection close */
2646 if (ms_setting
.reconnect
&& (! ms_global
.time_out
2647 || ((ms_setting
.run_time
== 0)
2648 && (c
->remain_exec_num
> 0))))
2650 if (ms_reconn(c
) != 0)
2657 ms_reset_conn(c
, false);
2659 if (c
->total_sfds
== 1)
2661 if (! ms_update_event(c
, EV_WRITE
| EV_PERSIST
))
2663 fprintf(stderr
, "Couldn't update event.\n");
2664 ms_conn_set_state(c
, conn_closing
);
2682 } /* ms_drive_machine */
2686 * the event handler of each thread
2688 * @param fd, the file descriptor of socket
2689 * @param which, event flag
2690 * @param arg, argument
2692 void ms_event_handler(const int fd
, const short which
, void *arg
)
2694 ms_conn_t
*c
= (ms_conn_t
*)arg
;
2704 "Catastrophic: event fd: %d doesn't match conn fd: %d\n",
2710 assert(fd
== c
->sfd
);
2712 /* event timeout, close the current connection */
2713 if (c
->which
== EV_TIMEOUT
)
2715 ms_conn_set_state(c
, conn_closing
);
2718 ms_drive_machine(c
);
2720 /* wait for next event */
2721 } /* ms_event_handler */
2725 * get the next socket descriptor index to run for replication
2727 * @param c, pointer of the concurrency
2728 * @param cmd, command(get or set )
2730 * @return int, if success, return the index, else return 0
2732 static int ms_get_rep_sock_index(ms_conn_t
*c
, int cmd
)
2737 if (c
->total_sfds
== 1)
2742 if (ms_setting
.rep_write_srv
== 0)
2751 for (i
= 0; i
< ms_setting
.rep_write_srv
; i
++)
2753 if (c
->tcpsfd
[i
] > 0)
2759 if (i
== ms_setting
.rep_write_srv
)
2761 /* random get one replication server to read */
2762 sock_index
= (int)(random() % c
->total_sfds
);
2766 /* random get one replication writing server to write */
2767 sock_index
= (int)(random() % ms_setting
.rep_write_srv
);
2770 else if (cmd
== CMD_GET
)
2772 /* random get one replication server to read */
2773 sock_index
= (int)(random() % c
->total_sfds
);
2776 while (c
->tcpsfd
[sock_index
] == 0);
2779 } /* ms_get_rep_sock_index */
2783 * get the next socket descriptor index to run
2785 * @param c, pointer of the concurrency
2787 * @return int, return the index
2789 static int ms_get_next_sock_index(ms_conn_t
*c
)
2795 sock_index
= (++c
->cur_idx
== c
->total_sfds
) ? 0 : c
->cur_idx
;
2797 while (c
->tcpsfd
[sock_index
] == 0);
2800 } /* ms_get_next_sock_index */
2804 * update socket event of the connections
2806 * @param c, pointer of the concurrency
2808 * @return int, if success, return 0, else return -1
2810 static int ms_update_conn_sock_event(ms_conn_t
*c
)
2814 switch (c
->currcmd
.cmd
)
2817 if (ms_setting
.facebook_test
&& c
->udp
)
2819 c
->sfd
= c
->tcpsfd
[0];
2821 c
->change_sfd
= true;
2826 if (ms_setting
.facebook_test
&& ! c
->udp
)
2830 c
->change_sfd
= true;
2838 if (! c
->udp
&& (c
->total_sfds
> 1))
2840 if (c
->cur_idx
!= c
->total_sfds
)
2842 if (ms_setting
.rep_write_srv
== 0)
2844 c
->cur_idx
= ms_get_next_sock_index(c
);
2848 c
->cur_idx
= ms_get_rep_sock_index(c
, c
->currcmd
.cmd
);
2853 /* must select the first sock of the connection at the beginning */
2857 c
->sfd
= c
->tcpsfd
[c
->cur_idx
];
2858 assert(c
->sfd
!= 0);
2859 c
->change_sfd
= true;
2864 if (! ms_update_event(c
, EV_WRITE
| EV_PERSIST
))
2866 fprintf(stderr
, "Couldn't update event.\n");
2867 ms_conn_set_state(c
, conn_closing
);
2873 } /* ms_update_conn_sock_event */
2877 * for ASCII protocol, this function build the set command
2878 * string and send the command.
2880 * @param c, pointer of the concurrency
2881 * @param item, pointer of task item which includes the object
2884 * @return int, if success, return 0, else return -1
2886 static int ms_build_ascii_write_buf_set(ms_conn_t
*c
, ms_task_item_t
*item
)
2890 char *buffer
= c
->wbuf
;
2892 write_len
= sprintf(buffer
,
2898 if (write_len
> c
->wsize
)
2900 /* ought to be always enough. just fail for simplicity */
2901 fprintf(stderr
, "output command line too long.\n");
2905 if (item
->value_offset
== INVALID_OFFSET
)
2907 value_offset
= item
->key_suffix_offset
;
2911 value_offset
= item
->value_offset
;
2914 if ((ms_add_iov(c
, "set ", 4) != 0)
2915 || (ms_add_iov(c
, (char *)&item
->key_prefix
,
2916 (int)KEY_PREFIX_SIZE
) != 0)
2917 || (ms_add_iov(c
, &ms_setting
.char_block
[item
->key_suffix_offset
],
2918 item
->key_size
- (int)KEY_PREFIX_SIZE
) != 0)
2919 || (ms_add_iov(c
, buffer
, write_len
) != 0)
2920 || (ms_add_iov(c
, &ms_setting
.char_block
[value_offset
],
2921 item
->value_size
) != 0)
2922 || (ms_add_iov(c
, "\r\n", 2) != 0)
2923 || (c
->udp
&& (ms_build_udp_headers(c
) != 0)))
2929 } /* ms_build_ascii_write_buf_set */
2933 * used to send set command to server
2935 * @param c, pointer of the concurrency
2936 * @param item, pointer of task item which includes the object
2939 * @return int, if success, return 0, else return -1
2941 int ms_mcd_set(ms_conn_t
*c
, ms_task_item_t
*item
)
2945 c
->currcmd
.cmd
= CMD_SET
;
2946 c
->currcmd
.isfinish
= false;
2947 c
->currcmd
.retstat
= MCD_FAILURE
;
2949 if (ms_update_conn_sock_event(c
) != 0)
2957 if (ms_add_msghdr(c
) != 0)
2959 fprintf(stderr
, "Out of memory preparing request.");
2963 /* binary protocol */
2964 if (c
->protocol
== binary_prot
)
2966 if (ms_build_bin_write_buf_set(c
, item
) != 0)
2973 if (ms_build_ascii_write_buf_set(c
, item
) != 0)
2979 atomic_add_64(&ms_stats
.obj_bytes
,
2980 item
->key_size
+ item
->value_size
);
2981 atomic_add_64(&ms_stats
.cmd_set
, 1);
2988 * for ASCII protocol, this function build the get command
2989 * string and send the command.
2991 * @param c, pointer of the concurrency
2992 * @param item, pointer of task item which includes the object
2995 * @return int, if success, return 0, else return -1
2997 static int ms_build_ascii_write_buf_get(ms_conn_t
*c
, ms_task_item_t
*item
)
2999 if ((ms_add_iov(c
, "get ", 4) != 0)
3000 || (ms_add_iov(c
, (char *)&item
->key_prefix
,
3001 (int)KEY_PREFIX_SIZE
) != 0)
3002 || (ms_add_iov(c
, &ms_setting
.char_block
[item
->key_suffix_offset
],
3003 item
->key_size
- (int)KEY_PREFIX_SIZE
) != 0)
3004 || (ms_add_iov(c
, "\r\n", 2) != 0)
3005 || (c
->udp
&& (ms_build_udp_headers(c
) != 0)))
3011 } /* ms_build_ascii_write_buf_get */
3015 * used to send the get command to server
3017 * @param c, pointer of the concurrency
3018 * @param item, pointer of task item which includes the object
3020 * @param verify, whether do verification
3022 * @return int, if success, return 0, else return -1
3024 int ms_mcd_get(ms_conn_t
*c
, ms_task_item_t
*item
, bool verify
)
3026 /* verify not supported yet */
3027 UNUSED_ARGUMENT(verify
);
3031 c
->currcmd
.cmd
= CMD_GET
;
3032 c
->currcmd
.isfinish
= false;
3033 c
->currcmd
.retstat
= MCD_FAILURE
;
3035 if (ms_update_conn_sock_event(c
) != 0)
3043 if (ms_add_msghdr(c
) != 0)
3045 fprintf(stderr
, "Out of memory preparing request.");
3049 /* binary protocol */
3050 if (c
->protocol
== binary_prot
)
3052 if (ms_build_bin_write_buf_get(c
, item
) != 0)
3059 if (ms_build_ascii_write_buf_get(c
, item
) != 0)
3065 atomic_add_64(&ms_stats
.cmd_get
, 1);
3072 * for ASCII protocol, this function build the multi-get command
3073 * string and send the command.
3075 * @param c, pointer of the concurrency
3077 * @return int, if success, return 0, else return -1
3079 static int ms_build_ascii_write_buf_mlget(ms_conn_t
*c
)
3081 ms_task_item_t
*item
;
3083 if (ms_add_iov(c
, "get", 3) != 0)
3088 for (int i
= 0; i
< c
->mlget_task
.mlget_num
; i
++)
3090 item
= c
->mlget_task
.mlget_item
[i
].item
;
3091 assert(item
!= NULL
);
3092 if ((ms_add_iov(c
, " ", 1) != 0)
3093 || (ms_add_iov(c
, (char *)&item
->key_prefix
,
3094 (int)KEY_PREFIX_SIZE
) != 0)
3095 || (ms_add_iov(c
, &ms_setting
.char_block
[item
->key_suffix_offset
],
3096 item
->key_size
- (int)KEY_PREFIX_SIZE
) != 0))
3102 if ((ms_add_iov(c
, "\r\n", 2) != 0)
3103 || (c
->udp
&& (ms_build_udp_headers(c
) != 0)))
3109 } /* ms_build_ascii_write_buf_mlget */
3113 * used to send the multi-get command to server
3115 * @param c, pointer of the concurrency
3117 * @return int, if success, return 0, else return -1
3119 int ms_mcd_mlget(ms_conn_t
*c
)
3121 ms_task_item_t
*item
;
3124 assert(c
->mlget_task
.mlget_num
>= 1);
3126 c
->currcmd
.cmd
= CMD_GET
;
3127 c
->currcmd
.isfinish
= false;
3128 c
->currcmd
.retstat
= MCD_FAILURE
;
3130 if (ms_update_conn_sock_event(c
) != 0)
3138 if (ms_add_msghdr(c
) != 0)
3140 fprintf(stderr
, "Out of memory preparing request.");
3144 /* binary protocol */
3145 if (c
->protocol
== binary_prot
)
3147 if (ms_build_bin_write_buf_mlget(c
) != 0)
3154 if (ms_build_ascii_write_buf_mlget(c
) != 0)
3160 /* decrease operation time of each item */
3161 for (int i
= 0; i
< c
->mlget_task
.mlget_num
; i
++)
3163 item
= c
->mlget_task
.mlget_item
[i
].item
;
3164 atomic_add_64(&ms_stats
.cmd_get
, 1);
3168 } /* ms_mcd_mlget */
3172 * binary protocol support
3176 * for binary protocol, parse the response of server
3178 * @param c, pointer of the concurrency
3180 * @return int, if success, return 0, else return -1
3182 static int ms_bin_process_response(ms_conn_t
*c
)
3184 const char *errstr
= NULL
;
3188 uint32_t bodylen
= c
->binary_header
.response
.bodylen
;
3189 uint8_t opcode
= c
->binary_header
.response
.opcode
;
3190 uint16_t status
= c
->binary_header
.response
.status
;
3194 c
->rvbytes
= (int32_t)bodylen
;
3202 case PROTOCOL_BINARY_RESPONSE_SUCCESS
:
3203 if (opcode
== PROTOCOL_BINARY_CMD_SET
)
3205 c
->currcmd
.retstat
= MCD_STORED
;
3207 else if (opcode
== PROTOCOL_BINARY_CMD_DELETE
)
3209 c
->currcmd
.retstat
= MCD_DELETED
;
3211 else if (opcode
== PROTOCOL_BINARY_CMD_GET
)
3213 c
->currcmd
.retstat
= MCD_END
;
3217 case PROTOCOL_BINARY_RESPONSE_ENOMEM
:
3218 errstr
= "Out of memory";
3219 c
->currcmd
.retstat
= MCD_SERVER_ERROR
;
3222 case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
:
3223 errstr
= "Unknown command";
3224 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
3227 case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT
:
3228 errstr
= "Not found";
3229 c
->currcmd
.retstat
= MCD_NOTFOUND
;
3232 case PROTOCOL_BINARY_RESPONSE_EINVAL
:
3233 errstr
= "Invalid arguments";
3234 c
->currcmd
.retstat
= MCD_PROTOCOL_ERROR
;
3237 case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS
:
3238 errstr
= "Data exists for key.";
3241 case PROTOCOL_BINARY_RESPONSE_E2BIG
:
3242 errstr
= "Too large.";
3243 c
->currcmd
.retstat
= MCD_SERVER_ERROR
;
3246 case PROTOCOL_BINARY_RESPONSE_NOT_STORED
:
3247 errstr
= "Not stored.";
3248 c
->currcmd
.retstat
= MCD_NOTSTORED
;
3252 errstr
= "Unknown error";
3253 c
->currcmd
.retstat
= MCD_UNKNOWN_READ_FAILURE
;
3259 fprintf(stderr
, "%s\n", errstr
);
3264 } /* ms_bin_process_response */
3267 /* build binary header and add the header to the buffer to send */
3270 * build binary header and add the header to the buffer to send
3272 * @param c, pointer of the concurrency
3273 * @param opcode, operation code
3274 * @param hdr_len, length of header
3275 * @param key_len, length of key
3276 * @param body_len. length of body
3278 static void ms_add_bin_header(ms_conn_t
*c
,
3284 protocol_binary_request_header
*header
;
3288 header
= (protocol_binary_request_header
*)c
->wcurr
;
3290 header
->request
.magic
= (uint8_t)PROTOCOL_BINARY_REQ
;
3291 header
->request
.opcode
= (uint8_t)opcode
;
3292 header
->request
.keylen
= htonl(key_len
);
3294 header
->request
.extlen
= (uint8_t)hdr_len
;
3295 header
->request
.datatype
= (uint8_t)PROTOCOL_BINARY_RAW_BYTES
;
3296 header
->request
.reserved
= 0;
3298 header
->request
.bodylen
= htonl(body_len
);
3299 header
->request
.opaque
= 0;
3300 header
->request
.cas
= 0;
3302 ms_add_iov(c
, c
->wcurr
, sizeof(header
->request
));
3303 } /* ms_add_bin_header */
3307 * add the key to the socket write buffer array
3309 * @param c, pointer of the concurrency
3310 * @param item, pointer of task item which includes the object
3313 static void ms_add_key_to_iov(ms_conn_t
*c
, ms_task_item_t
*item
)
3315 ms_add_iov(c
, (char *)&item
->key_prefix
, (int)KEY_PREFIX_SIZE
);
3316 ms_add_iov(c
, &ms_setting
.char_block
[item
->key_suffix_offset
],
3317 item
->key_size
- (int)KEY_PREFIX_SIZE
);
3322 * for binary protocol, this function build the set command
3323 * and add the command to send buffer array.
3325 * @param c, pointer of the concurrency
3326 * @param item, pointer of task item which includes the object
3329 * @return int, if success, return 0, else return -1
3331 static int ms_build_bin_write_buf_set(ms_conn_t
*c
, ms_task_item_t
*item
)
3333 assert(c
->wbuf
== c
->wcurr
);
3336 protocol_binary_request_set
*rep
= (protocol_binary_request_set
*)c
->wcurr
;
3337 uint16_t keylen
= (uint16_t)item
->key_size
;
3338 uint32_t bodylen
= (uint32_t)sizeof(rep
->message
.body
)
3339 + (uint32_t)keylen
+ (uint32_t)item
->value_size
;
3341 ms_add_bin_header(c
,
3342 PROTOCOL_BINARY_CMD_SET
,
3343 sizeof(rep
->message
.body
),
3346 rep
->message
.body
.flags
= 0;
3347 rep
->message
.body
.expiration
= htonl((uint32_t)item
->exp_time
);
3348 ms_add_iov(c
, &rep
->message
.body
, sizeof(rep
->message
.body
));
3349 ms_add_key_to_iov(c
, item
);
3351 if (item
->value_offset
== INVALID_OFFSET
)
3353 value_offset
= item
->key_suffix_offset
;
3357 value_offset
= item
->value_offset
;
3359 ms_add_iov(c
, &ms_setting
.char_block
[value_offset
], item
->value_size
);
3362 } /* ms_build_bin_write_buf_set */
3366 * for binary protocol, this function build the get command and
3367 * add the command to send buffer array.
3369 * @param c, pointer of the concurrency
3370 * @param item, pointer of task item which includes the object
3373 * @return int, if success, return 0, else return -1
3375 static int ms_build_bin_write_buf_get(ms_conn_t
*c
, ms_task_item_t
*item
)
3377 assert(c
->wbuf
== c
->wcurr
);
3379 ms_add_bin_header(c
, PROTOCOL_BINARY_CMD_GET
, 0, (uint16_t)item
->key_size
,
3380 (uint32_t)item
->key_size
);
3381 ms_add_key_to_iov(c
, item
);
3384 } /* ms_build_bin_write_buf_get */
3388 * for binary protocol, this function build the multi-get
3389 * command and add the command to send buffer array.
3391 * @param c, pointer of the concurrency
3392 * @param item, pointer of task item which includes the object
3395 * @return int, if success, return 0, else return -1
3397 static int ms_build_bin_write_buf_mlget(ms_conn_t
*c
)
3399 ms_task_item_t
*item
;
3401 assert(c
->wbuf
== c
->wcurr
);
3403 for (int i
= 0; i
< c
->mlget_task
.mlget_num
; i
++)
3405 item
= c
->mlget_task
.mlget_item
[i
].item
;
3406 assert(item
!= NULL
);
3408 ms_add_bin_header(c
,
3409 PROTOCOL_BINARY_CMD_GET
,
3411 (uint16_t)item
->key_size
,
3412 (uint32_t)item
->key_size
);
3413 ms_add_key_to_iov(c
, item
);
3414 c
->wcurr
+= sizeof(protocol_binary_request_get
);
3420 } /* ms_build_bin_write_buf_mlget */