From 9f64858beaf5871f01fa35d6e8e0ac1d06d2c5ae Mon Sep 17 00:00:00 2001 From: uhm0311 Date: Thu, 8 Aug 2024 19:04:23 +0900 Subject: [PATCH] FEATURE: Add mset API. --- libmemcached/fetch.cc | 40 ++++++ libmemcached/libmemcached_probes.h | 6 +- libmemcached/storage.cc | 168 ++++++++++++++++++++++- libmemcached/storage.h | 30 ++++- libmemcached/types.h | 3 +- tests/include.am | 12 +- tests/storage | 210 +++++++++++++++++++++++++++++ tests/storage.cc | 187 +++++++++++++++++++++++++ tests/storage.h | 15 +++ 9 files changed, 661 insertions(+), 10 deletions(-) create mode 100755 tests/storage create mode 100644 tests/storage.cc create mode 100644 tests/storage.h diff --git a/libmemcached/fetch.cc b/libmemcached/fetch.cc index aa022ee2..0d98a61e 100644 --- a/libmemcached/fetch.cc +++ b/libmemcached/fetch.cc @@ -53,6 +53,46 @@ #include +memcached_return_t memcached_fetch_storage_result(memcached_st *ptr, + const char *key, const size_t key_length) +{ + unlikely (not ptr or not key) + { + return MEMCACHED_INVALID_ARGUMENTS; + } + unlikely (ptr->flags.use_udp) + { + return MEMCACHED_NOT_SUPPORTED; + } + + uint32_t server_key= memcached_generate_hash_with_redistribution(ptr, key, key_length); + memcached_server_write_instance_st instance= memcached_server_instance_fetch(ptr, server_key); + +#ifdef ENABLE_REPLICATION +do_action: +#endif + char result[MEMCACHED_DEFAULT_COMMAND_SIZE]; + memcached_return_t rc= memcached_read_one_response(instance, result, MEMCACHED_DEFAULT_COMMAND_SIZE, NULL); + + if (rc == MEMCACHED_STORED) + { + rc= MEMCACHED_SUCCESS; + } +#ifdef ENABLE_REPLICATION + else if (rc == MEMCACHED_SWITCHOVER or rc == MEMCACHED_REPL_SLAVE) + { + ZOO_LOG_INFO(("Switchover: hostname=%s port=%d error=%s", + instance->hostname, instance->port, memcached_strerror(ptr, rc))); + if (memcached_rgroup_switchover(ptr, instance) == true) { + instance= memcached_server_instance_fetch(ptr, server_key); + goto do_action; + } + } +#endif + + return rc; +} + char *memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, size_t *value_length, uint32_t *flags, diff --git a/libmemcached/libmemcached_probes.h b/libmemcached/libmemcached_probes.h index b819f78a..fcca3a44 100644 --- a/libmemcached/libmemcached_probes.h +++ b/libmemcached/libmemcached_probes.h @@ -1,5 +1,5 @@ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * + * * Libmemcached library * * Copyright (C) 2011 Data Differential, http://datadifferential.com/ @@ -111,6 +111,10 @@ #define LIBMEMCACHED_MEMCACHED_SET_END_ENABLED() (0) #define LIBMEMCACHED_MEMCACHED_SET_START() #define LIBMEMCACHED_MEMCACHED_SET_START_ENABLED() (0) +#define LIBMEMCACHED_MEMCACHED_MSET_END() +#define LIBMEMCACHED_MEMCACHED_MSET_END_ENABLED() (0) +#define LIBMEMCACHED_MEMCACHED_MSET_START() +#define LIBMEMCACHED_MEMCACHED_MSET_START_ENABLED() (0) #endif diff --git a/libmemcached/storage.cc b/libmemcached/storage.cc index bce3425e..73b05a06 100644 --- a/libmemcached/storage.cc +++ b/libmemcached/storage.cc @@ -38,6 +38,11 @@ enum memcached_storage_action_t { CAS_OP }; +enum memcached_storage_type_t { + SINGLE, + MULTI +}; + /* Inline this */ static inline const char *storage_op_string(memcached_storage_action_t verb) { @@ -121,6 +126,67 @@ static inline uint8_t get_com_code(memcached_storage_action_t verb, bool noreply return ret; } +static memcached_return_t before_storage_query(memcached_st *ptr, + const char *group_key, size_t group_key_length, + const memcached_storage_query_st* queries, + const size_t number_of_queries) +{ + memcached_return_t rc= initialize_query(ptr); + if (memcached_failed(rc)) + { + return rc; + } + + if (ptr->flags.use_udp) + { + return memcached_set_error(*ptr, MEMCACHED_NOT_SUPPORTED, MEMCACHED_AT); + } + + if (queries == NULL) + { + return memcached_set_error(*ptr, MEMCACHED_NOTFOUND, MEMCACHED_AT, + memcached_literal_param("queries were null")); + } + if (number_of_queries == 0) + { + return memcached_set_error(*ptr, MEMCACHED_NOTFOUND, MEMCACHED_AT, + memcached_literal_param("number_of_queries were zero")); + } + + if (group_key and group_key_length) + { + if (memcached_failed(memcached_key_test(*ptr, (const char * const *)&group_key, &group_key_length, 1))) + { + return memcached_set_error(*ptr, MEMCACHED_BAD_KEY_PROVIDED, MEMCACHED_AT, + memcached_literal_param("A bad group key was provided.")); + } + } + + /* + Here is where we pay for the non-block API. We need to remove any data sitting + in the queue before we start our store operations. + + It might be optimum to bounce the connection if count > some number. + */ + for (uint32_t x= 0; x < memcached_server_count(ptr); x++) + { + memcached_server_write_instance_st instance= + memcached_server_instance_fetch(ptr, x); + + if (memcached_server_response_count(instance)) + { + char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE]; + + if (ptr->flags.no_block) + (void)memcached_io_write(instance, NULL, 0, true); + + while(memcached_server_response_count(instance)) + (void)memcached_response(instance, buffer, sizeof(buffer), &ptr->result); + } + } + return MEMCACHED_SUCCESS; +} + static memcached_return_t memcached_send_binary(memcached_st *ptr, const char *group_key, size_t group_key_length, @@ -131,7 +197,8 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr, time_t expiration, uint32_t flags, uint64_t cas, - memcached_storage_action_t verb) + memcached_storage_action_t verb, + memcached_storage_type_t send_type) { bool flush; protocol_binary_request_set request= {}; @@ -227,7 +294,7 @@ static memcached_return_t memcached_send_binary(memcached_st *ptr, return MEMCACHED_BUFFERED; } - if (noreply) + if (send_type == MULTI || noreply) { return MEMCACHED_SUCCESS; } @@ -257,7 +324,8 @@ static memcached_return_t memcached_send_ascii(memcached_st *ptr, const time_t expiration, const uint32_t flags, const uint64_t cas, - memcached_storage_action_t verb) + memcached_storage_action_t verb, + memcached_storage_type_t send_type) { char flags_buffer[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH +1]; int flags_buffer_length= snprintf(flags_buffer, sizeof(flags_buffer), " %u", flags); @@ -349,7 +417,7 @@ static memcached_return_t memcached_send_ascii(memcached_st *ptr, if (rc == MEMCACHED_SUCCESS) { - if (ptr->flags.no_reply) + if (send_type == MULTI || ptr->flags.no_reply) { rc= (to_write == false) ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS; } @@ -415,19 +483,85 @@ static inline memcached_return_t memcached_send(memcached_st *ptr, rc= memcached_send_binary(ptr, group_key, group_key_length, key, key_length, value, value_length, expiration, - flags, cas, verb); + flags, cas, verb, SINGLE); } else { rc= memcached_send_ascii(ptr, group_key, group_key_length, key, key_length, value, value_length, expiration, - flags, cas, verb); + flags, cas, verb, SINGLE); } return rc; } +static memcached_return_t memcached_multi_send(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const memcached_storage_query_st* queries, + const size_t number_of_queries, + memcached_return_t *results, + memcached_storage_action_t verb) +{ + arcus_server_check_for_update(ptr); + + memcached_return_t rc; + if (memcached_failed(rc= before_storage_query(ptr, group_key, group_key_length, queries, number_of_queries))) + { + return rc; + } + + if (memcached_server_count(ptr) > MAX_SERVERS_FOR_MULTI_KEY_OPERATION) + { + return memcached_set_error(*ptr, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, + memcached_literal_param("memcached instances should be <= MAX_SERVERS_FOR_MULTI_KEY_OPERATION")); + } + + bool is_group_key_set= group_key and group_key_length; + + for (size_t i= 0; i < number_of_queries; i++) + { + if (memcached_failed(rc= memcached_validate_key_length(queries[i].key_length, ptr->flags.binary_protocol))) + { + results[i]= rc; + continue; + } + if (memcached_failed(rc= memcached_key_test(*ptr, &(queries[i].key), &(queries[i].key_length), 1))) + { + results[i]= rc; + continue; + } + if (verb == CAS_OP and queries[i].cas == 0) + { + results[i]= MEMCACHED_PROTOCOL_ERROR; + continue; + } + + char *safe_group_key= (is_group_key_set ? (char *)group_key : queries[i].key); + size_t safe_group_key_length= (is_group_key_set ? group_key_length : queries[i].key_length); + uint64_t safe_cas = verb == CAS_OP ? queries[i].cas : 0; + + if (ptr->flags.binary_protocol) + { + results[i]= memcached_send_binary(ptr, safe_group_key, safe_group_key_length, + queries[i].key, queries[i].key_length, + queries[i].value, queries[i].value_length, + queries[i].expiration, queries[i].flags, + safe_cas, verb, MULTI); + } + else + { + results[i]= memcached_send_ascii(ptr, safe_group_key, safe_group_key_length, + queries[i].key, queries[i].key_length, + queries[i].value, queries[i].value_length, + queries[i].expiration, queries[i].flags, + safe_cas, verb, MULTI); + } + } + + return MEMCACHED_SUCCESS; +} memcached_return_t memcached_set(memcached_st *ptr, const char *key, size_t key_length, const char *value, size_t value_length, @@ -605,3 +739,25 @@ memcached_return_t memcached_cas_by_key(memcached_st *ptr, return rc; } +memcached_return_t memcached_mset(memcached_st *ptr, + const memcached_storage_query_st* queries, + const size_t number_of_queries, + memcached_return_t *results) +{ + return memcached_mset_by_key(ptr, NULL, 0, queries, number_of_queries, results); +} + +memcached_return_t memcached_mset_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const memcached_storage_query_st* queries, + const size_t number_of_queries, + memcached_return_t *results) +{ + memcached_return_t rc; + LIBMEMCACHED_MEMCACHED_MSET_START(); + rc= memcached_multi_send(ptr, group_key, group_key_length, + queries, number_of_queries, results, SET_OP); + LIBMEMCACHED_MEMCACHED_MSET_END(); + return rc; +} diff --git a/libmemcached/storage.h b/libmemcached/storage.h index 215f4ebb..f6a09bff 100644 --- a/libmemcached/storage.h +++ b/libmemcached/storage.h @@ -1,5 +1,5 @@ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * + * * Libmemcached library * * Copyright (C) 2011 Data Differential, http://datadifferential.com/ @@ -40,6 +40,16 @@ #include "libmemcached/memcached.h" +struct memcached_storage_query_st { + char *key; + size_t key_length; + char *value; + size_t value_length; + time_t expiration; + uint32_t flags; + uint64_t cas; +}; + #ifdef __cplusplus extern "C" { #endif @@ -129,6 +139,24 @@ memcached_return_t memcached_cas_by_key(memcached_st *ptr, uint32_t flags, uint64_t cas); +LIBMEMCACHED_API +memcached_return_t memcached_mset(memcached_st *ptr, + const memcached_storage_query_st* queries, + const size_t number_of_queries, + memcached_return_t *results); + +LIBMEMCACHED_API +memcached_return_t memcached_mset_by_key(memcached_st *ptr, + const char *group_key, + size_t group_key_length, + const memcached_storage_query_st* queries, + const size_t number_of_queries, + memcached_return_t *results); + +LIBMEMCACHED_API +memcached_return_t memcached_fetch_storage_result(memcached_st *ptr, + const char *key, const size_t key_length); + #ifdef __cplusplus } #endif diff --git a/libmemcached/types.h b/libmemcached/types.h index cc8cf828..33d8a848 100644 --- a/libmemcached/types.h +++ b/libmemcached/types.h @@ -15,7 +15,7 @@ * limitations under the License. */ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: - * + * * Libmemcached library * * Copyright (C) 2011 Data Differential, http://datadifferential.com/ @@ -60,6 +60,7 @@ typedef struct memcached_stat_st memcached_stat_st; typedef struct memcached_analysis_st memcached_analysis_st; typedef struct memcached_result_st memcached_result_st; typedef struct memcached_array_st memcached_array_st; +typedef struct memcached_storage_query_st memcached_storage_query_st; typedef struct memcached_error_t memcached_error_t; // All of the flavors of memcache_server_st diff --git a/tests/include.am b/tests/include.am index 29b21027..a53df3da 100644 --- a/tests/include.am +++ b/tests/include.am @@ -65,6 +65,16 @@ tests_internals_LDADD+= libtest/libtest.la check_PROGRAMS+= tests/internals noinst_PROGRAMS+= tests/internals +# Test storage +tests_storage_SOURCES= tests/storage.cc +tests_storage_CXXFLAGS = $(AM_CXXFLAGS) ${PTHREAD_CFLAGS} +tests_storage_DEPENDENCIES= libmemcachedinternal/libmemcachedinternal.la libtest/libtest.la libmemcachedinternal/libmemcachedutilinternal.la +tests_storage_LDADD= libmemcachedinternal/libmemcachedinternal.la +tests_storage_LDADD+= ${PTHREAD_LIBS} +tests_storage_LDADD+= libmemcachedinternal/libmemcachedutilinternal.la +tests_storage_LDADD+= libtest/libtest.la +check_PROGRAMS+= tests/storage +noinst_PROGRAMS+= tests/storage tests_testapp_CXXFLAGS = $(AM_CXXFLAGS) ${PTHREAD_CFLAGS} tests_testapp_CFLAGS= $(AM_CFLAGS) $(NO_CONVERSION) $(NO_STRICT_ALIASING) @@ -239,7 +249,7 @@ tests_memdump_DEPENDENCIES= libtest/libtest.la $(TESTS_LDADDS) tests_memdump_LDADD= $(tests_memdump_DEPENDENCIES) check_PROGRAMS+= tests/memdump noinst_PROGRAMS+= tests/memdump - + # Test linking with C application tests_c_test_SOURCES= tests/c_test.c tests_c_test_CFLAGS= ${PTHREAD_CFLAGS} diff --git a/tests/storage b/tests/storage new file mode 100755 index 00000000..bed0ef00 --- /dev/null +++ b/tests/storage @@ -0,0 +1,210 @@ +#! /bin/sh + +# tests/storage - temporary wrapper script for .libs/storage +# Generated by libtool (GNU libtool) 2.4.7 +# +# The tests/storage program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.7' + notinst_deplibs=' /Users/uhm0311/Repositories/uhm0311/arcus-c-client/libmemcached/libmemcachedutil.la /Users/uhm0311/Repositories/uhm0311/arcus-c-client/libmemcached/libmemcached.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ./libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "storage:tests/storage:$LINENO: libtool wrapper (GNU libtool) 2.4.7" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "storage:tests/storage:$LINENO: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "storage:tests/storage:$LINENO: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program='storage' + progdir="$thisdir/.libs" + + + if test -f "$progdir/$program"; then + # Add our own library path to DYLD_LIBRARY_PATH + DYLD_LIBRARY_PATH="/Users/uhm0311/Repositories/uhm0311/arcus-c-client/libmemcached/.libs:$DYLD_LIBRARY_PATH" + + # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH + # The second colon is a workaround for a bug in BeOS R4 sed + DYLD_LIBRARY_PATH=`$ECHO "$DYLD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'` + + export DYLD_LIBRARY_PATH + + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/tests/storage.cc b/tests/storage.cc new file mode 100644 index 00000000..d95324c2 --- /dev/null +++ b/tests/storage.cc @@ -0,0 +1,187 @@ +#include +#include + +using namespace libtest; + +#include "tests/storage.h" + +#define MSET_COUNT 5 +#define BUFFER_SIZE 64 +#define EXPIRE_TIME 60 + +static void safe_free(char *c) +{ + if (c != NULL) + { + free(c); + } +} + +static void safe_free_queries(memcached_storage_query_st queries[MSET_COUNT]) +{ + for (int i= 0; i < MSET_COUNT; i++) + { + safe_free(queries[i].key); + safe_free(queries[i].value); + } +} + +test_return_t mset_and_get_test(void *) +{ + memcached_st *mc = memcached_create(NULL); + memcached_return_t rc= memcached_server_add(mc, "127.0.0.1", 11211); + printf("memcached_server_add: rc is %s\n", memcached_strerror(mc, rc)); + + if (memcached_failed(rc)) + { + memcached_free(mc); + return TEST_FAILURE; + } + + memcached_storage_query_st queries[MSET_COUNT]; + srand(time(NULL)); + + for (int i= 0; i < MSET_COUNT; i++) + { + char *key= (char *)malloc(BUFFER_SIZE); + if (not key) + { + printf("key cannot be allocated...\n"); + memcached_free(mc); + return TEST_FAILURE; + } + + char *value= (char *)malloc(BUFFER_SIZE); + if (not value) + { + printf("value cannot be allocated...\n"); + safe_free(key); + memcached_free(mc); + return TEST_FAILURE; + } + + memset(key, 0, BUFFER_SIZE); + sprintf(key, "MSET:mset-key-%d", i); + + memset(value, 0, BUFFER_SIZE); + sprintf(value, "mset-value-%d", i); + + queries[i].key= key; + queries[i].key_length= strlen(key); + + queries[i].value= value; + queries[i].value_length= strlen(value); + + queries[i].expiration= EXPIRE_TIME; + queries[i].flags= (uint32_t) rand(); + } + + memcached_return_t results[MSET_COUNT]; + rc= memcached_mset(mc, queries, MSET_COUNT, results); + printf("memcached_mset: rc is %s\n", memcached_strerror(mc, rc)); + + if (memcached_failed(rc)) + { + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + + for (int i= 0; i < MSET_COUNT; i++) + { + printf("memcached_mset: rc[%d] is %s\n", i, memcached_strerror(mc, results[i])); + + if (memcached_failed(results[i])) + { + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + } + + for (int i= 0; i < MSET_COUNT; i++) + { + rc= memcached_fetch_storage_result(mc, queries[i].key, queries[i].key_length); + printf("memcached_storage_fetch: rc[%d] is %s\n", i, memcached_strerror(mc, rc)); + + if (memcached_failed(rc)) + { + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + } + + for (int i= 0; i < MSET_COUNT; i++) + { + size_t value_length= -1; + uint32_t flags= -1; + + char *value= memcached_get(mc, queries[i].key, queries[i].key_length, &value_length, &flags, &rc); + printf("memcached_get: rc[%d] is %s\n", i, memcached_strerror(mc, rc)); + + if (value == NULL || memcached_failed(rc)) + { + safe_free(value); + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + + printf("memcached_get: flags[%d] is %u\n", i, flags); + printf("memcached_get: value[%d] is %s\n", i, value); + + if (queries[i].value_length != value_length) + { + printf("memcached_get: value_length[%d] is not equal... stored %ld but got %ld\n", i, queries[i].value_length, value_length); + + safe_free(value); + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + if (strcmp(queries[i].value, value)) + { + printf("memcached_get: value[%d] is not equal... stored %s but got %s\n", i, queries[i].value, value); + + safe_free(value); + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + if (queries[i].flags != flags) + { + printf("memcached_get: flags[%d] is not equal... stored %u but got %u\n", i, queries[i].flags, flags); + + safe_free(value); + safe_free_queries(queries); + memcached_free(mc); + return TEST_FAILURE; + } + + safe_free(value); + } + + safe_free_queries(queries); + memcached_free(mc); + + return TEST_SUCCESS; +} + +/* + Test cases +*/ +test_st mset_tests[] ={ + {"mset_and_get_test", true, (test_callback_fn*)mset_and_get_test }, + {0, 0, 0} +}; + +collection_st collection[] ={ + {"mset_tests", 0, 0, mset_tests}, + {0, 0, 0, 0} +}; + +void get_world(Framework *frame) +{ + frame->collections= collection; +} diff --git a/tests/storage.h b/tests/storage.h new file mode 100644 index 00000000..33419a43 --- /dev/null +++ b/tests/storage.h @@ -0,0 +1,15 @@ +#ifndef __TESTS_STORAGE_H__ +#define __TESTS_STORAGE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +LIBTEST_LOCAL +test_return_t mset_and_get_test(void *); + +#ifdef __cplusplus +} +#endif + +#endif /* __TESTS_STORAGE_H__ */