OPT_HELP= 'h',
OPT_VERBOSE= 'v',
OPT_DEBUG= 'd',
+ OPT_ANALYZE= 'a',
OPT_FLAG= 257,
OPT_EXPIRE,
OPT_SET,
static void options_parse(int argc, char *argv[]);
static void print_server_listing(memcached_st *memc, memcached_stat_st *stat,
memcached_server_st *server_list);
+static void print_analysis_report(memcached_st *memc,
+ memcached_analysis_st *report,
+ memcached_server_st *server_list);
static int opt_verbose= 0;
static int opt_displayflag= 0;
+static int opt_analyze= 0;
static char *opt_servers= NULL;
static struct option long_options[]=
{"debug", no_argument, &opt_verbose, OPT_DEBUG},
{"servers", required_argument, NULL, OPT_SERVERS},
{"flag", no_argument, &opt_displayflag, OPT_FLAG},
+ {"analyze", no_argument, NULL, OPT_ANALYZE},
{0, 0, 0, 0},
};
memcached_stat_st *stat;
memcached_server_st *servers;
memcached_server_st *server_list;
+ memcached_analysis_st *report;
options_parse(argc, argv);
if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS)
{
printf("Failure to communicate with servers (%s)\n",
- memcached_strerror(memc, rc));
+ memcached_strerror(memc, rc));
exit(1);
}
server_list= memcached_server_list(memc);
- print_server_listing(memc, stat, server_list);
+
+ if (opt_analyze)
+ {
+ report= memcached_analyze(memc, stat, &rc);
+ if (rc != MEMCACHED_SUCCESS || report == NULL)
+ {
+ printf("Failure to analyze servers (%s)\n",
+ memcached_strerror(memc, rc));
+ exit(1);
+ }
+ print_analysis_report(memc, report, server_list);
+ free(report);
+ }
+ else
+ print_server_listing(memc, stat, server_list);
free(stat);
free(opt_servers);
}
}
+static void print_analysis_report(memcached_st *memc,
+ memcached_analysis_st *report,
+ memcached_server_st *server_list)
+{
+ uint32_t server_count= memcached_server_count(memc);
+
+ printf("Memcached Cluster Analysis Report\n\n");
+
+ printf("\tNumber of Servers Analyzed : %d\n", server_count);
+ printf("\tAverage Item Size (incl/overhead) : %u bytes\n",
+ report->average_item_size);
+
+ if (server_count == 1)
+ {
+ printf("\nFor a detailed report, you must supply multiple servers.\n");
+ return;
+ }
+
+ printf("\n");
+ printf("\tNode with most memory consumption : %s:%u (%u bytes)\n",
+ memcached_server_name(memc, server_list[report->most_consumed_server]),
+ memcached_server_port(memc, server_list[report->most_consumed_server]),
+ report->most_used_bytes);
+ printf("\tNode with least free space : %s:%u (%u bytes remaining)\n",
+ memcached_server_name(memc, server_list[report->least_free_server]),
+ memcached_server_port(memc, server_list[report->least_free_server]),
+ report->least_remaining_bytes);
+ printf("\tNode with longest uptime : %s:%u (%us)\n",
+ memcached_server_name(memc, server_list[report->oldest_server]),
+ memcached_server_port(memc, server_list[report->oldest_server]),
+ report->longest_uptime);
+ printf("\tPool-wide Hit Ratio : %1.f%%\n", report->pool_hit_ratio);
+ printf("\n");
+}
+
static void options_parse(int argc, char *argv[])
{
memcached_programs_help_st help_options[]=
while (1)
{
- option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
+ option_rv= getopt_long(argc, argv, "Vhvds:a", long_options, &option_index);
if (option_rv == -1) break;
switch (option_rv)
{
case OPT_SERVERS: /* --servers or -s */
opt_servers= strdup(optarg);
break;
+ case OPT_ANALYZE: /* --analyze or -a */
+ opt_analyze= OPT_ANALYZE;
+ break;
case '?':
/* getopt_long already printed an error message. */
exit(1);
case OPT_FLUSH: return("Flush servers before running tests.");
case OPT_HASH: return("Select hash type.");
case OPT_BINARY: return("Switch to binary protocol.");
+ case OPT_ANALYZE: return("Analyze the provided servers.");
};
WATCHPOINT_ASSERT(0);
memcached_set.pod\\r
memcached_version.pod\\r
memflush.pod\\r
- memcached_flush_buffers.pod\r
+ memcached_flush_buffers.pod\\r
+ memcached_analyze.pod\r
\r
man_MANS = libmemcached.3\\r
libmemcached_examples.3\\r
memcached_add_by_key.3\\r
memcached_append.3\\r
memcached_append_by_key.3\\r
+ memcached_analyze.3\\r
memcached_behavior_get.3\\r
memcached_behavior_set.3\\r
memcached_callback_get.3\\r
memcached_verbosity.3\\r
memcached_lib_version.3\\r
memcached_version.3\\r
- memcached_flush_buffers.3\r
+ memcached_flush_buffers.3\r
\r
libmemcached.3: libmemcached.pod \r
@POD2MAN@ -c "libmemcached" -r "" -s 3 libmemcached.pod > libmemcached.3\r
memcached_flush_buffers.3: memcached_flush_buffers.pod\r
@POD2MAN@ -c "libmemcached" -r "" -s 3 memcached_flush_buffers.pod > memcached_flush_buffers.3\r
\r
+memcached_analyze.3: memcached_analyze.pod\r
+ @POD2MAN@ -c "libmemcached" -r "" -s 3 memcached_analyze.pod > memcached_analyze.3\r
+\r
memcp.1: memcp.pod\r
@POD2MAN@ -c "libmemcached" -r "" -s 1 memcp.pod > memcp.1\r
\r
--- /dev/null
+=head1 NAME
+
+memcached_analyze
+
+=head1 LIBRARY
+
+C Client Library for memcached (libmemcached, -lmemcached)
+
+=head1 SYNOPSIS
+
+ #include <memcached.h>
+
+ memcached_analysis_st *memcached_analyze(memcached_st *ptr,
+ memcached_stat_st *stat,
+ memcached_return *error);
+
+=head1 DESCRIPTION
+
+libmemcached(3) has the ability to query a memcached server (or collection
+of servers) for their current state. Queries to find state return a
+C<memcached_analysis_st> structure. You are responsible for freeing this structure.
+
+memcached_analyze() analyzes useful information based on the provided servers
+and sets the result to the C<memcached_analysis_st> structure. The return value
+must be freed by the calling application.
+
+A command line tool, memstat(1) with the option --analyze, is provided so that
+you do not have to write an application to use this method.
+
+=head1 RETURN
+
+A pointer to the allocated C<memcached_analysis_st> structure on success and
+a NULL pointer on failure. You may inspect the error detail by checking the
+C<memcached_return> value.
+
+Any method returning a C<memcached_analysis_st> expects you to free the
+memory allocated for it.
+
+=head1 HOME
+
+To find out more information please check:
+L<http://tangent.org/552/libmemcached.html>
+
+=head1 AUTHOR
+
+Toru Maesaka, E<lt>dev@torum.netE<gt>
+
+=head1 SEE ALSO
+
+memcached(1) libmemcached(3) memcached_strerror(3)
+
+=cut
hsieh_hash.c \
memcached.c \
memcached_auto.c \
+ memcached_analyze.c \
memcached_behavior.c \
memcached_callback.c \
memcached_connect.c \
memcached_storage.c \
memcached_string.c \
memcached_stats.c \
- memcached_strerror.c \
+ memcached_strerror.c \
memcached_verbosity.c \
memcached_version.c \
murmur_hash.c \
#define LIBMEMCACHED_VERSION_STRING "0.25"
+struct memcached_analysis_st {
+ uint64_t most_used_bytes;
+ uint64_t least_remaining_bytes;
+ uint32_t average_item_size;
+ uint32_t longest_uptime;
+ uint32_t least_free_server;
+ uint32_t most_consumed_server;
+ uint32_t oldest_server;
+ double pool_hit_ratio;
+};
+
struct memcached_stat_st {
uint32_t pid;
uint32_t uptime;
--- /dev/null
+#include "common.h"
+
+static void calc_largest_consumption(memcached_analysis_st *result,
+ const uint32_t server_num,
+ const uint64_t nbytes)
+{
+ if (result->most_used_bytes < nbytes)
+ {
+ result->most_used_bytes= nbytes;
+ result->most_consumed_server= server_num;
+ }
+}
+
+static void calc_oldest_node(memcached_analysis_st *result,
+ const uint32_t server_num,
+ const uint32_t uptime)
+{
+ if (result->longest_uptime < uptime)
+ {
+ result->longest_uptime= uptime;
+ result->oldest_server= server_num;
+ }
+}
+
+static void calc_least_free_node(memcached_analysis_st *result,
+ const uint32_t server_num,
+ const long max_allowed_bytes,
+ const long used_bytes)
+{
+ long remaining_bytes= max_allowed_bytes - used_bytes;
+
+ if (result->least_remaining_bytes == 0 ||
+ remaining_bytes < result->least_remaining_bytes)
+ {
+ result->least_remaining_bytes= remaining_bytes;
+ result->least_free_server= server_num;
+ }
+}
+
+static void calc_average_item_size(memcached_analysis_st *result,
+ const uint64_t total_items,
+ const uint64_t total_bytes)
+{
+ if (total_items > 0 && total_bytes > 0)
+ result->average_item_size= total_bytes / total_items;
+}
+
+static void calc_hit_ratio(memcached_analysis_st *result,
+ const uint64_t total_get_hits,
+ const uint64_t total_get_cmds)
+{
+ if (total_get_hits == 0 || total_get_cmds == 0)
+ {
+ result->pool_hit_ratio= 0;
+ return;
+ }
+
+ double temp= (double)total_get_hits/total_get_cmds;
+ result->pool_hit_ratio= temp * 100;
+}
+
+memcached_analysis_st *memcached_analyze(memcached_st *memc,
+ memcached_stat_st *stat,
+ memcached_return *error)
+{
+ uint64_t total_items= 0, total_bytes= 0;
+ uint64_t total_get_cmds= 0, total_get_hits= 0;
+ uint32_t server_count, x;
+ memcached_analysis_st *result;
+
+ *error= MEMCACHED_SUCCESS;
+ server_count= memcached_server_count(memc);
+ result= (memcached_analysis_st*)malloc(sizeof(memcached_analysis_st)
+ * (memc->number_of_hosts));
+ if (!result)
+ {
+ *error= MEMCACHED_MEMORY_ALLOCATION_FAILURE;
+ return NULL;
+ }
+
+ memset(result, 0, sizeof(*result));
+
+ for (x= 0; x < server_count; x++)
+ {
+ calc_largest_consumption(result, x, stat[x].bytes);
+ calc_oldest_node(result, x, stat[x].uptime);
+ calc_least_free_node(result, x, stat[x].limit_maxbytes, stat[x].bytes);
+
+ total_get_hits+= stat[x].get_hits;
+ total_get_cmds+= stat[x].cmd_get;
+ total_items+= stat[x].curr_items;
+ total_bytes+= stat[x].bytes;
+ }
+
+ calc_average_item_size(result, total_items, total_bytes);
+ calc_hit_ratio(result, total_get_hits, total_get_cmds);
+
+ return result;
+}
void memcached_server_free(memcached_server_st *ptr);
memcached_server_st *memcached_server_clone(memcached_server_st *clone, memcached_server_st *ptr);
+memcached_analysis_st *memcached_analyze(memcached_st *memc, memcached_stat_st *stat,
+ memcached_return *error);
#ifdef __cplusplus
typedef struct memcached_st memcached_st;
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_string_st memcached_string_st;
typedef struct memcached_server_st memcached_server_st;
return TEST_SUCCESS;
}
+static memcached_return analyzer_test(memcached_st *memc)
+{
+ memcached_return rc;
+ memcached_stat_st *stat;
+ memcached_analysis_st *report;
+
+ stat= memcached_stat(memc, NULL, &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(stat);
+
+ report= memcached_analyze(memc, stat, &rc);
+ assert(rc == MEMCACHED_SUCCESS);
+ assert(report);
+
+ free(report);
+ memcached_stat_free(NULL, stat);
+
+ return MEMCACHED_SUCCESS;
+}
+
/* Clean the server before beginning testing */
test_st tests[] ={
{"flush", 0, flush_test },
{"read_through", 1, read_through },
{"delete_through", 1, delete_through },
{"noreply", 1, noreply_test},
+ {"analyzer", 1, analyzer_test},
{0, 0, 0}
};