Better reporting on errors from mget.
[awesomized/libmemcached] / libmemcached / get.c
1 /* LibMemcached
2 * Copyright (C) 2006-2009 Brian Aker
3 * All rights reserved.
4 *
5 * Use and distribution licensed under the BSD license. See
6 * the COPYING file in the parent directory for full text.
7 *
8 * Summary: Get functions for libmemcached
9 *
10 */
11
12 #include "common.h"
13
14 /*
15 What happens if no servers exist?
16 */
17 char *memcached_get(memcached_st *ptr, const char *key,
18 size_t key_length,
19 size_t *value_length,
20 uint32_t *flags,
21 memcached_return_t *error)
22 {
23 return memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length,
24 flags, error);
25 }
26
27 static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr,
28 const char *master_key,
29 size_t master_key_length,
30 const char * const *keys,
31 const size_t *key_length,
32 size_t number_of_keys,
33 bool mget_mode);
34
35 char *memcached_get_by_key(memcached_st *ptr,
36 const char *master_key,
37 size_t master_key_length,
38 const char *key, size_t key_length,
39 size_t *value_length,
40 uint32_t *flags,
41 memcached_return_t *error)
42 {
43 char *value;
44 size_t dummy_length;
45 uint32_t dummy_flags;
46 memcached_return_t dummy_error;
47
48 unlikely (ptr->flags.use_udp)
49 {
50 *error= MEMCACHED_NOT_SUPPORTED;
51 return NULL;
52 }
53
54 /* Request the key */
55 *error= memcached_mget_by_key_real(ptr, master_key, master_key_length,
56 (const char * const *)&key,
57 &key_length, 1, false);
58
59 value= memcached_fetch(ptr, NULL, NULL,
60 value_length, flags, error);
61 /* This is for historical reasons */
62 if (*error == MEMCACHED_END)
63 *error= MEMCACHED_NOTFOUND;
64
65 if (value == NULL)
66 {
67 if (ptr->get_key_failure && *error == MEMCACHED_NOTFOUND)
68 {
69 memcached_return_t rc;
70
71 memcached_result_reset(&ptr->result);
72 rc= ptr->get_key_failure(ptr, key, key_length, &ptr->result);
73
74 /* On all failure drop to returning NULL */
75 if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED)
76 {
77 if (rc == MEMCACHED_BUFFERED)
78 {
79 uint64_t latch; /* We use latch to track the state of the original socket */
80 latch= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS);
81 if (latch == 0)
82 memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 1);
83
84 rc= memcached_set(ptr, key, key_length,
85 (memcached_result_value(&ptr->result)),
86 (memcached_result_length(&ptr->result)),
87 0,
88 (memcached_result_flags(&ptr->result)));
89
90 if (rc == MEMCACHED_BUFFERED && latch == 0)
91 memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0);
92 }
93 else
94 {
95 rc= memcached_set(ptr, key, key_length,
96 (memcached_result_value(&ptr->result)),
97 (memcached_result_length(&ptr->result)),
98 0,
99 (memcached_result_flags(&ptr->result)));
100 }
101
102 if (rc == MEMCACHED_SUCCESS || rc == MEMCACHED_BUFFERED)
103 {
104 *error= rc;
105 *value_length= memcached_result_length(&ptr->result);
106 *flags= memcached_result_flags(&ptr->result);
107 return memcached_string_c_copy(&ptr->result.value);
108 }
109 }
110 }
111
112 return NULL;
113 }
114
115 (void)memcached_fetch(ptr, NULL, NULL,
116 &dummy_length, &dummy_flags,
117 &dummy_error);
118 WATCHPOINT_ASSERT(dummy_length == 0);
119
120 return value;
121 }
122
123 memcached_return_t memcached_mget(memcached_st *ptr,
124 const char * const *keys,
125 const size_t *key_length,
126 size_t number_of_keys)
127 {
128 return memcached_mget_by_key(ptr, NULL, 0, keys, key_length, number_of_keys);
129 }
130
131 static memcached_return_t binary_mget_by_key(memcached_st *ptr,
132 uint32_t master_server_key,
133 bool is_master_key_set,
134 const char * const *keys,
135 const size_t *key_length,
136 size_t number_of_keys,
137 bool mget_mode);
138
139 static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr,
140 const char *master_key,
141 size_t master_key_length,
142 const char * const *keys,
143 const size_t *key_length,
144 size_t number_of_keys,
145 bool mget_mode)
146 {
147 bool failures_occured_in_sending= false;
148 const char *get_command= "get ";
149 uint8_t get_command_length= 4;
150 unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */
151 bool is_master_key_set= false;
152
153 unlikely (ptr->flags.use_udp)
154 return MEMCACHED_NOT_SUPPORTED;
155
156 LIBMEMCACHED_MEMCACHED_MGET_START();
157
158 if (number_of_keys == 0)
159 return MEMCACHED_NOTFOUND;
160
161 if (memcached_server_count(ptr) == 0)
162 return MEMCACHED_NO_SERVERS;
163
164 if (ptr->flags.verify_key && (memcached_key_test(keys, key_length, number_of_keys) == MEMCACHED_BAD_KEY_PROVIDED))
165 return MEMCACHED_BAD_KEY_PROVIDED;
166
167 if (master_key && master_key_length)
168 {
169 if (ptr->flags.verify_key && (memcached_key_test((const char * const *)&master_key, &master_key_length, 1) == MEMCACHED_BAD_KEY_PROVIDED))
170 return MEMCACHED_BAD_KEY_PROVIDED;
171 master_server_key= memcached_generate_hash_with_redistribution(ptr, master_key, master_key_length);
172 is_master_key_set= true;
173 }
174
175 /*
176 Here is where we pay for the non-block API. We need to remove any data sitting
177 in the queue before we start our get.
178
179 It might be optimum to bounce the connection if count > some number.
180 */
181 for (uint32_t x= 0; x < memcached_server_count(ptr); x++)
182 {
183 memcached_server_write_instance_st instance=
184 memcached_server_instance_fetch(ptr, x);
185
186 if (memcached_server_response_count(instance))
187 {
188 char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
189
190 if (ptr->flags.no_block)
191 (void)memcached_io_write(instance, NULL, 0, true);
192
193 while(memcached_server_response_count(instance))
194 (void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result);
195 }
196 }
197
198 if (ptr->flags.binary_protocol)
199 {
200 return binary_mget_by_key(ptr, master_server_key, is_master_key_set, keys,
201 key_length, number_of_keys, mget_mode);
202 }
203
204 if (ptr->flags.support_cas)
205 {
206 get_command= "gets ";
207 get_command_length= 5;
208 }
209
210 /*
211 If a server fails we warn about errors and start all over with sending keys
212 to the server.
213 */
214 memcached_return_t rc= MEMCACHED_SUCCESS;
215 size_t hosts_connected= 0;
216 for (uint32_t x= 0; x < number_of_keys; x++)
217 {
218 memcached_server_write_instance_st instance;
219 uint32_t server_key;
220
221 if (is_master_key_set)
222 {
223 server_key= master_server_key;
224 }
225 else
226 {
227 server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]);
228 }
229
230 instance= memcached_server_instance_fetch(ptr, server_key);
231
232 struct libmemcached_io_vector_st vector[]=
233 {
234 { .length= get_command_length, .buffer= get_command },
235 { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
236 { .length= key_length[x], .buffer= keys[x] },
237 { .length= 1, .buffer= " " }
238 };
239
240
241 if (memcached_server_response_count(instance) == 0)
242 {
243 rc= memcached_connect(instance);
244
245 if (rc != MEMCACHED_SUCCESS)
246 {
247 continue;
248 }
249 hosts_connected++;
250
251 if ((memcached_io_writev(instance, vector, 4, false)) == -1)
252 {
253 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
254 failures_occured_in_sending= true;
255 continue;
256 }
257 WATCHPOINT_ASSERT(instance->cursor_active == 0);
258 memcached_server_response_increment(instance);
259 WATCHPOINT_ASSERT(instance->cursor_active == 1);
260 }
261 else
262 {
263 if ((memcached_io_writev(instance, (vector + 1), 3, false)) == -1)
264 {
265 memcached_server_response_reset(instance);
266 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
267 failures_occured_in_sending= true;
268 continue;
269 }
270 }
271 }
272
273 if (hosts_connected == 0)
274 {
275 LIBMEMCACHED_MEMCACHED_MGET_END();
276
277 if (rc != MEMCACHED_SUCCESS)
278 return rc;
279
280 return MEMCACHED_NO_SERVERS;
281 }
282
283
284 /*
285 Should we muddle on if some servers are dead?
286 */
287 bool success_happened= false;
288 for (uint32_t x= 0; x < memcached_server_count(ptr); x++)
289 {
290 memcached_server_write_instance_st instance=
291 memcached_server_instance_fetch(ptr, x);
292
293 if (memcached_server_response_count(instance))
294 {
295 /* We need to do something about non-connnected hosts in the future */
296 if ((memcached_io_write(instance, "\r\n", 2, true)) == -1)
297 {
298 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
299 failures_occured_in_sending= true;
300 }
301 else
302 {
303 success_happened= true;
304 }
305 }
306 }
307
308 LIBMEMCACHED_MEMCACHED_MGET_END();
309
310 if (failures_occured_in_sending && success_happened)
311 return MEMCACHED_SOME_ERRORS;
312
313 if (success_happened)
314 return MEMCACHED_SUCCESS;
315
316 return MEMCACHED_FAILURE;
317 }
318
319 memcached_return_t memcached_mget_by_key(memcached_st *ptr,
320 const char *master_key,
321 size_t master_key_length,
322 const char * const *keys,
323 const size_t *key_length,
324 size_t number_of_keys)
325 {
326 return memcached_mget_by_key_real(ptr, master_key, master_key_length, keys,
327 key_length, number_of_keys, true);
328 }
329
330 memcached_return_t memcached_mget_execute(memcached_st *ptr,
331 const char * const *keys,
332 const size_t *key_length,
333 size_t number_of_keys,
334 memcached_execute_fn *callback,
335 void *context,
336 unsigned int number_of_callbacks)
337 {
338 return memcached_mget_execute_by_key(ptr, NULL, 0, keys, key_length,
339 number_of_keys, callback,
340 context, number_of_callbacks);
341 }
342
343 memcached_return_t memcached_mget_execute_by_key(memcached_st *ptr,
344 const char *master_key,
345 size_t master_key_length,
346 const char * const *keys,
347 const size_t *key_length,
348 size_t number_of_keys,
349 memcached_execute_fn *callback,
350 void *context,
351 unsigned int number_of_callbacks)
352 {
353 if ((ptr->flags.binary_protocol) == 0)
354 return MEMCACHED_NOT_SUPPORTED;
355
356 memcached_return_t rc;
357 memcached_callback_st *original_callbacks= ptr->callbacks;
358 memcached_callback_st cb= {
359 .callback= callback,
360 .context= context,
361 .number_of_callback= number_of_callbacks
362 };
363
364 ptr->callbacks= &cb;
365 rc= memcached_mget_by_key(ptr, master_key, master_key_length, keys,
366 key_length, number_of_keys);
367 ptr->callbacks= original_callbacks;
368 return rc;
369 }
370
371 static memcached_return_t simple_binary_mget(memcached_st *ptr,
372 uint32_t master_server_key,
373 bool is_master_key_set,
374 const char * const *keys,
375 const size_t *key_length,
376 size_t number_of_keys, bool mget_mode)
377 {
378 memcached_return_t rc= MEMCACHED_NOTFOUND;
379
380 int flush= number_of_keys == 1;
381
382 /*
383 If a server fails we warn about errors and start all over with sending keys
384 to the server.
385 */
386 for (uint32_t x= 0; x < number_of_keys; ++x)
387 {
388 uint32_t server_key;
389 memcached_server_write_instance_st instance;
390
391 if (is_master_key_set)
392 {
393 server_key= master_server_key;
394 }
395 else
396 {
397 server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]);
398 }
399
400 instance= memcached_server_instance_fetch(ptr, server_key);
401
402 if (memcached_server_response_count(instance) == 0)
403 {
404 rc= memcached_connect(instance);
405 if (rc != MEMCACHED_SUCCESS)
406 continue;
407 }
408
409 protocol_binary_request_getk request= {.bytes= {0}};
410 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
411 if (mget_mode)
412 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETKQ;
413 else
414 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_GETK;
415
416 memcached_return_t vk;
417 vk= memcached_validate_key_length(key_length[x],
418 ptr->flags.binary_protocol);
419 unlikely (vk != MEMCACHED_SUCCESS)
420 {
421 if (x > 0)
422 {
423 memcached_io_reset(instance);
424 }
425
426 return vk;
427 }
428
429 request.message.header.request.keylen= htons((uint16_t)(key_length[x] + ptr->prefix_key_length));
430 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
431 request.message.header.request.bodylen= htonl((uint32_t)( key_length[x] + ptr->prefix_key_length));
432
433 struct libmemcached_io_vector_st vector[]=
434 {
435 { .length= sizeof(request.bytes), .buffer= request.bytes },
436 { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
437 { .length= key_length[x], .buffer= keys[x] }
438 };
439
440 if (memcached_io_writev(instance, vector, 3, flush) == -1)
441 {
442 memcached_server_response_reset(instance);
443 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
444 rc= MEMCACHED_SOME_ERRORS;
445 continue;
446 }
447
448 /* We just want one pending response per server */
449 memcached_server_response_reset(instance);
450 memcached_server_response_increment(instance);
451 if ((x > 0 && x == ptr->io_key_prefetch) && memcached_flush_buffers(ptr) != MEMCACHED_SUCCESS)
452 {
453 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
454 rc= MEMCACHED_SOME_ERRORS;
455 }
456 }
457
458 if (mget_mode)
459 {
460 /*
461 * Send a noop command to flush the buffers
462 */
463 protocol_binary_request_noop request= {.bytes= {0}};
464 request.message.header.request.magic= PROTOCOL_BINARY_REQ;
465 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_NOOP;
466 request.message.header.request.datatype= PROTOCOL_BINARY_RAW_BYTES;
467
468 for (uint32_t x= 0; x < memcached_server_count(ptr); ++x)
469 {
470 memcached_server_write_instance_st instance=
471 memcached_server_instance_fetch(ptr, x);
472
473 if (memcached_server_response_count(instance))
474 {
475 if (memcached_io_write(instance, NULL, 0, true) == -1)
476 {
477 memcached_server_response_reset(instance);
478 memcached_io_reset(instance);
479 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
480 rc= MEMCACHED_SOME_ERRORS;
481 }
482
483 if (memcached_io_write(instance, request.bytes,
484 sizeof(request.bytes), true) == -1)
485 {
486 memcached_server_response_reset(instance);
487 memcached_io_reset(instance);
488 fprintf(stderr, "%s:%d (%s)\n", __FILE__, __LINE__,__func__);fflush(stdout);
489 rc= MEMCACHED_SOME_ERRORS;
490 }
491 }
492 }
493 }
494
495
496 return rc;
497 }
498
499 static memcached_return_t replication_binary_mget(memcached_st *ptr,
500 uint32_t* hash,
501 bool* dead_servers,
502 const char *const *keys,
503 const size_t *key_length,
504 size_t number_of_keys)
505 {
506 memcached_return_t rc= MEMCACHED_NOTFOUND;
507 uint32_t start= 0;
508 uint64_t randomize_read= memcached_behavior_get(ptr, MEMCACHED_BEHAVIOR_RANDOMIZE_REPLICA_READ);
509
510 if (randomize_read)
511 start= (uint32_t)random() % (uint32_t)(ptr->number_of_replicas + 1);
512
513 /* Loop for each replica */
514 for (uint32_t replica= 0; replica <= ptr->number_of_replicas; ++replica)
515 {
516 bool success= true;
517
518 for (uint32_t x= 0; x < number_of_keys; ++x)
519 {
520 memcached_server_write_instance_st instance;
521
522 if (hash[x] == memcached_server_count(ptr))
523 continue; /* Already successfully sent */
524
525 uint32_t server= hash[x] + replica;
526
527 /* In case of randomized reads */
528 if (randomize_read && ((server + start) <= (hash[x] + ptr->number_of_replicas)))
529 server += start;
530
531 while (server >= memcached_server_count(ptr))
532 server -= memcached_server_count(ptr);
533
534 if (dead_servers[server])
535 continue;
536
537 instance= memcached_server_instance_fetch(ptr, server);
538
539 if (memcached_server_response_count(instance) == 0)
540 {
541 rc= memcached_connect(instance);
542 if (rc != MEMCACHED_SUCCESS)
543 {
544 memcached_io_reset(instance);
545 dead_servers[server]= true;
546 success= false;
547 continue;
548 }
549 }
550
551 protocol_binary_request_getk request= {
552 .message.header.request= {
553 .magic= PROTOCOL_BINARY_REQ,
554 .opcode= PROTOCOL_BINARY_CMD_GETK,
555 .keylen= htons((uint16_t)(key_length[x] + ptr->prefix_key_length)),
556 .datatype= PROTOCOL_BINARY_RAW_BYTES,
557 .bodylen= htonl((uint32_t)(key_length[x] + ptr->prefix_key_length))
558 }
559 };
560
561 /*
562 * We need to disable buffering to actually know that the request was
563 * successfully sent to the server (so that we should expect a result
564 * back). It would be nice to do this in buffered mode, but then it
565 * would be complex to handle all error situations if we got to send
566 * some of the messages, and then we failed on writing out some others
567 * and we used the callback interface from memcached_mget_execute so
568 * that we might have processed some of the responses etc. For now,
569 * just make sure we work _correctly_
570 */
571 struct libmemcached_io_vector_st vector[]=
572 {
573 { .length= sizeof(request.bytes), .buffer= request.bytes },
574 { .length= ptr->prefix_key_length, .buffer= ptr->prefix_key },
575 { .length= key_length[x], .buffer= keys[x] }
576 };
577
578 if (memcached_io_writev(instance, vector, 3, true) == -1)
579 {
580 memcached_io_reset(instance);
581 dead_servers[server]= true;
582 success= false;
583 continue;
584 }
585
586 memcached_server_response_increment(instance);
587 hash[x]= memcached_server_count(ptr);
588 }
589
590 if (success)
591 break;
592 }
593
594 return rc;
595 }
596
597 static memcached_return_t binary_mget_by_key(memcached_st *ptr,
598 uint32_t master_server_key,
599 bool is_master_key_set,
600 const char * const *keys,
601 const size_t *key_length,
602 size_t number_of_keys,
603 bool mget_mode)
604 {
605 memcached_return_t rc;
606
607 if (ptr->number_of_replicas == 0)
608 {
609 rc= simple_binary_mget(ptr, master_server_key, is_master_key_set,
610 keys, key_length, number_of_keys, mget_mode);
611 }
612 else
613 {
614 uint32_t* hash;
615 bool* dead_servers;
616
617 hash= libmemcached_malloc(ptr, sizeof(uint32_t) * number_of_keys);
618 dead_servers= libmemcached_calloc(ptr, memcached_server_count(ptr), sizeof(bool));
619
620 if (hash == NULL || dead_servers == NULL)
621 {
622 libmemcached_free(ptr, hash);
623 libmemcached_free(ptr, dead_servers);
624 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
625 }
626
627 if (is_master_key_set)
628 {
629 for (size_t x= 0; x < number_of_keys; x++)
630 {
631 hash[x]= master_server_key;
632 }
633 }
634 else
635 {
636 for (size_t x= 0; x < number_of_keys; x++)
637 {
638 hash[x]= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]);
639 }
640 }
641
642 rc= replication_binary_mget(ptr, hash, dead_servers, keys,
643 key_length, number_of_keys);
644
645 libmemcached_free(ptr, hash);
646 libmemcached_free(ptr, dead_servers);
647
648 return MEMCACHED_SUCCESS;
649 }
650
651 return rc;
652 }